]> git.saurik.com Git - apple/dyld.git/commitdiff
dyld-750.5.tar.gz macos-10154 macos-10155 v750.5
authorApple <opensource@apple.com>
Fri, 1 May 2020 18:20:25 +0000 (18:20 +0000)
committerApple <opensource@apple.com>
Fri, 1 May 2020 18:20:25 +0000 (18:20 +0000)
250 files changed:
chroot_util.cpp [new file with mode: 0644]
configs/libdyld.xcconfig
doc/man/man3/dlsym.3
dyld.xcodeproj/ContainerizedTestRunner.xctestplan [new file with mode: 0644]
dyld.xcodeproj/project.pbxproj
dyld3/APIs.cpp
dyld3/Closure.cpp
dyld3/Closure.h
dyld3/ClosureBuilder.cpp
dyld3/ClosurePrinter.cpp
dyld3/ClosureWriter.cpp
dyld3/ClosureWriter.h
dyld3/MachOAnalyzer.cpp
dyld3/MachOAnalyzer.h
dyld3/MachOFile.cpp
dyld3/MachOLoaded.cpp
dyld3/MachOLoaded.h
dyld3/SharedCacheRuntime.cpp
dyld3/libdyldEntryVector.cpp
dyld3/libdyldEntryVector.h
dyld3/shared-cache/AdjustDylibSegments.cpp
dyld3/shared-cache/BuilderUtils.h [deleted file]
dyld3/shared-cache/BuilderUtils.mm [deleted file]
dyld3/shared-cache/CacheBuilder.cpp
dyld3/shared-cache/CacheBuilder.h
dyld3/shared-cache/DyldSharedCache.cpp
dyld3/shared-cache/MachOFileAbstraction.hpp
dyld3/shared-cache/Manifest.h [deleted file]
dyld3/shared-cache/Manifest.mm [deleted file]
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 [new file with mode: 0644]
dyld3/shared-cache/SharedCacheBuilder.h [new file with mode: 0644]
dyld3/shared-cache/dyld_shared_cache_builder.mm
dyld3/shared-cache/dyldinfo.cpp
dyld3/shared-cache/make_ios_dyld_cache.cpp [deleted file]
dyld3/shared-cache/mrm_shared_cache_builder.cpp
dyld3/shared-cache/mrm_shared_cache_builder.h
dyld3/shared-cache/multi_dyld_shared_cache_builder.mm [deleted file]
include/mach-o/fixup-chains.h
launch-cache/MachOFileAbstraction.hpp
launch-cache/dyld_shared_cache_util.cpp
local_test_runner/ContainerizedTestRunner.mm [new file with mode: 0644]
local_test_runner/Info.plist [new file with mode: 0644]
src/ImageLoader.cpp
src/ImageLoader.h
src/ImageLoaderMachO.cpp
src/ImageLoaderMachOCompressed.cpp
src/dyld2.cpp
src/dyldSyscallInterface.h
src/glue.c
testing/README.txt
testing/build_ninja.py [new file with mode: 0755]
testing/build_tests.py [deleted file]
testing/include/dyld_test.h [deleted file]
testing/include/test_support.h
testing/lib/execserver.defs [new file with mode: 0644]
testing/lib/test_support.cpp [new file with mode: 0644]
testing/lib/test_support.exp [new file with mode: 0644]
testing/nocr/execserver.defs [deleted file]
testing/nocr/nocr.c [deleted file]
testing/nocr/nocr.cpp [new file with mode: 0644]
testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c
testing/test-cases/NSAddImage-basic.dtest/main.c
testing/test-cases/NSAddImage-fail.dtest/main.c [deleted file]
testing/test-cases/NSAddImage-fail.dtest/main.cpp [new file with mode: 0644]
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_slide.dtest/main.c
testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m
testing/test-cases/_dyld_get_objc_selector-shared-cache.dtest/main.c
testing/test-cases/_dyld_get_objc_selector.dtest/main.m
testing/test-cases/_dyld_images_for_addresses.dtest/main.c
testing/test-cases/_dyld_is_memory_immutable-lock.dtest/main.c
testing/test-cases/_dyld_is_memory_immutable.dtest/main.c
testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/bar.c
testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/main.cxx
testing/test-cases/_dyld_register_for_image_loads.dtest/bar.c
testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx
testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx
testing/test-cases/_dyld_shared_cache_is_locally_built.dtest/main.c
testing/test-cases/amfi-hardened-dlopen-leaf.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/main.c
testing/test-cases/crt-old-mac10.5-vars-libSystem.dtest/main.c [new file with mode: 0644]
testing/test-cases/crt-old-mac10.6-vars-libSystem.dtest/main.c [new file with mode: 0644]
testing/test-cases/crt-vars-libSystem.dtest/main.c
testing/test-cases/cwd-relative-load.dtest/main.c
testing/test-cases/dladdr-basic.dtest/main-no-syms.c
testing/test-cases/dladdr-basic.dtest/main.c
testing/test-cases/dladdr-dylib.dtest/foo.c
testing/test-cases/dladdr-dylib.dtest/main.c
testing/test-cases/dlclose-static-terminator.dtest/main.c
testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c
testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c
testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c
testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c
testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c
testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c
testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c
testing/test-cases/dlopen-RTLD_NOW.dtest/main.c
testing/test-cases/dlopen-atpath-restricted.dtest/main.c
testing/test-cases/dlopen-bad-file.dtest/main.c
testing/test-cases/dlopen-basic.dtest/main.c
testing/test-cases/dlopen-empty-data.dtest/main.c
testing/test-cases/dlopen-fail-cleanly.dtest/main.c
testing/test-cases/dlopen-flat.dtest/main.c
testing/test-cases/dlopen-framework-fallback.dtest/main.c
testing/test-cases/dlopen-haswell.dtest/main.c
testing/test-cases/dlopen-in-init.dtest/foo.c
testing/test-cases/dlopen-in-init.dtest/main.c
testing/test-cases/dlopen-in-init2.dtest/bar.c
testing/test-cases/dlopen-in-init2.dtest/foo.c
testing/test-cases/dlopen-in-init2.dtest/main.c
testing/test-cases/dlopen-in-init3.dtest/bar.c
testing/test-cases/dlopen-in-init3.dtest/foo.c
testing/test-cases/dlopen-in-init3.dtest/main.c
testing/test-cases/dlopen-indirect-groupNum.dtest/main.c
testing/test-cases/dlopen-intertwined.dtest/base.c
testing/test-cases/dlopen-intertwined.dtest/main.c
testing/test-cases/dlopen-long-error-message.dtest/main.c
testing/test-cases/dlopen-prebuilt-dlopen-closure.dtest/main.c
testing/test-cases/dlopen-race.dtest/foo.c
testing/test-cases/dlopen-race.dtest/main.c
testing/test-cases/dlopen-realpath.dtest/main.c
testing/test-cases/dlopen-recurse.dtest/bar.c
testing/test-cases/dlopen-recurse.dtest/main.c
testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c
testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c
testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c
testing/test-cases/dlopen-rpath-implicit.dtest/foo.c
testing/test-cases/dlopen-rpath-implicit.dtest/main.c
testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c
testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c
testing/test-cases/dlopen-rpath-prev-override.dtest/foo.c
testing/test-cases/dlopen-rpath-prev-override.dtest/good.c
testing/test-cases/dlopen-rpath-prev-override.dtest/main.c
testing/test-cases/dlopen-rpath-prev.dtest/foo.c
testing/test-cases/dlopen-rpath-prev.dtest/main.c
testing/test-cases/dlopen-rpath-prev.dtest/sub1.c
testing/test-cases/dlopen-rpath-prev.dtest/sub2.c
testing/test-cases/dlopen-signing.dtest/main.c
testing/test-cases/dlopen-symlink.dtest/main.c
testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c
testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c
testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c
testing/test-cases/dlsym-RTLD_SELF.dtest/main.c
testing/test-cases/dlsym-handle.dtest/main.c
testing/test-cases/dlsym-in-interposed-malloc.dtest/interposer.c
testing/test-cases/dlsym-in-interposed-malloc.dtest/main.c
testing/test-cases/dlsym-re-export.dtest/main.c
testing/test-cases/dtrace.dtest/main.c
testing/test-cases/dyld-insert-library-double.dtest/main.cpp
testing/test-cases/dyld-insert-library-rpath.dtest/main.cpp
testing/test-cases/dyld_abort_payload.dtest/main.c [deleted file]
testing/test-cases/dyld_abort_payload.dtest/main.cpp [new file with mode: 0644]
testing/test-cases/dyld_fork-locks.dest/main.c
testing/test-cases/dyld_get_image_versions.dtest/main.c
testing/test-cases/dyld_get_sdk_version.dtest/bad.txt [deleted file]
testing/test-cases/dyld_get_sdk_version.dtest/main.c
testing/test-cases/dyld_has_inserted_or_interposing_libraries.dtest/main.c
testing/test-cases/dyld_image_path_containing_address.dtest/main.c
testing/test-cases/dyld_need_closure.dtest/main.c
testing/test-cases/dyld_process_info.dtest/linksWithCF.c
testing/test-cases/dyld_process_info.dtest/main.c [deleted file]
testing/test-cases/dyld_process_info.dtest/main.cpp [new file with mode: 0644]
testing/test-cases/dyld_process_info_notify.dtest/main.c [deleted file]
testing/test-cases/dyld_process_info_notify.dtest/main.cpp [new file with mode: 0644]
testing/test-cases/dyld_process_info_notify.dtest/target.c
testing/test-cases/dyld_process_info_unload.dtest/main.c [deleted file]
testing/test-cases/dyld_process_info_unload.dtest/main.cpp [new file with mode: 0644]
testing/test-cases/dyld_process_info_unload.dtest/target.c
testing/test-cases/dyld_shared_cache_some_image_overridden.dtest/main.c
testing/test-cases/dyld_version_spis.dtest/main.c
testing/test-cases/dylib-re-export-old-format.dtest/main.c
testing/test-cases/dylib-re-export.dtest/main.c
testing/test-cases/dylib-static-link.dtest/main.c [new file with mode: 0644]
testing/test-cases/dylib-static-link.dtest/missing.c [deleted file]
testing/test-cases/dylib-static-link.dtest/present.c [deleted file]
testing/test-cases/dylib-static-weak-link.dtest/missing.c
testing/test-cases/dylib-static-weak-link.dtest/present.c
testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c
testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c
testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c
testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c
testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c
testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c
testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c
testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/main.c
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/image_infos-uuids.dtest/main.c
testing/test-cases/init-term-segments.dtest/main.c
testing/test-cases/interpose-malloc.dtest/main.c
testing/test-cases/interpose-resolver.dtest/main.c
testing/test-cases/interpose-then-dlopen.dtest/main.c
testing/test-cases/interpose-weak.dtest/main.c
testing/test-cases/launch-image-cache.dtest/main.c
testing/test-cases/lazy-symbol-missing.dtest/main.c
testing/test-cases/lazy-symbol-missing.dtest/runner.c [deleted file]
testing/test-cases/lazy-symbol-missing.dtest/runner.cpp [new file with mode: 0644]
testing/test-cases/macOS-cache-rebuild.dtest/main.c [deleted file]
testing/test-cases/macOS-cache-rebuild.dtest/main.cpp [new file with mode: 0644]
testing/test-cases/missing-weak-def.dtest/main.c
testing/test-cases/no-shared-cache.dtest/main.c
testing/test-cases/operator-new.dtest/main.cxx
testing/test-cases/read-only-data.dtest/main.c
testing/test-cases/restrict-search.dtest/main.c
testing/test-cases/rpath-absolute.dtest/main.c
testing/test-cases/rpath-relative.dtest/foo.c [new file with mode: 0644]
testing/test-cases/rpath-relative.dtest/main.c [new file with mode: 0644]
testing/test-cases/rpath-weak-missing.dtest/main.c
testing/test-cases/shared_cache_iterate.dtest/main.c
testing/test-cases/shared_cache_optimized.dtest/main.c
testing/test-cases/shared_cache_range.dtest/main.c
testing/test-cases/static-terminators.dtest/base.c
testing/test-cases/static-terminators.dtest/foo.c
testing/test-cases/static-terminators.dtest/main.c
testing/test-cases/symbol-resolver-basic.dtest/main.c
testing/test-cases/thread-local-atexit-macOS.dtest/main.cpp
testing/test-cases/thread-local-atexit.dtest/main.cpp
testing/test-cases/thread-local-cleanup.dtest/main.c
testing/test-cases/thread-local-destructors.dtest/main.cpp
testing/test-cases/thread-local-variables.dtest/main.c
testing/test-cases/unix-conformance.dtest/main.c
testing/test-cases/weak-coalesce-inserted-dylibs.dtest/main.cpp
testing/test-cases/weak-coalesce-unload.dtest/main.c
testing/test-cases/weak-coalesce.dtest/Makefile [deleted file]
testing/test-cases/weak-coalesce.dtest/base.c
testing/test-cases/weak-coalesce.dtest/foo1.c
testing/test-cases/weak-coalesce.dtest/foo2.c
testing/test-cases/weak-coalesce.dtest/foo3.c
testing/test-cases/weak-coalesce.dtest/main.c
testing/test-cases/weak-def-bind-old-format.dtest/bar.c [new file with mode: 0644]
testing/test-cases/weak-def-bind-old-format.dtest/foo.c [new file with mode: 0644]
testing/test-cases/weak-def-bind-old-format.dtest/main.c [new file with mode: 0644]
testing/test-cases/weak-dylib-re-export.dtest/main.c

diff --git a/chroot_util.cpp b/chroot_util.cpp
new file mode 100644 (file)
index 0000000..5c5fcc3
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+* 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@
+*/
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <sys/attr.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <copyfile.h>
+
+#include <set>
+#include <string>
+#include <vector>
+#include <functional>
+#include <filesystem>
+
+#include "StringUtils.h"
+#include "MachOFile.h"
+
+std::set<std::string> scanForDependencies(const std::string& path) {
+    __block std::set<std::string> result;
+    struct stat stat_buf;
+    int fd = open(path.c_str(), O_RDONLY, 0);
+    if (fd == -1) {
+        return result;
+    }
+
+    if (fstat(fd, &stat_buf) == -1) {
+        close(fd);
+        return result;
+    }
+
+    const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if (buffer == MAP_FAILED) {
+        close(fd);
+        return result;
+    }
+
+    auto scanner = ^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+        if (isWeak) { return; } // We explicily avoid LC_LOAD_WEAK_DYLIB since we are trying to build a minimal chroot
+        if (loadPath[0] != '/') { return; } // Only include absolute dependencies
+        result.insert(loadPath);
+    };
+    Diagnostics diag;
+    if ( dyld3::FatFile::isFatFile(buffer) ) {
+        const dyld3::FatFile* ff = (dyld3::FatFile*)buffer;
+        ff->forEachSlice(diag, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
+            const dyld3::MachOFile* mf = (dyld3::MachOFile*)sliceStart;
+            mf->forEachDependentDylib(scanner);
+         });
+    } else {
+        const dyld3::MachOFile* mf = (dyld3::MachOFile*)buffer;
+        if (mf->isMachO(diag, stat_buf.st_size)) {
+            mf->forEachDependentDylib(scanner);
+        }
+    }
+    close(fd);
+    return result;
+}
+
+std::string withoutPrefixPath(const std::string& path, const std::string& prefix ) {
+    std::string result = path;
+    size_t pos = result.find(prefix);
+    result.erase(pos, prefix.length());
+    return result;
+}
+
+void add_symlinks_to_dylib(const std::string path) {
+    static std::set<std::string> alreadyMatched;
+    size_t pos = path.rfind(".framework/Versions/");
+    auto prefixPath = path.substr(0, pos);
+    if (alreadyMatched.find(prefixPath) != alreadyMatched.end()) { return; }
+
+    if (pos == std::string::npos) { return; }
+//    fprintf(stderr, "PATH: %s\n", path.c_str());
+    size_t versionStart = pos+20;
+    size_t versionEnd = versionStart;
+    while (path[versionEnd] != '/') {
+        ++versionEnd;
+    }
+    size_t frameworkNameBegin = pos;
+    while (path[frameworkNameBegin-1] != '/') {
+        --frameworkNameBegin;
+    }
+    auto frameworkName = path.substr(frameworkNameBegin, pos-frameworkNameBegin);
+    auto version = path.substr(versionStart, versionEnd-versionStart);
+    std::string mainLinkPath = prefixPath + ".framework/" + frameworkName;
+    std::string mainLinkTarget = "Versions/Current/" + frameworkName;
+    std::string versionLinkPath =  prefixPath + ".framework/Versions/Current";
+    std::string versionLinkTarget =  version;;
+    alreadyMatched.insert(prefixPath);
+    if (!std::filesystem::exists(versionLinkPath)) {
+        std::filesystem::create_symlink(version, versionLinkPath);
+    }
+    if (!std::filesystem::exists(mainLinkPath)) {
+        std::filesystem::create_symlink(mainLinkTarget, mainLinkPath);
+    }
+}
+
+void add_symlink(const std::string& target, const std::string& path) {
+    if (!std::filesystem::exists(path)) {
+        std::filesystem::create_symlink(target, path);
+    }
+}
+
+void buildChroot(const std::string& chroot, const std::string& fallback, const std::vector<std::string>& binaries) {
+    auto chrootPath = std::filesystem::path(chroot);
+    auto fallbackPath = std::filesystem::path(fallback);
+
+    for (const auto& binary : binaries) {
+        if (std::filesystem::exists(chroot + binary)) { continue; }
+        std::filesystem::create_directories(std::filesystem::path(chroot + binary).parent_path());
+        std::filesystem::copy(fallback + binary, chroot + binary);
+    }
+    bool foundNewEntries = true;
+    std::set<std::string> scannedFiles;
+    std::string devfsPath = chroot + "/dev";
+    while (foundNewEntries) {
+        foundNewEntries = false;
+        for(auto file = std::filesystem::recursive_directory_iterator(chroot);
+            file != std::filesystem::recursive_directory_iterator();
+            ++file ) {
+            auto filePath = file->path().string();
+            if (filePath == devfsPath) {
+                file.disable_recursion_pending();
+                continue;
+            }
+            if (scannedFiles.find(filePath) != scannedFiles.end()) { continue; }
+            scannedFiles.insert(filePath);
+            auto candidates = scanForDependencies(filePath);
+            for (const auto& candidate : candidates) {
+                if (std::filesystem::exists(chroot + candidate)) { continue; }
+                if (!std::filesystem::exists(fallback + candidate)) { continue; }
+                std::filesystem::create_directories(std::filesystem::path(chroot + candidate).parent_path());
+                std::filesystem::copy(fallback + candidate, chroot + candidate);
+                add_symlinks_to_dylib(chroot + candidate);
+                foundNewEntries = true;
+            }
+        }
+    }
+    add_symlink("libSystem.B.dylib", chroot + "/usr/lib/libSystem.dylib");
+    add_symlink("libSystem.dylib", chroot + "/usr/lib/libc.dylib");
+}
+
+int main(int argc, const char * argv[]) {
+    std::vector<std::string> binaries;
+    std::vector<std::string> overlays;
+    std::string fallback;
+    std::string chroot;
+    for (int i = 1; i < argc; ++i) {
+        const char* arg = argv[i];
+        if (arg[0] == '-') {
+            if (strcmp(arg, "-chroot") == 0) {
+                chroot = argv[++i];
+            } else if (strcmp(arg, "-fallback") == 0) {
+                fallback = argv[++i];
+            } else if (strcmp(arg, "-add_file") == 0) {
+                binaries.push_back(argv[++i]);
+            } else {
+                fprintf(stderr, "unknown option: %s\n", arg);
+                exit(-1);
+            }
+        } else {
+            fprintf(stderr, "unknown option: %s\n", arg);
+            exit(-1);
+        }
+    }
+
+    if (chroot.length() == 0) {
+        fprintf(stderr, "No -chroot <dir>\n");
+        exit(-1);
+    }
+    if (fallback.length() == 0) {
+        fprintf(stderr, "No -fallback <dir>\n");
+        exit(-1);
+    }
+    buildChroot(chroot, fallback, binaries);
+    // insert code here...
+    return 0;
+}
index 69f4fb44a023f20a6745dedf9e42d3d991f95cc7..6917378f47b14ac21dd83cebf71cc8ab37790a98 100644 (file)
@@ -4,5 +4,7 @@ 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                  = 
+
 INSTALL_PATH = /usr/lib/system
 
index b981ad666be6c9c2978e8c3bf1fe295869831592..40d1fdfb04c5e36270e1378a37cf1c60f0c9629e 100644 (file)
@@ -85,7 +85,6 @@ is the name used in C source code.  For example to find the address
 of function foo(), you would pass "foo" as the symbol name.  This
 is unlike the older dyld APIs which required a leading underscore.
 If you are looking up a C++ symbol, you need to use the mangled C++ symbol name.
-name.  
 .Sh SEE ALSO
 .Xr dlopen 3
 .Xr dlerror 3
diff --git a/dyld.xcodeproj/ContainerizedTestRunner.xctestplan b/dyld.xcodeproj/ContainerizedTestRunner.xctestplan
new file mode 100644 (file)
index 0000000..92776a4
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "configurations" : [
+    {
+      "id" : "0ED518CA-34AA-4808-822E-3532C949AFBA",
+      "name" : "Configuration 1",
+      "options" : {
+
+      }
+    }
+  ],
+  "defaultOptions" : {
+    "codeCoverage" : false
+  },
+  "testTargets" : [
+    {
+      "parallelizable" : true,
+      "target" : {
+        "containerPath" : "container:dyld.xcodeproj",
+        "identifier" : "3715A2FD232320BC0059433D",
+        "name" : "ContainerizedTestRunner"
+      }
+    }
+  ],
+  "version" : 1
+}
index 5bdd191f103b26056b4c49dfeda6f2c235c306ee..c8b67886d851cd6f3740492127874fa38846622d 100644 (file)
                        buildConfigurationList = F9F6F42A1C1FB0A700BD8FED /* Build configuration list for PBXAggregateTarget "dyld_tests" */;
                        buildPhases = (
                                F9F6F42B1C1FB0AE00BD8FED /* build */,
+                               37E2FC9D22F62FE1004AF213 /* install */,
                        );
                        dependencies = (
-                               F97FF3661C237F97000ACDD2 /* PBXTargetDependency */,
+                               37382F6A230CB46500E375CE /* PBXTargetDependency */,
                        );
                        name = dyld_tests;
                        productName = dyld_tests;
 /* End PBXAggregateTarget section */
 
 /* Begin PBXBuildFile section */
+               37065AA82310889D00A20034 /* libtest_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3721A635230CABAF00594066 /* libtest_support.a */; };
+               37065AAB2310A18300A20034 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F93F46511FA420630060D9F9 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; };
+               3715A301232320BD0059433D /* ContainerizedTestRunner.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3715A300232320BD0059433D /* ContainerizedTestRunner.mm */; };
+               3715A303232320BD0059433D /* libtest_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3721A635230CABAF00594066 /* libtest_support.a */; };
+               37382F68230CADEE00E375CE /* test_support.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37382F67230CADEE00E375CE /* test_support.cpp */; };
                373C58F1219CE478003442D5 /* BootArgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 373C58EF219CE478003442D5 /* BootArgs.cpp */; };
-               37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; };
-               37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
-               37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
-               37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */; };
-               37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
-               37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
                37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
-               37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
                37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
-               37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
                37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
-               37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
                37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
                37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
-               37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
                37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
-               37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
                37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
-               376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; };
-               376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; };
-               378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
-               378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
-               37908A2E1E3A8632009613FA /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; };
+               375A4C74233DE07600CFBD6B /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               375A4C75233DE09E00CFBD6B /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
                37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */; };
                37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
                37908A321E3ED667009613FA /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
                37918AC52058915E00F39A77 /* dyld.codes in install ktrace codes file */ = {isa = PBXBuildFile; fileRef = 37918AC42058913800F39A77 /* dyld.codes */; };
-               37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
-               37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
                37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+               37CE9D1A2321A7EB001FBA91 /* chroot_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37CE9D192321A7EB001FBA91 /* chroot_util.cpp */; };
                37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; };
                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 */; };
+               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 */; };
                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 */; };
                C187B90D1FE067C70042D3B7 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
                C187B90E1FE067CD0042D3B7 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
                C187B9161FE0680A0042D3B7 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
                C187B9171FE068180042D3B7 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
                C187B9181FE068260042D3B7 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
-               C187B9191FE0682C0042D3B7 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
                C187B91B1FE0683F0042D3B7 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
                C187B91E1FE0684C0042D3B7 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
                C18A75F9209A1AF600DC01BB /* JSONReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = C18A75F8209A1AF600DC01BB /* JSONReader.mm */; };
                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 */; };
-               C1D268401FE9B464009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
                C1F003CC213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
-               C1F003CD213F3CB4002D9DC9 /* 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 */; };
                F93D73451F8475C3007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
                F93D73461F8475C3007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
                F93D73471F8C4E55007D9413 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
-               F93D73481F8FF780007D9413 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
-               F93D73491F8FF780007D9413 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
-               F93D734A1F8FF780007D9413 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
-               F93D734B1F8FF79E007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
-               F93D734C1F8FF79E007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
-               F93D734D1F8FF79E007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
                F93D734E1F8FF7C2007D9413 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
                F93D734F1F8FF7C2007D9413 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
                F93D73501F8FF7C2007D9413 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
                F93D73511F8FF7C2007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
                F93D73521F8FF7C2007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
                F93D73531F8FF7C2007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
-               F93F46521FA420850060D9F9 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F93F46511FA420630060D9F9 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; };
                F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
                F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
                F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; };
                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 */; };
-               F97FF3611C23640C000ACDD2 /* nocr.c in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.c */; };
+               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 */; };
 /* End PBXBuildRule section */
 
 /* Begin PBXContainerItemProxy section */
+               3715A30A23272F890059433D /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 3721B6A72321A75B006F6AB7;
+                       remoteInfo = chroot_util;
+               };
+               37382F69230CB46500E375CE /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 3721A634230CABAF00594066;
+                       remoteInfo = test_support;
+               };
                37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        remoteGlobalIDString = F97C61A61DBAD1A900A84CD7;
                        remoteInfo = dyld_closure_util;
                };
-               F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */ = {
-                       isa = PBXContainerItemProxy;
-                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
-                       proxyType = 1;
-                       remoteGlobalIDString = F97FF3551C23638F000ACDD2;
-                       remoteInfo = nocr;
-               };
                F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
-               377685FE1AC4B27D00026E6C /* CopyFiles */ = {
+               3721B6A62321A75B006F6AB7 /* CopyFiles */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 2147483647;
                        dstPath = /usr/share/man/man1/;
 
 /* Begin PBXFileReference section */
                3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; };
+               37065AA72310856E00A20034 /* nocr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nocr; sourceTree = BUILT_PRODUCTS_DIR; };
+               3715A2FE232320BC0059433D /* ContainerizedTestRunner.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ContainerizedTestRunner.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+               3715A300232320BD0059433D /* ContainerizedTestRunner.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ContainerizedTestRunner.mm; sourceTree = "<group>"; };
+               3715A302232320BD0059433D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+               3721A635230CABAF00594066 /* libtest_support.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libtest_support.a; sourceTree = BUILT_PRODUCTS_DIR; };
+               3721B6A82321A75B006F6AB7 /* chroot_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chroot_util; sourceTree = BUILT_PRODUCTS_DIR; };
+               37382F67230CADEE00E375CE /* test_support.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = test_support.cpp; path = testing/lib/test_support.cpp; sourceTree = "<group>"; };
                373C58EF219CE478003442D5 /* BootArgs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = BootArgs.cpp; path = dyld3/BootArgs.cpp; sourceTree = "<group>"; };
                373C58F0219CE478003442D5 /* BootArgs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BootArgs.h; path = dyld3/BootArgs.h; sourceTree = "<group>"; };
+               376AA37C23305CE10070C28C /* ContainerizedTestRunner.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = ContainerizedTestRunner.xctestplan; path = dyld.xcodeproj/ContainerizedTestRunner.xctestplan; sourceTree = "<group>"; };
                376ED1D71C46F2710051DD54 /* Metabom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metabom.framework; path = AppleInternal/Library/Frameworks/Metabom.framework; sourceTree = SDKROOT; };
-               377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = multi_dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; };
                37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/dyld_shared_cache_builder.mm"; sourceTree = "<group>"; };
-               37908A281E3A853E009613FA /* Manifest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Manifest.mm; path = "dyld3/shared-cache/Manifest.mm"; sourceTree = "<group>"; };
-               37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = multi_dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/multi_dyld_shared_cache_builder.mm"; sourceTree = "<group>"; };
                37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = "dyld3/shared-cache/FileAbstraction.hpp"; sourceTree = "<group>"; };
                37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = "dyld3/shared-cache/MachOFileAbstraction.hpp"; sourceTree = "<group>"; };
-               37908A2C1E3A85A4009613FA /* Manifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Manifest.h; path = "dyld3/shared-cache/Manifest.h"; sourceTree = "<group>"; };
                37908A2D1E3A85A4009613FA /* Trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Trie.hpp; path = "dyld3/shared-cache/Trie.hpp"; sourceTree = "<group>"; };
                37918AC0205890D700F39A77 /* dyld.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = dyld.plist; sourceTree = "<group>"; };
                37918AC42058913800F39A77 /* dyld.codes */ = {isa = PBXFileReference; lastKnownFileType = text; path = dyld.codes; sourceTree = "<group>"; };
-               37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BuilderUtils.mm; path = "dyld3/shared-cache/BuilderUtils.mm"; sourceTree = "<group>"; };
-               37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuilderUtils.h; path = "dyld3/shared-cache/BuilderUtils.h"; sourceTree = "<group>"; };
+               37CE9D192321A7EB001FBA91 /* chroot_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = chroot_util.cpp; 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; };
+               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>"; };
                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>"; };
                F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; sourceTree = "<group>"; };
                F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = "<group>"; };
                F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = "<group>"; };
-               F93F46511FA420630060D9F9 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; sourceTree = "<group>"; };
+               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>"; };
                F94C22241E513CA90079E5DD /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
                F97C61A01D9CA6B800A84CD7 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = dyld3/Logging.cpp; sourceTree = "<group>"; usesTabs = 0; };
                F97C61A11D9CA6B800A84CD7 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = dyld3/Logging.h; sourceTree = "<group>"; usesTabs = 0; };
                F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_closure_util; sourceTree = BUILT_PRODUCTS_DIR; };
-               F97FF3561C23638F000ACDD2 /* nocr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nocr; sourceTree = BUILT_PRODUCTS_DIR; };
-               F97FF35F1C236402000ACDD2 /* nocr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nocr.c; path = testing/nocr/nocr.c; 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>"; };
                F98692001DC3EF4800CBEDE6 /* Diagnostics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Diagnostics.h; path = dyld3/Diagnostics.h; sourceTree = "<group>"; usesTabs = 0; };
                F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; sourceTree = "<group>"; };
                F9AC7E930B7BB67700FEB38B /* version.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = BUILT_PRODUCTS_DIR; };
                F9B01E3D0739ABDE00CF981B /* dyld.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld.exp; path = src/dyld.exp; sourceTree = SOURCE_ROOT; };
-               F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = make_ios_dyld_cache.cpp; path = "dyld3/shared-cache/make_ios_dyld_cache.cpp"; sourceTree = "<group>"; };
                F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs_macOS.cpp; path = dyld3/APIs_macOS.cpp; sourceTree = "<group>"; };
                F9C275581DA71A13007A5D8A /* Loading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Loading.cpp; path = dyld3/Loading.cpp; sourceTree = "<group>"; usesTabs = 0; };
                F9C275591DA71A13007A5D8A /* Loading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Loading.h; path = dyld3/Loading.h; sourceTree = "<group>"; usesTabs = 0; };
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */,
-                               378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */,
+                               C123176B22B9B4F00046E3E5 /* Bom.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               3715A2FB232320BC0059433D /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               3715A303232320BD0059433D /* libtest_support.a in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
-               377685FD1AC4B27D00026E6C /* Frameworks */ = {
+               3721B6A52321A75B006F6AB7 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */,
-                               378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               37065AA82310889D00A20034 /* libtest_support.a in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+               3715A2FF232320BC0059433D /* local_test_runner */ = {
+                       isa = PBXGroup;
+                       children = (
+                               3715A300232320BD0059433D /* ContainerizedTestRunner.mm */,
+                               3715A302232320BD0059433D /* Info.plist */,
+                       );
+                       path = local_test_runner;
+                       sourceTree = "<group>";
+               };
                37918ABF2058908000F39A77 /* tracing */ = {
                        isa = PBXGroup;
                        children = (
                                37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */,
                                F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */,
                                F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */,
-                               37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */,
-                               37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */,
                                F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */,
                                F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */,
                                F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */,
                                F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */,
                                F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */,
                                F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */,
-                               37908A2C1E3A85A4009613FA /* Manifest.h */,
-                               37908A281E3A853E009613FA /* Manifest.mm */,
                                F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */,
                                F98692101DC3EF6C00CBEDE6 /* ObjC2Abstraction.hpp */,
                                F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */,
                                F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */,
                                F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */,
+                               C11ECA8F233C307C0011726F /* SharedCacheBuilder.h */,
+                               C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */,
                                F902031F1DEE83C000AC3F76 /* StringUtils.h */,
                                37908A2D1E3A85A4009613FA /* Trie.hpp */,
                                F9D862441DC9759C000A199A /* dyld_closure_util.cpp */,
                                37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */,
-                               37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */,
-                               F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */,
                                F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */,
                                F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */,
                                F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */,
                F9ED4C870630A72200DF4E74 = {
                        isa = PBXGroup;
                        children = (
+                               376AA37C23305CE10070C28C /* ContainerizedTestRunner.xctestplan */,
                                F9F6F4261C1FAF8000BD8FED /* testing */,
                                F971DD121A4A0E0700BBDD52 /* configs */,
                                F9ED4CBB0630A7AA00DF4E74 /* src */,
                                F9ED4CC30630A7BE00DF4E74 /* doc */,
                                F9ED4CBE0630A7B100DF4E74 /* include */,
+                               3715A2FF232320BC0059433D /* local_test_runner */,
                                F9ED4C990630A76000DF4E74 /* Products */,
                                F96D19A41D9363B7007AF3CE /* dyld3 */,
                                F939373D0A94FC4700070A07 /* launch-cache */,
                                F9F2A5590F7AEE9800B7C9EB /* libdsc.a */,
                                F99B8E670FEC121100701838 /* dyld_shared_cache_util */,
                                F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */,
-                               377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
                                3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */,
-                               F97FF3561C23638F000ACDD2 /* nocr */,
                                F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */,
                                F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */,
                                C187B90A1FE063A40042D3B7 /* slc_builder.dylib */,
                                F92C7E1421E59840000D12B5 /* libdyld.dylib */,
                                F9556D3920C1F896004DF62A /* dyldinfo */,
                                F98E37952332D048003706B4 /* update_dyld_shared_cache_root_mode */,
+                               3721A635230CABAF00594066 /* libtest_support.a */,
+                               37065AA72310856E00A20034 /* nocr */,
+                               3721B6A82321A75B006F6AB7 /* chroot_util */,
+                               3715A2FE232320BC0059433D /* ContainerizedTestRunner.xctest */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                F9ED4CBB0630A7AA00DF4E74 /* src */ = {
                        isa = PBXGroup;
                        children = (
+                               37382F67230CADEE00E375CE /* test_support.cpp */,
                                F93F46511FA420630060D9F9 /* execserver.defs */,
-                               F97FF35F1C236402000ACDD2 /* nocr.c */,
+                               F97FF35F1C236402000ACDD2 /* nocr.cpp */,
                                37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */,
+                               37CE9D192321A7EB001FBA91 /* chroot_util.cpp */,
                                F9ED4CC60630A7F100DF4E74 /* dyld_debugger.cpp */,
                                F9ED4CC70630A7F100DF4E74 /* dyld2.cpp */,
                                F9ED4CC80630A7F100DF4E74 /* dyld2.h */,
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
+               3721A631230CABAF00594066 /* Headers */ = {
+                       isa = PBXHeadersBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F92C7E0221E59840000D12B5 /* Headers */ = {
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        productReference = 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */;
                        productType = "com.apple.product-type.tool";
                };
-               377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = {
+               3715A2FD232320BC0059433D /* ContainerizedTestRunner */ = {
                        isa = PBXNativeTarget;
-                       buildConfigurationList = 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */;
+                       buildConfigurationList = 3715A306232320BD0059433D /* Build configuration list for PBXNativeTarget "ContainerizedTestRunner" */;
                        buildPhases = (
-                               377685F31AC4B27D00026E6C /* make dyld_cache_config.h */,
-                               377685F41AC4B27D00026E6C /* Sources */,
-                               377685FD1AC4B27D00026E6C /* Frameworks */,
-                               377685FE1AC4B27D00026E6C /* CopyFiles */,
+                               3715A3092327003C0059433D /* Build Everything */,
+                               3715A2FA232320BC0059433D /* Sources */,
+                               3715A2FB232320BC0059433D /* Frameworks */,
+                               3715A2FC232320BC0059433D /* Resources */,
                        );
                        buildRules = (
                        );
                        dependencies = (
+                               3715A30B23272F890059433D /* PBXTargetDependency */,
                        );
-                       name = multi_dyld_shared_cache_builder;
-                       productName = update_os_interlinked_dylib;
-                       productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
+                       name = ContainerizedTestRunner;
+                       productName = local_test_runner;
+                       productReference = 3715A2FE232320BC0059433D /* ContainerizedTestRunner.xctest */;
+                       productType = "com.apple.product-type.bundle.unit-test";
+               };
+               3721A634230CABAF00594066 /* test_support */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 3721A638230CABAF00594066 /* Build configuration list for PBXNativeTarget "test_support" */;
+                       buildPhases = (
+                               3721A631230CABAF00594066 /* Headers */,
+                               3721A632230CABAF00594066 /* Sources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = test_support;
+                       productName = test_support;
+                       productReference = 3721A635230CABAF00594066 /* libtest_support.a */;
+                       productType = "com.apple.product-type.library.static";
+               };
+               3721B6A72321A75B006F6AB7 /* chroot_util */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 3721B6AE2321A75B006F6AB7 /* Build configuration list for PBXNativeTarget "chroot_util" */;
+                       buildPhases = (
+                               3721B6A42321A75B006F6AB7 /* Sources */,
+                               3721B6A52321A75B006F6AB7 /* Frameworks */,
+                               3721B6A62321A75B006F6AB7 /* CopyFiles */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = chroot_util;
+                       productName = chroot_builder;
+                       productReference = 3721B6A82321A75B006F6AB7 /* chroot_util */;
                        productType = "com.apple.product-type.tool";
                };
                37F597CC2061EB4200F9B6F9 /* dyld_usage */ = {
                        );
                        name = nocr;
                        productName = nocr;
-                       productReference = F97FF3561C23638F000ACDD2 /* nocr */;
+                       productReference = 37065AA72310856E00A20034 /* nocr */;
                        productType = "com.apple.product-type.tool";
                };
                F98E37762332D048003706B4 /* update_dyld_shared_cache_root_mode_tool */ = {
                        isa = PBXProject;
                        attributes = {
                                DefaultBuildSystemTypeForWorkspace = Latest;
-                               LastUpgradeCheck = 1000;
+                               LastUpgradeCheck = 1120;
                                TargetAttributes = {
+                                       3715A2FD232320BC0059433D = {
+                                               CreatedOnToolsVersion = 11.0;
+                                               ProvisioningStyle = Automatic;
+                                       };
+                                       3721A634230CABAF00594066 = {
+                                               CreatedOnToolsVersion = 11.0;
+                                               ProvisioningStyle = Automatic;
+                                       };
+                                       3721B6A72321A75B006F6AB7 = {
+                                               CreatedOnToolsVersion = 11.0;
+                                               ProvisioningStyle = Automatic;
+                                       };
                                        37A0AD0A1C15FFF500731E50 = {
                                                CreatedOnToolsVersion = 8.0;
                                        };
                        };
                        buildConfigurationList = F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */;
                        compatibilityVersion = "Xcode 3.2";
-                       developmentRegion = English;
+                       developmentRegion = en;
                        hasScannedForEncodings = 1;
                        knownRegions = (
-                               English,
-                               Japanese,
-                               French,
-                               German,
+                               en,
+                               Base,
                        );
                        mainGroup = F9ED4C870630A72200DF4E74;
                        productRefGroup = F9ED4C990630A76000DF4E74 /* Products */;
                                F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */,
                                F92C7DE521E59840000D12B5 /* libdyld_driverkit */,
                                F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */,
-                               377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
                                F98E37762332D048003706B4 /* update_dyld_shared_cache_root_mode_tool */,
                                3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */,
                                F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */,
                                F97FF3551C23638F000ACDD2 /* nocr */,
                                37F597CC2061EB4200F9B6F9 /* dyld_usage */,
                                F9556D3820C1F896004DF62A /* dyldinfo */,
+                               3721A634230CABAF00594066 /* test_support */,
+                               3721B6A72321A75B006F6AB7 /* chroot_util */,
+                               3715A2FD232320BC0059433D /* ContainerizedTestRunner */,
                        );
                };
 /* End PBXProject section */
 
+/* Begin PBXResourcesBuildPhase section */
+               3715A2FC232320BC0059433D /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXResourcesBuildPhase section */
+
 /* Begin PBXShellScriptBuildPhase section */
                3703A1121B38C1B300ADBA7F /* make dyld_cache_config.h */ = {
                        isa = PBXShellScriptBuildPhase;
                        shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n    # if iOS SDK not available, use MacOSX SDK\n    ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n    echo -n \"#define ARM_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM64_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM64_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n    if [ \"$?\" -eq \"0\" ]; then\n        echo -n \"#define ARM64_32_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_BASE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n        echo -n \"#define ARM64_32_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    fi\nelse\n    echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n    exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n    mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n    cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n    mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n    cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
                        showEnvVarsInLog = 0;
                };
+               3715A3092327003C0059433D /* Build Everything */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputFileListPaths = (
+                       );
+                       inputPaths = (
+                       );
+                       name = "Build Everything";
+                       outputFileListPaths = (
+                       );
+                       outputPaths = (
+                       );
+                       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;
                        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";
                        showEnvVarsInLog = 0;
                };
-               377685F31AC4B27D00026E6C /* make dyld_cache_config.h */ = {
+               37E2FC9D22F62FE1004AF213 /* install */ = {
                        isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 2147483647;
+                       buildActionMask = 8;
                        files = (
                        );
+                       inputFileListPaths = (
+                       );
                        inputPaths = (
                        );
-                       name = "make dyld_cache_config.h";
+                       name = install;
+                       outputFileListPaths = (
+                       );
                        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\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";
+                       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";
                        showEnvVarsInLog = 0;
                };
                C1225E3E21FA84BF0079CF9C /* create dyld_cache_config.h */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "\nif [ \"${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";
+                       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 */ = {
                        );
                        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 = "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";
                        showEnvVarsInLog = 0;
                };
                F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/sh;
-                       shellScript = "${SRCROOT}/testing/build_tests.py && cp ${SRCROOT}/testing/run_all_dyld_tests.py ${DSTROOT}/AppleInternal/CoreOS/tests/dyld/\n";
+                       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";
                        showEnvVarsInLog = 0;
                };
 /* End PBXShellScriptBuildPhase section */
                                F93D73531F8FF7C2007D9413 /* MachOAnalyzer.cpp in Sources */,
                                37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */,
                                C18A75F9209A1AF600DC01BB /* JSONReader.mm in Sources */,
+                               C11ECA92233C307C0011726F /* SharedCacheBuilder.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 */,
-                               37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */,
                                37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */,
                                37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */,
                                C1F003CE213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */,
                                37908A321E3ED667009613FA /* FileUtils.cpp in Sources */,
-                               37908A2E1E3A8632009613FA /* Manifest.mm in Sources */,
                                C1D268351FE0A77B009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
                                37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
-               377685F41AC4B27D00026E6C /* Sources */ = {
+               3715A2FA232320BC0059433D /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               3715A301232320BD0059433D /* ContainerizedTestRunner.mm in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               3721A632230CABAF00594066 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               F93D734B1F8FF79E007D9413 /* MachOFile.cpp in Sources */,
-                               F93D734C1F8FF79E007D9413 /* MachOLoaded.cpp in Sources */,
-                               F93D734D1F8FF79E007D9413 /* MachOAnalyzer.cpp in Sources */,
-                               C1F003CD213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */,
-                               37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */,
-                               37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */,
-                               37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */,
-                               37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */,
-                               37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */,
-                               37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */,
-                               37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */,
-                               37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */,
-                               37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */,
-                               37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */,
-                               37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */,
-                               37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */,
-                               F93D73481F8FF780007D9413 /* Closure.cpp in Sources */,
-                               F93D73491F8FF780007D9413 /* ClosureWriter.cpp in Sources */,
-                               F93D734A1F8FF780007D9413 /* ClosureBuilder.cpp in Sources */,
-                               C1D268401FE9B464009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
+                               37065AAB2310A18300A20034 /* execserver.defs in Sources */,
+                               37382F68230CADEE00E375CE /* test_support.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               3721B6A42321A75B006F6AB7 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               375A4C75233DE09E00CFBD6B /* Diagnostics.cpp in Sources */,
+                               375A4C74233DE07600CFBD6B /* MachOFile.cpp in Sources */,
+                               37CE9D1A2321A7EB001FBA91 /* chroot_util.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                C187B91B1FE0683F0042D3B7 /* OptimizerLinkedit.cpp in Sources */,
                                C187B90E1FE067CD0042D3B7 /* ClosureWriter.cpp in Sources */,
                                C187B91E1FE0684C0042D3B7 /* AdjustDylibSegments.cpp in Sources */,
-                               C187B9191FE0682C0042D3B7 /* BuilderUtils.mm in Sources */,
                                C187B90F1FE067D30042D3B7 /* ClosureBuilder.cpp in Sources */,
                                C187B9131FE067F10042D3B7 /* CacheBuilder.cpp in Sources */,
                                C187B9121FE067E60042D3B7 /* MachOAnalyzer.cpp in Sources */,
                                C187B9101FE067D90042D3B7 /* MachOFile.cpp in Sources */,
                                C187B9111FE067E10042D3B7 /* MachOLoaded.cpp in Sources */,
                                C187B90D1FE067C70042D3B7 /* Closure.cpp in Sources */,
+                               C11ECA94233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
                                C1D268311FE0891C009F115B /* mrm_shared_cache_builder.cpp in Sources */,
                                C187B9141FE067FA0042D3B7 /* OptimizerBranches.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 */,
                                F96354361DCD74A400895049 /* FileUtils.cpp in Sources */,
                                F96354371DCD74A400895049 /* Diagnostics.cpp in Sources */,
                                F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */,
+                               C11ECA93233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
                                F96354391DCD74A400895049 /* OptimizerObjC.cpp in Sources */,
                                37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */,
                                F963543C1DCD74A400895049 /* OptimizerLinkedit.cpp in Sources */,
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               F93F46521FA420850060D9F9 /* execserver.defs in Sources */,
-                               F97FF3611C23640C000ACDD2 /* nocr.c in Sources */,
+                               F97FF3611C23640C000ACDD2 /* nocr.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                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 */,
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               C176CB5C2321AB74009C1259 /* ClosureFileSystemPhysical.cpp in Sources */,
                                C1960ED02090D9F0007E3E6B /* Diagnostics.cpp in Sources */,
                                C1960ED42090DA09007E3E6B /* Closure.cpp in Sources */,
                                C1960ECF2090D9E5007E3E6B /* DyldSharedCache.cpp in Sources */,
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
+               3715A30B23272F890059433D /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 3721B6A72321A75B006F6AB7 /* chroot_util */;
+                       targetProxy = 3715A30A23272F890059433D /* PBXContainerItemProxy */;
+               };
+               37382F6A230CB46500E375CE /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 3721A634230CABAF00594066 /* test_support */;
+                       targetProxy = 37382F69230CB46500E375CE /* PBXContainerItemProxy */;
+               };
                37A0AD0F1C16000F00731E50 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */;
                        target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */;
                        targetProxy = F96543A01E343601003C5540 /* PBXContainerItemProxy */;
                };
-               F97FF3661C237F97000ACDD2 /* PBXTargetDependency */ = {
-                       isa = PBXTargetDependency;
-                       target = F97FF3551C23638F000ACDD2 /* nocr */;
-                       targetProxy = F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */;
-               };
                F99B8EB20FEC220C00701838 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
                        };
                        name = Release;
                };
-               377686001AC4B27D00026E6C /* Debug */ = {
+               3715A307232320BD0059433D /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
-                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_CXX_LIBRARY = "libc++";
-                               CLANG_ENABLE_MODULES = NO;
+                               CLANG_ENABLE_MODULES = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
-                               CLANG_WARN_BOOL_CONVERSION = YES;
-                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_EMPTY_BODY = YES;
-                               CLANG_WARN_ENUM_CONVERSION = YES;
-                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
-                               CLANG_WARN_UNREACHABLE_CODE = YES;
-                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_STYLE = Automatic;
+                               COMBINE_HIDPI_IMAGES = YES;
                                COPY_PHASE_STRIP = NO;
-                               CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
-                               DEAD_CODE_STRIPPING = YES;
-                               ENABLE_STRICT_OBJC_MSGSEND = YES;
-                               FRAMEWORK_SEARCH_PATHS = (
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               ENABLE_TESTABILITY = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
                                        "$(inherited)",
-                                       "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
-                                       "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
-                                       "$(SDKROOT)/AppleInternal/Library/Frameworks",
                                );
-                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               INFOPLIST_FILE = local_test_runner/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+                               MACOSX_DEPLOYMENT_TARGET = 10.15;
+                               MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+                               MTL_FAST_MATH = YES;
+                               OTHER_CFLAGS = "-DCHROOT_PATH=\\\"$(DERIVED_FILES_DIR)/TestRoot\\\"";
+                               PRODUCT_BUNDLE_IDENTIFIER = "com.apple.dyld.local-test-runner";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                               STRIP_INSTALLED_PRODUCT = YES;
+                               USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache $SRCROOT/testing/include";
+                       };
+                       name = Debug;
+               };
+               3715A308232320BD0059433D /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_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;
+                               COMBINE_HIDPI_IMAGES = YES;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               ENABLE_NS_ASSERTIONS = NO;
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               INFOPLIST_FILE = local_test_runner/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+                               MACOSX_DEPLOYMENT_TARGET = 10.15;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               MTL_FAST_MATH = YES;
+                               OTHER_CFLAGS = "-DCHROOT_PATH=\\\"$(DERIVED_FILES_DIR)/TestRoot\\\"";
+                               PRODUCT_BUNDLE_IDENTIFIER = "com.apple.dyld.local-test-runner";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                               STRIP_INSTALLED_PRODUCT = YES;
+                               USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache $SRCROOT/testing/include";
+                       };
+                       name = Release;
+               };
+               3721A636230CABAF00594066 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_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;
+                               EXECUTABLE_PREFIX = lib;
+                               EXPORTED_SYMBOLS_FILE = "";
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
                                GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_ENABLE_CPP_EXCEPTIONS = YES;
+                               GCC_ENABLE_OBJC_EXCEPTIONS = NO;
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "DEBUG=1",
                                        "$(inherited)",
-                                       "BUILDING_CACHE_BUILDER=1",
                                );
-                               GCC_SYMBOLS_PRIVATE_EXTERN = NO;
-                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
-                               GCC_WARN_UNDECLARED_SELECTOR = YES;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
-                               GCC_WARN_UNUSED_FUNCTION = YES;
-                               GCC_WARN_UNUSED_VARIABLE = YES;
-                               INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
-                               MACOSX_DEPLOYMENT_TARGET = 10.11;
-                               MTL_ENABLE_DEBUG_INFO = YES;
-                               ONLY_ACTIVE_ARCH = YES;
-                               OTHER_CFLAGS = (
-                                       "-DBOM_SUPPORT=1",
-                                       "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
-                               );
-                               PRODUCT_NAME = multi_dyld_shared_cache_builder;
+                               GENERATE_MASTER_OBJECT_FILE = YES;
+                               LLVM_LTO = NO;
+                               MACOSX_DEPLOYMENT_TARGET = 10.14;
+                               MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+                               MTL_FAST_MATH = YES;
+                               PRELINK_FLAGS = "-exported_symbols_list $(SRCROOT)/testing/lib/test_support.exp";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
+                               SKIP_INSTALL = YES;
                                STRIP_INSTALLED_PRODUCT = NO;
-                               USER_HEADER_SEARCH_PATHS = "../launch-cache/";
-                               VALID_ARCHS = "x86_64 x86_64h";
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
+                               USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include";
                        };
                        name = Debug;
                };
-               377686011AC4B27D00026E6C /* Release */ = {
+               3721A637230CABAF00594066 /* Release */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
-                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_CXX_LIBRARY = "libc++";
-                               CLANG_ENABLE_MODULES = NO;
+                               CLANG_ENABLE_MODULES = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
-                               CLANG_WARN_BOOL_CONVERSION = YES;
-                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_EMPTY_BODY = YES;
-                               CLANG_WARN_ENUM_CONVERSION = YES;
-                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
-                               CLANG_WARN_UNREACHABLE_CODE = YES;
-                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_STYLE = Automatic;
                                COPY_PHASE_STRIP = NO;
-                               CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
-                               DEAD_CODE_STRIPPING = YES;
-                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               DEBUG_INFORMATION_FORMAT = dwarf;
                                ENABLE_NS_ASSERTIONS = NO;
-                               ENABLE_STRICT_OBJC_MSGSEND = YES;
-                               FRAMEWORK_SEARCH_PATHS = (
+                               EXECUTABLE_PREFIX = lib;
+                               EXPORTED_SYMBOLS_FILE = "";
+                               GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_ENABLE_OBJC_EXCEPTIONS = NO;
+                               GCC_OPTIMIZATION_LEVEL = s;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GENERATE_MASTER_OBJECT_FILE = YES;
+                               LLVM_LTO = NO;
+                               MACOSX_DEPLOYMENT_TARGET = 10.14;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               MTL_FAST_MATH = YES;
+                               PRELINK_FLAGS = "-exported_symbols_list $(SRCROOT)/testing/lib/test_support.exp";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                               SKIP_INSTALL = YES;
+                               STRIP_INSTALLED_PRODUCT = NO;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
+                               USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include";
+                       };
+                       name = Release;
+               };
+               3721B6AC2321A75B006F6AB7 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               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_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_ENABLE_CPP_EXCEPTIONS = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
                                        "$(inherited)",
-                                       "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
-                                       "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
-                                       "$(SDKROOT)/AppleInternal/Library/Frameworks",
                                );
-                               GCC_C_LANGUAGE_STANDARD = gnu99;
-                               GCC_ENABLE_CPP_EXCEPTIONS = YES;
-                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
-                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
-                               GCC_WARN_UNDECLARED_SELECTOR = YES;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
-                               GCC_WARN_UNUSED_FUNCTION = YES;
-                               GCC_WARN_UNUSED_VARIABLE = YES;
-                               INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
-                               MACOSX_DEPLOYMENT_TARGET = 10.11;
+                               MACOSX_DEPLOYMENT_TARGET = 10.15;
+                               MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+                               MTL_FAST_MATH = YES;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                       };
+                       name = Debug;
+               };
+               3721B6AD2321A75B006F6AB7 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               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_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_ENABLE_CPP_EXCEPTIONS = NO;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               MACOSX_DEPLOYMENT_TARGET = 10.15;
                                MTL_ENABLE_DEBUG_INFO = NO;
-                               OTHER_CFLAGS = (
-                                       "-DBOM_SUPPORT=1",
-                                       "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
-                               );
-                               PRODUCT_NAME = multi_dyld_shared_cache_builder;
+                               MTL_FAST_MATH = YES;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
-                               STRIP_INSTALLED_PRODUCT = NO;
-                               USER_HEADER_SEARCH_PATHS = "../launch-cache/";
-                               VALID_ARCHS = "x86_64 x86_64h";
-                               VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                };
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "DEBUG=1",
+                                       "BUILDING_DYLDINFO=1",
                                        "$(inherited)",
                                );
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                GCC_C_LANGUAGE_STANDARD = gnu11;
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_DYLDINFO=1";
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
                                MACOSX_DEPLOYMENT_TARGET = 10.14;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
+                               USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include";
                        };
                        name = Debug;
                };
                                MTL_ENABLE_DEBUG_INFO = NO;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
+                               USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include";
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               OTHER_CFLAGS = "-DBUILDING_SHARED_CACHE_UTIL=1";
                                PRODUCT_NAME = dyld_shared_cache_util;
                                SDKROOT = macosx.internal;
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               OTHER_CFLAGS = "-DBUILDING_SHARED_CACHE_UTIL=1";
                                PRODUCT_NAME = dyld_shared_cache_util;
                                SDKROOT = macosx.internal;
                                SKIP_INSTALL = NO;
                                );
                                OTHER_LDFLAGS = (
                                        "-Wl,-no_inits",
+                                       "$(USE_CHAINED_FIXUPS)",
                                        "-nostdlib",
                                        "-lCrashReporterClient",
                                        "$(LIBSYSTEM_LIBS)",
                                );
                                OTHER_LDFLAGS = (
                                        "-Wl,-no_inits",
+                                       "$(USE_CHAINED_FIXUPS)",
                                        "-nostdlib",
                                        "-lCrashReporterClient",
                                        "$(LIBSYSTEM_LIBS)",
                                HEADER_SEARCH_PATHS = ./include;
                                ONLY_ACTIVE_ARCH = YES;
                                SDKROOT = macosx.internal;
+                               STRIP_INSTALLED_PRODUCT = NO;
                                USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache";
                                WARNING_CFLAGS = "-Wimplicit-fallthrough";
                        };
                        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";
+                               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";
+                               USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/dyld3 $(SRCROOT)/shared-cache $(SRCROOT)/include $(SRCROOT)/testing/include";
                        };
                        name = Release;
                };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
-               377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */ = {
+               3715A306232320BD0059433D /* Build configuration list for PBXNativeTarget "ContainerizedTestRunner" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               3715A307232320BD0059433D /* Debug */,
+                               3715A308232320BD0059433D /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               3721A638230CABAF00594066 /* Build configuration list for PBXNativeTarget "test_support" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               3721A636230CABAF00594066 /* Debug */,
+                               3721A637230CABAF00594066 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               3721B6AE2321A75B006F6AB7 /* Build configuration list for PBXNativeTarget "chroot_util" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
-                               377686001AC4B27D00026E6C /* Debug */,
-                               377686011AC4B27D00026E6C /* Release */,
+                               3721B6AC2321A75B006F6AB7 /* Debug */,
+                               3721B6AD2321A75B006F6AB7 /* Release */,
                        );
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
index 476c23a4f288f5ebd6323dd968eeba4c018046ed..8ed4a6dfb7771bc65e020ca7630630a55f8808c6 100644 (file)
@@ -58,6 +58,7 @@
 #include <ptrauth.h>
 #endif
 
+extern mach_header __dso_handle;
 
 namespace dyld {
     extern dyld_all_image_infos dyld_all_image_infos;
@@ -469,8 +470,13 @@ 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) {
-            sdk = 0x000A0F00;
+            if (sdk >= 0x000D0400) {
+                sdk = 0x000A0F04;
+            } else {
+                sdk = 0x000A0F00;
+            }
         }
         callback((const dyld_platform_t)platform, sdk, minOS);
     });
@@ -506,29 +512,24 @@ void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld
     // FIXME: Once dyld2 is gone gAllImages.mainExecutable() will be valid in all cases
     // and we can stop calling _NSGetMachExecuteHeader()
     if (mh == (const struct mach_header*)_NSGetMachExecuteHeader()) {
-        // Cache the main executable and short circuit parsing the
-        if (mainExecutablePlatform == 0) {
-            dyld_get_image_versions_internal(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
-#if 0
-                //FIXME: Reenable this once Libc supports dynamic platforms.
-                if (platform == PLATFORM_MACOS && dyld_get_active_platform() == PLATFORM_IOSMAC) {
-                    //FIXME: This version should be generated at link time
-                    mainExecutablePlatform = PLATFORM_IOSMAC;
-                    mainExecutableSDKVersion = 0x000D0000;
-                    mainExecutableMinOSVersion = 0x000D0000;
-                } else {
-                    mainExecutablePlatform = platform;
-                    mainExecutableSDKVersion = sdk_version;
-                    mainExecutableMinOSVersion = min_version;
-                }
-#else
-                mainExecutablePlatform = platform;
-                mainExecutableSDKVersion = sdk_version;
-                mainExecutableMinOSVersion = min_version;
-#endif
-                //FIXME: Assert if more than one command?
-            });
+        if (mainExecutablePlatform) {
+            return callback(mainExecutablePlatform, mainExecutableSDKVersion, mainExecutableMinOSVersion);
         }
+        mainExecutablePlatform = ::dyld_get_active_platform();
+        dyld_get_image_versions_internal(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+            if (platform == PLATFORM_MACOS && dyld_get_base_platform(mainExecutablePlatform) == PLATFORM_IOS) {
+                // We are running with DYLD_FORCE_PLATFORM, use the current OSes values
+                dyld_get_image_versions_internal(&__dso_handle, ^(dyld_platform_t dyld_platform, uint32_t dyld_sdk_version, uint32_t dyld_min_version) {
+                    if (dyld_get_base_platform(dyld_platform) == PLATFORM_IOS) {
+                        mainExecutableSDKVersion = dyld_sdk_version;
+                        mainExecutableMinOSVersion = dyld_min_version;
+                    }
+                });
+            } else {
+                 mainExecutableSDKVersion = sdk_version;
+                 mainExecutableMinOSVersion = min_version;
+             }
+        });
         return callback(mainExecutablePlatform, mainExecutableSDKVersion, mainExecutableMinOSVersion);
     }
 #if TARGET_OS_EMBEDDED
@@ -1387,7 +1388,14 @@ void _dyld_missing_symbol_abort()
     // dyld3 binds all such missing symbols to this one handler.
     // We need the crash log to contain the backtrace so someone can
     // figure out the symbol.
-    abort_report_np("missing lazy symbol called");
+
+    auto allImageInfos = gAllImages.oldAllImageInfo();
+    allImageInfos->errorKind           = DYLD_EXIT_REASON_SYMBOL_MISSING;
+    allImageInfos->errorClientOfDylibPath   = "<unknown>";
+    allImageInfos->errorTargetDylibPath     = "<unknown>";
+    allImageInfos->errorSymbol              = "<unknown>";
+
+    halt("missing lazy symbol called");
 }
 
 const char* _dyld_get_objc_selector(const char* selName)
index 21b0c2e9822ef74ae1431261763dbfae0d125c97..ef1fffe8e9cbbb3b607dec207d59d2ececcf3b6b 100644 (file)
@@ -1155,6 +1155,18 @@ bool LaunchClosure::hasInsertedLibraries() const
     return getFlags().hasInsertedLibraries;
 }
 
+bool LaunchClosure::hasProgramVars(uint32_t& runtimeOffset) const
+{
+    if ( !getFlags().hasProgVars )
+        return false;
+    uint32_t payloadSize = 0;
+    const uint8_t* buffer = (const uint8_t*)findAttributePayload(Type::progVars, &payloadSize);
+    if (buffer == nullptr)
+        return false;
+    runtimeOffset = *((uint32_t*)buffer);
+    return true;
+}
+
 bool LaunchClosure::usedInterposing() const
 {
     return getFlags().usedInterposing;
index ab1a15fdbfb5c72deb38e4279c5903b46dc5f302..ace36a542d4ba08affb925d3a5a4ea867994737a 100644 (file)
@@ -119,8 +119,9 @@ struct VIS_HIDDEN TypedBytes
         selectorTable           = 45,  // uint32_t + (sizeof(ObjCSelectorImage) * count) + hashTable size
         classTable              = 46,  // (3 * uint32_t) + (sizeof(ObjCClassImage) * count) + classHashTable size + protocolHashTable size
         warning                 = 47,  // len = uint32_t + length path + 1, use one entry per warning
-        duplicateClassesTable   = 48, // duplicateClassesHashTable
-   };
+        duplicateClassesTable   = 48,  // duplicateClassesHashTable
+        progVars                = 49,  // sizeof(uint32_t)
+    };
 
     Type         type          : 8;
     uint32_t     payloadLength : 24;
@@ -227,7 +228,7 @@ struct VIS_HIDDEN Image : ContainerTypedBytes
             return (raw != rhs.raw);
         }
      };
-     static_assert(sizeof(ResolvedSymbolTarget) == 8);
+     static_assert(sizeof(ResolvedSymbolTarget) == 8, "Invalid size");
 
 
     // ObjC optimisations
@@ -718,6 +719,7 @@ struct VIS_HIDDEN LaunchClosure : public Closure
     void                duplicateClassesHashTable(const ObjCClassDuplicatesOpt*& duplicateClassesHashTable) const;
     bool                hasInsertedLibraries() const;
     bool                hasInterposings() const;
+    bool                hasProgramVars(uint32_t& runtimeOffset) const;
     
     static bool         buildClosureCachePath(const char* mainExecutablePath, char closurePath[], const char* tempDir,
                                               bool makeDirsIfMissing);
@@ -731,8 +733,9 @@ private:
                         usedFallbackPaths        :  1,
                         initImageCount           : 16,
                         hasInsertedLibraries     :  1,
+                        hasProgVars              :  1,
                         usedInterposing          :  1,
-                        padding                  : 12;
+                        padding                  : 11;
     };
     const Flags&        getFlags() const;
 };
index 10da1bf70e887cd4655947005882c249e74f4143..2d83fc593c200d4850000daf192ba9a635c26789 100644 (file)
@@ -435,15 +435,24 @@ bool ClosureBuilder::expandAtLoaderPath(const char* loadPath, bool fromLCRPATH,
         case AtPath::all:
             break;
     }
-    if ( strncmp(loadPath, "@loader_path/", 13) != 0 )
-        return false;
-
-    strlcpy(fixedPath, loadedImage.path(), PATH_MAX);
-    char* lastSlash = strrchr(fixedPath, '/');
-    if ( lastSlash != nullptr ) {
-        strcpy(lastSlash+1, &loadPath[13]);
-        return true;
+    if ( strncmp(loadPath, "@loader_path/", 13) == 0 ) {
+        strlcpy(fixedPath, loadedImage.path(), PATH_MAX);
+        char* lastSlash = strrchr(fixedPath, '/');
+        if ( lastSlash != nullptr ) {
+            strcpy(lastSlash+1, &loadPath[13]);
+            return true;
+        }
     }
+    else if ( fromLCRPATH && (strcmp(loadPath, "@loader_path") == 0) ) {
+        // <rdar://problem/52881387> in LC_RPATH allow "@loader_path" without trailing slash
+        strlcpy(fixedPath, loadedImage.path(), PATH_MAX);
+        char* lastSlash = strrchr(fixedPath, '/');
+        if ( lastSlash != nullptr ) {
+            lastSlash[1] = '\0';
+            return true;
+        }
+    }
+
     return false;
 }
 
@@ -459,18 +468,25 @@ bool ClosureBuilder::expandAtExecutablePath(const char* loadPath, bool fromLCRPA
         case AtPath::all:
             break;
     }
-    if ( strncmp(loadPath, "@executable_path/", 17) != 0 )
-        return false;
 
-    if ( _atPathHandling != AtPath::all )
-        return false;
-
-    strlcpy(fixedPath, _mainProgLoadPath, PATH_MAX);
-    char* lastSlash = strrchr(fixedPath, '/');
-    if ( lastSlash != nullptr ) {
-        strcpy(lastSlash+1, &loadPath[17]);
-        return true;
+    if ( strncmp(loadPath, "@executable_path/", 17) == 0 ) {
+        strlcpy(fixedPath, _mainProgLoadPath, PATH_MAX);
+        char* lastSlash = strrchr(fixedPath, '/');
+        if ( lastSlash != nullptr ) {
+            strcpy(lastSlash+1, &loadPath[17]);
+            return true;
+        }
+    }
+    else if ( fromLCRPATH && (strcmp(loadPath, "@executable_path") == 0) ) {
+        // <rdar://problem/52881387> in LC_RPATH allow "@executable_path" without trailing slash
+        strlcpy(fixedPath, _mainProgLoadPath, PATH_MAX);
+        char* lastSlash = strrchr(fixedPath, '/');
+        if ( lastSlash != nullptr ) {
+            lastSlash[1] = '\0';
+            return true;
+        }
     }
+
     return false;
 }
 
@@ -1435,8 +1451,6 @@ void ClosureBuilder::addChainedFixupInfo(ImageWriter& writer, BuilderLoadedImage
         Image::ResolvedSymbolTarget target;
         ResolvedTargetInfo          targetInfo;
         if ( !findSymbol(forImage, libOrdinal, symbolName, weakImport, false, addend, target, targetInfo) ) {
-            const char* expectedInPath = forImage.loadAddress()->dependentDylibLoadPath(libOrdinal-1);
-            _diag.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName, expectedInPath, forImage.path());
             stop = true;
             return;
         }
@@ -1536,6 +1550,7 @@ bool ClosureBuilder::findSymbol(BuilderLoadedImage& fromImage, int libOrdinal, c
     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) {
@@ -2079,6 +2094,11 @@ bool ClosureBuilder::optimizeObjC(Array<ImageWriter>& writers) {
                 case DYLD_CHAINED_PTR_64:
                     // 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:
+                    // FIXME: Test 64-bit offset chained fixups then enable this.
+                    continue;
                 case DYLD_CHAINED_PTR_32:
                 case DYLD_CHAINED_PTR_32_CACHE:
                 case DYLD_CHAINED_PTR_32_FIRMWARE:
@@ -3174,6 +3194,16 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil
         closureWriter.setBootUUID(bootSessionUUID);
 #endif
 
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+    uint32_t progVarsOffset;
+    if ( mainExecutable->hasProgramVars(_diag, progVarsOffset) ) {
+        // on macOS binaries may have a __dyld section that has ProgramVars to use
+        closureWriter.setHasProgramVars(progVarsOffset);
+    }
+    if ( _diag.hasError() )
+        return nullptr;
+#endif
+
     // record any interposing info
     if ( !_interposingDisabled ) {
         imageArray->forEachImage(^(const Image* image, bool &stop) {
index 48d28b447e9237cd79bae69fe0e02eba0c2bf95b..9df3d40c09f6ea186ec2031604e3b74628d49e13 100644 (file)
@@ -159,6 +159,8 @@ static const char* nameForType(TypedBytes::Type type) {
         return "warning";
     case TypedBytes::Type::duplicateClassesTable:
         return "duplicateClassesTable";
+    case TypedBytes::Type::progVars:
+        return "programVars";
     }
 }
 
@@ -699,6 +701,11 @@ static Node buildClosureNode(const LaunchClosure* closure, const Array<const Ima
         root.map["objc-duplicate-class-warnings"].array.push_back(warningNode);
     });
 
+    // add program vars info for old macOS binaries
+    uint32_t progVarsOffset;
+    if ( closure->hasProgramVars(progVarsOffset) )
+        root.map["program-vars-offset"].value = hex8(progVarsOffset);
+
 #if 0
 
 
index f4911bc7077d965d7ee4947d823ca0ab1f97417d..2501f87b74aae28d69598ebf5e64f34e9c619ddb 100644 (file)
@@ -632,6 +632,12 @@ void LaunchClosureWriter::setBootUUID(const char* uuid)
     append(TypedBytes::Type::bootUUID, temp, paddedSize);
 }
 
+void LaunchClosureWriter::setHasProgramVars(uint32_t offset)
+{
+    getFlags().hasProgVars = true;
+    append(TypedBytes::Type::progVars, &offset, sizeof(uint32_t));
+}
+
 void LaunchClosureWriter::setObjCSelectorInfo(const Array<uint8_t>& hashTable, const Array<Image::ObjCSelectorImage>& hashTableImages) {
     uint32_t count = (uint32_t)hashTableImages.count();
     uint32_t totalSize = (uint32_t)(sizeof(count) + (sizeof(Image::ObjCSelectorImage) * count) + hashTable.count());
index 277431896daa46d8787f5565e5f6169cd2208fb2..d190deeff688b39bac22399282999b6d540e4f91 100644 (file)
@@ -166,6 +166,7 @@ public:
     void                    setObjCClassAndProtocolInfo(const Array<uint8_t>& classHashTable, const Array<uint8_t>& protocolHashTable,
                                                         const Array<Image::ObjCClassImage>& hashTableImages);
     void                    setObjCDuplicateClassesInfo(const Array<uint8_t>& hashTable);
+    void                    setHasProgramVars(uint32_t offset);
 
 private:
     LaunchClosure::Flags&   getFlags();
index ec3b8d7ab2f3aed6ba87855081a8ce6c20a97a5f..8279c5c12d5f2322447c845a7d1198594dc3fb4a 100644 (file)
@@ -165,6 +165,11 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice
         case MH_BUNDLE:
         case MH_DYLINKER:
            break;
+#if BUILDING_DYLDINFO
+        // Allow offline tools to analyze binaries dyld doesn't load
+        case MH_KEXT_BUNDLE:
+            break;
+#endif
         default:
             diag.error("could not use '%s' because it is not a dylib, bundle, or executable, filetype=0x%08X", path, this->filetype);
            return false;
@@ -177,8 +182,11 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice
 
     // filter out static executables
     if ( (this->filetype == MH_EXECUTE) && !isDynamicExecutable() ) {
+#if !BUILDING_DYLDINFO
+        // 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)
@@ -212,10 +220,16 @@ bool MachOAnalyzer::validLinkedit(Diagnostics& diag, const char* path) const
     if ( !validLinkeditLayout(diag, path) )
         return false;
 
-    if ( hasChainedFixups() ) {
+    if ( hasLoadCommand(LC_DYLD_CHAINED_FIXUPS) ) {
         if ( !validChainedFixupsInfo(diag, path) )
             return false;
     }
+#if SUPPORT_ARCH_arm64e
+    else if ( (this->cputype == CPU_TYPE_ARM64) && (this->cpusubtype == CPU_SUBTYPE_ARM64E) ) {
+        if ( !validChainedFixupsInfoOldArm64e(diag, path) )
+            return false;
+    }
+#endif
     else {
         // validate rebasing info
         if ( !validRebaseInfo(diag, path) )
@@ -486,7 +500,7 @@ bool MachOAnalyzer::validEmbeddedPaths(Diagnostics& diag, Platform platform, con
         }
     }
 
-    if ( (dependentsCount == 0) && (this->filetype == MH_EXECUTE)  ) {
+    if ( (dependentsCount == 0) && (this->filetype == MH_EXECUTE) && isDynamicExecutable() ) {
         diag.error("in '%s' missing LC_LOAD_DYLIB (must link with at least libSystem.dylib)", path);
         return false;
     }
@@ -640,8 +654,9 @@ bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fi
             }
             if ( (info1.segIndex < info2.segIndex) && !stop1 ) {
                 if ( (info1.vmAddr > info2.vmAddr) || ((info1.fileOffset > info2.fileOffset ) && (info1.fileOffset != 0) && (info2.fileOffset  != 0)) ){
-                    if ( !inDyldCache() && enforceFormat(Malformed::segmentOrder) ) {
+                    if ( !inDyldCache() && enforceFormat(Malformed::segmentOrder) && !isStaticExecutable() ) {
                         // dyld cache __DATA_* segments are moved around
+                        // The static kernel also has segments with vmAddr's before __TEXT
                         diag.error("in '%s' segment load commands out of order with respect to layout for %s and %s", path, info1.segName, info2.segName);
                         badSegments = true;
                         stop1 = true;
@@ -746,6 +761,23 @@ bool MachOAnalyzer::validMain(Diagnostics& diag, const char* path) const
                     diag.error("LC_UNIXTHREAD not valid for arch %s", archName());
                     stop = true;
                 }
+#if BUILDING_DYLDINFO
+                else if ( isStaticExecutable() ) {
+                    __block bool foundSegment = false;
+                    forEachSegment(^(const SegmentInfo& info, bool& stopSegment) {
+                        // Skip segments which don't contain this address
+                        if ( (startAddress < info.vmAddr) || (startAddress >= info.vmAddr+info.vmSize) )
+                            return;
+                        foundSegment = true;
+                        if ( (info.protections & VM_PROT_EXECUTE) == 0 )
+                            diag.error("LC_UNIXTHREAD points to non-executable segment");
+                        stopSegment = true;
+                    });
+                    if (!foundSegment)
+                        diag.error("LC_UNIXTHREAD entry is out of range");
+                    stop = true;
+                }
+#endif
                 else if ( (startAddress < textSegStartAddr) || (startAddress >= textSegStartAddr+textSegStartSize) ) {
                     diag.error("LC_UNIXTHREAD entry not in __TEXT segment");
                     stop = true;
@@ -1206,6 +1238,9 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag,
             }
         }
     }
+    else if ( leInfo.chainedFixups != nullptr ) {
+        // binary uses chained fixups, so do nothing
+    }
     else {
         // old binary, walk relocations
         const uint64_t                  relocsStartAddress = relocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
@@ -1341,6 +1376,7 @@ void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(ui
                 return;
             }
             uint16_t n_desc = is64Bit ? symbols64[symNum].n_desc : symbols32[symNum].n_desc;
+            uint8_t  n_type     = is64Bit ? symbols64[symNum].n_type : symbols32[symNum].n_type;
             uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
             uint32_t strOffset = is64Bit ? symbols64[symNum].n_un.n_strx : symbols32[symNum].n_un.n_strx;
             if ( strOffset > poolSize ) {
@@ -1351,6 +1387,9 @@ void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(ui
             const char* symbolName  = stringPool + strOffset;
             bool        weakImport  = (n_desc & N_WEAK_REF);
             bool        lazy        = (sectionType == S_LAZY_SYMBOL_POINTERS);
+            // 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;
             handler(sectInfo.sectAddr+i*elementSize, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop);
         }
         sectionStop = stop;
@@ -1774,6 +1813,9 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag,
             }
         }
     }
+    else if ( leInfo.chainedFixups != nullptr ) {
+        // binary uses chained fixups, so do nothing
+    }
     else {
         // old binary, process external relocations
         const uint64_t                  relocsStartAddress = relocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
@@ -1807,6 +1849,7 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag,
                 else {
                     uint32_t strOffset  = is64Bit ? symbols64[symbolIndex].n_un.n_strx : symbols32[symbolIndex].n_un.n_strx;
                     uint16_t n_desc     = is64Bit ? symbols64[symbolIndex].n_desc : symbols32[symbolIndex].n_desc;
+                    uint8_t  n_type     = is64Bit ? symbols64[symbolIndex].n_type : symbols32[symbolIndex].n_type;
                     uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
                     if ( strOffset >= poolSize ) {
                         diag.error("external relocation has r_symbolnum=%d which has out of range n_strx", symbolIndex);
@@ -1817,6 +1860,9 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag,
                         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);
+                        // 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;
                         handler("external relocation", leInfo, segmentsInfo, true, true, dylibCount, libOrdinal,
                                 ptrSize, segIndex, segOffset, BIND_TYPE_POINTER, symbolName, weakImport, false, addend, stop);
                     }
@@ -1847,8 +1893,158 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag,
 
 }
 
-
 bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics& diag, const char* path) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return false;
+
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+    getAllSegmentsInfos(diag, segmentsInfo);
+    if ( diag.hasError() )
+        return false;
+
+    // validate dyld_chained_fixups_header
+    const dyld_chained_fixups_header* chainsHeader = (dyld_chained_fixups_header*)getLinkEditContent(leInfo.layout, leInfo.chainedFixups->dataoff);
+    if ( chainsHeader->fixups_version != 0 ) {
+        diag.error("chained fixups, unknown header version");
+        return false;
+    }
+    if ( chainsHeader->starts_offset >= leInfo.chainedFixups->datasize )  {
+        diag.error("chained fixups, starts_offset exceeds LC_DYLD_CHAINED_FIXUPS size");
+        return false;
+    }
+    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:
+            formatEntrySize = sizeof(dyld_chained_import);
+            break;
+        case DYLD_CHAINED_IMPORT_ADDEND:
+            formatEntrySize = sizeof(dyld_chained_import_addend);
+            break;
+        case DYLD_CHAINED_IMPORT_ADDEND64:
+            formatEntrySize = sizeof(dyld_chained_import_addend64);
+            break;
+        default:
+            diag.error("chained fixups, unknown imports_format");
+            return false;
+    }
+    if ( greaterThanAddOrOverflow(chainsHeader->imports_offset, (formatEntrySize * chainsHeader->imports_count), chainsHeader->symbols_offset) ) {
+         diag.error("chained fixups, imports array overlaps symbols");
+         return false;
+    }
+    if ( chainsHeader->symbols_format != 0 )  {
+         diag.error("chained fixups, symbols_format unknown");
+         return false;
+    }
+
+    // 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;
+    }
+    const uint64_t baseAddress = preferredLoadAddress();
+    uint32_t maxValidPointerSeen = 0;
+    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];
+        // 0 offset means this segment has no fixups
+        if ( segInfoOffset == 0 )
+            continue;
+        const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)startsInfo + segInfoOffset);
+        if ( segInfo->size > (endOfStarts - (uint8_t*)segInfo) ) {
+             diag.error("chained fixups, dyld_chained_starts_in_segment for segment #%d overruns imports table", i);
+             return false;
+        }
+
+        // validate dyld_chained_starts_in_segment
+        if ( (segInfo->page_size != 0x1000) && (segInfo->page_size != 0x4000) ) {
+            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);
+            return false;
+        }
+        if ( segInfo->segment_offset != (segmentsInfo[i].vmAddr - baseAddress) ) {
+            diag.error("chained fixups, segment_offset does not match vmaddr from LC_SEGMENT in segment #%d", i);
+            return false;
+        }
+        if ( segInfo->max_valid_pointer != 0 ) {
+            if ( maxValidPointerSeen == 0 ) {
+                // record max_valid_pointer values seen
+                maxValidPointerSeen = segInfo->max_valid_pointer;
+            }
+            else if ( maxValidPointerSeen != segInfo->max_valid_pointer ) {
+                diag.error("chained fixups, different max_valid_pointer values seen in different segments");
+                return false;
+            }
+        }
+        // validate starts table in segment
+        if ( offsetof(dyld_chained_starts_in_segment, page_start[segInfo->page_count]) > segInfo->size ) {
+            diag.error("chained fixups, page_start array overflows size");
+            return false;
+        }
+        uint32_t maxOverflowIndex = (uint32_t)(segInfo->size - offsetof(dyld_chained_starts_in_segment, page_start[segInfo->page_count]))/sizeof(uint16_t);
+        for (int pageIndex=0; pageIndex < segInfo->page_count; ++pageIndex) {
+            uint16_t offsetInPage = segInfo->page_start[pageIndex];
+            if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE )
+                continue;
+            if ( (offsetInPage & DYLD_CHAINED_PTR_START_MULTI) == 0 ) {
+                // this is the offset into the page where the first fixup is
+                if ( offsetInPage > segInfo->page_size ) {
+                    diag.error("chained fixups, in segment #%d page_start[%d]=0x%04X exceeds page size", i, pageIndex, offsetInPage);
+                }
+            }
+            else {
+                // this is actually an index into chain_starts[]
+                uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI;
+                // now verify all starts are within the page and in ascending order
+                uint16_t lastOffsetInPage = 0;
+                do {
+                    if ( overflowIndex > maxOverflowIndex )  {
+                        diag.error("chain overflow index out of range %d (max=%d) in segment %s", overflowIndex, maxOverflowIndex, segmentName(i));
+                        return false;
+                    }
+                    offsetInPage = (segInfo->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST);
+                    if ( offsetInPage > segInfo->page_size ) {
+                        diag.error("chained fixups, in segment #%d overflow page_start[%d]=0x%04X exceeds page size", i, overflowIndex, offsetInPage);
+                        return false;
+                    }
+                    if ( (offsetInPage <= lastOffsetInPage) && (lastOffsetInPage != 0) )  {
+                        diag.error("chained fixups, in segment #%d overflow page_start[%d]=0x%04X is before previous at 0x%04X\n", i, overflowIndex, offsetInPage, lastOffsetInPage);
+                        return false;
+                    }
+                    lastOffsetInPage = offsetInPage;
+                    ++overflowIndex;
+                } while ( (segInfo->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST) == 0 );
+           }
+        }
+
+    }
+    // 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;
+        if ( maxValidPointerSeen < lastSegmentLastVMAddr ) {
+            diag.error("chained fixups, max_valid_pointer too small for image");
+            return false;
+        }
+    }
+
+    return diag.noError();
+}
+
+bool MachOAnalyzer::validChainedFixupsInfoOldArm64e(Diagnostics& diag, const char* path) const
 {
     __block uint32_t maxTargetCount = 0;
     __block uint32_t currentTargetCount = 0;
@@ -2049,7 +2245,7 @@ void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback
         switch (header->imports_format) {
             case DYLD_CHAINED_IMPORT:
                 imports = (dyld_chained_import*)((uint8_t*)header + header->imports_offset);
-                for (uint32_t i=0; i < header->imports_count; ++i) {
+                for (uint32_t i=0; i < header->imports_count && !stop; ++i) {
                     const char* symbolName = &symbolsPool[imports[i].name_offset];
                     if ( imports[i].name_offset > maxSymbolOffset ) {
                         diag.error("malformed import table, string overflow");
@@ -2065,7 +2261,7 @@ void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback
                 break;
             case DYLD_CHAINED_IMPORT_ADDEND:
                 importsA32 = (dyld_chained_import_addend*)((uint8_t*)header + header->imports_offset);
-                for (uint32_t i=0; i < header->imports_count; ++i) {
+                for (uint32_t i=0; i < header->imports_count && !stop; ++i) {
                     const char* symbolName = &symbolsPool[importsA32[i].name_offset];
                     if ( importsA32[i].name_offset > maxSymbolOffset ) {
                         diag.error("malformed import table, string overflow");
@@ -2076,12 +2272,12 @@ void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback
                         libOrdinal = (int8_t)libVal;
                     else
                         libOrdinal = libVal;
-                   callback(libOrdinal, symbolName, importsA32[i].addend, importsA32[i].weak_import, stop);
+                    callback(libOrdinal, symbolName, importsA32[i].addend, importsA32[i].weak_import, stop);
                 }
                 break;
             case DYLD_CHAINED_IMPORT_ADDEND64:
                 importsA64 = (dyld_chained_import_addend64*)((uint8_t*)header + header->imports_offset);
-                for (uint32_t i=0; i < header->imports_count; ++i) {
+                for (uint32_t i=0; i < header->imports_count && !stop; ++i) {
                     const char* symbolName = &symbolsPool[importsA64[i].name_offset];
                     if ( importsA64[i].name_offset > maxSymbolOffset ) {
                         diag.error("malformed import table, string overflow");
@@ -2146,6 +2342,37 @@ bool MachOAnalyzer::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const
     return true;
 }
 
+bool MachOAnalyzer::hasProgramVars(Diagnostics& diag, uint32_t& progVarsOffset) const
+{
+    if ( this->filetype != MH_EXECUTE )
+        return false;
+
+    // macOS 10.8+              program uses LC_MAIN and ProgramVars are in libdyld.dylib
+    // macOS 10.6 -> 10.7       ProgramVars are in __program_vars section in main executable
+    // 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;
+    bool     usesCRT;
+    if ( getEntry(offset, usesCRT) && usesCRT ) {
+        // is pre-10.8 program
+        uint64_t sectionSize;
+        if ( const void* progVarsSection = findSectionContent("__DATA", "__program_vars", sectionSize) ) {
+            progVarsOffset = (uint32_t)((uint8_t*)progVarsSection - (uint8_t*)this);
+            return true;
+        }
+        else if ( const void* dyldSection = findSectionContent("__DATA", "__dyld", sectionSize) ) {
+            if ( sectionSize >= 7*pointerSize() ) {
+                progVarsOffset = (uint32_t)((uint8_t*)dyldSection - (uint8_t*)this) + 2*pointerSize();
+                return true;
+            }
+        }
+        diag.error("pre-macOS 10.5 binaries not supported");
+        return true;
+    }
+    return false;
+}
+
 bool MachOAnalyzer::hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache) const
 {
     __block bool result = false;
@@ -2338,8 +2565,8 @@ void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, v
         const uint32_t* initsEnd   = (uint32_t*)((uint8_t*)content + info.sectSize);
         for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
             uint32_t anInitOffset = *p;
-            if ( anInitOffset > executableSegments.segments[0].fileSize ) {
-                 diag.error("initializer 0x%0X is not an offset within __TEXT segment", anInitOffset);
+            if ( !executableSegments.contains(loadAddress + anInitOffset) ) {
+                 diag.error("initializer 0x%08X does not an offset to an executable segment", anInitOffset);
                  stop = true;
                  break;
             }
@@ -2963,6 +3190,9 @@ bool MachOAnalyzer::canBePlacedInDyldCache(const char* path, void (^failureReaso
     if ( !(isArch("x86_64") || isArch("x86_64h")) )
         return true;
 
+    if ( hasChainedFixups() )
+        return true;
+
     __block bool rebasesOk = true;
     Diagnostics diag;
     uint64_t startVMAddr = preferredLoadAddress();
@@ -2991,37 +3221,52 @@ bool MachOAnalyzer::canBePlacedInDyldCache(const char* path, void (^failureReaso
 }
 
 uint64_t MachOAnalyzer::chainStartsOffset() const
+{
+    const dyld_chained_fixups_header* header = chainedFixupsHeader();
+    // old arm64e binary has no dyld_chained_fixups_header
+    if ( header == nullptr )
+        return 0;
+    return header->starts_offset + ((uint8_t*)header - (uint8_t*)this);
+}
+
+const dyld_chained_fixups_header* MachOAnalyzer::chainedFixupsHeader() const
 {
     Diagnostics diag;
     LinkEditInfo leInfo;
     getLinkEditPointers(diag, leInfo);
     if ( diag.hasError() || (leInfo.chainedFixups == nullptr) )
-        return 0;
+        return nullptr;
 
-    const dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)getLinkEditContent(leInfo.layout, leInfo.chainedFixups->dataoff);
-    return header->starts_offset + ((uint8_t*)header - (uint8_t*)this);
+    return (dyld_chained_fixups_header*)getLinkEditContent(leInfo.layout, leInfo.chainedFixups->dataoff);
+}
+
+uint16_t MachOAnalyzer::chainedPointerFormat(const dyld_chained_fixups_header* header)
+{
+    const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)header + header->starts_offset);
+    for (uint32_t i=0; i < startsInfo->seg_count; ++i) {
+        uint32_t segInfoOffset = startsInfo->seg_info_offset[i];
+        // 0 offset means this segment has no fixups
+        if ( segInfoOffset == 0 )
+            continue;
+        const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)startsInfo + segInfoOffset);
+        if ( segInfo->page_count != 0 )
+            return segInfo->pointer_format;
+    }
+    return 0;  // no chains (perhaps no __DATA segment)
 }
 
 uint16_t MachOAnalyzer::chainedPointerFormat() const
 {
-    uint64_t infoOffset = chainStartsOffset();
-    if ( infoOffset != 0 ) {
+    const dyld_chained_fixups_header* header = chainedFixupsHeader();
+    if ( header != nullptr ) {
         // get pointer format from chain info struct in LINKEDIT
-        const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)this + infoOffset);
-        for (uint32_t i=0; i < startsInfo->seg_count; ++i) {
-            uint32_t segInfoOffset = startsInfo->seg_info_offset[i];
-            // 0 offset means this segment has no fixups
-            if ( segInfoOffset == 0 )
-                continue;
-            const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)startsInfo + segInfoOffset);
-            if ( segInfo->page_count != 0 )
-                return segInfo->pointer_format;
-        }
+        return chainedPointerFormat(header);
     }
     assert(this->cputype == CPU_TYPE_ARM64 && this->cpusubtype == 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
@@ -3175,6 +3420,10 @@ uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadO
             return convertToVMAddr(classData->nameVMAddr, vmAddrConverter);
         case ObjCClassInfo::ReadOnlyDataField::baseMethods:
             return convertToVMAddr(classData->baseMethodsVMAddr, vmAddrConverter);
+        case ObjCClassInfo::ReadOnlyDataField::baseProperties:
+            return convertToVMAddr(classData->basePropertiesVMAddr, vmAddrConverter);
+        case ObjCClassInfo::ReadOnlyDataField::flags:
+            return classData->flags;
         }
     } else {
         typedef uint32_t PtrTy;
@@ -3202,6 +3451,10 @@ uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadO
                 return convertToVMAddr(classData->nameVMAddr, vmAddrConverter);
             case ObjCClassInfo::ReadOnlyDataField::baseMethods:
                 return convertToVMAddr(classData->baseMethodsVMAddr, vmAddrConverter);
+            case ObjCClassInfo::ReadOnlyDataField::baseProperties:
+                return convertToVMAddr(classData->basePropertiesVMAddr, vmAddrConverter);
+            case ObjCClassInfo::ReadOnlyDataField::flags:
+                return classData->flags;
         }
     }
 }
@@ -3257,8 +3510,8 @@ const char* MachOAnalyzer::getPrintableString(uint64_t stringVMAddr, MachOAnalyz
             return;
         }
 
-        // We can't scan this section if its protected or not cstrings.
-        if ( sectInfo.segInfo.isProtected || ( (sectInfo.sectFlags & SECTION_TYPE) != S_CSTRING_LITERALS ) ) {
+        // We can't scan this section if its protected
+        if ( sectInfo.segInfo.isProtected ) {
             result = PrintableStringResult::ProtectedSection;
             stop = true;
             return;
@@ -3292,6 +3545,15 @@ const char* MachOAnalyzer::getPrintableString(uint64_t stringVMAddr, MachOAnalyz
         stop = true;
     });
 
+#if BUILDING_SHARED_CACHE_UTIL
+    // The shared cache coalesces strings in to their own section.
+    // Assume its a valid pointer
+    if (result == PrintableStringResult::UnknownSection) {
+        result = PrintableStringResult::CanPrint;
+        return (const char*)(stringVMAddr + getSlide());
+    }
+#endif
+
     if (result == PrintableStringResult::CanPrint)
         return (const char*)(stringVMAddr + getSlide());
     return nullptr;
@@ -3448,7 +3710,7 @@ void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, bool contentRebased,
                 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, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcMetaClass, true);
+                handler(diag, objcClass.isaVMAddr, classSuperclassVMAddr, classDataVMAddr, objcMetaClass, true);
                 if (diag.hasError())
                     return;
             }
@@ -3503,7 +3765,7 @@ void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, bool contentRebased,
                 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, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcMetaClass, true);
+                handler(diag, objcClass.isaVMAddr, classSuperclassVMAddr, classDataVMAddr, objcMetaClass, true);
                 if (diag.hasError())
                     return;
             }
@@ -3772,6 +4034,79 @@ void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentReb
     }
 }
 
+void MachOAnalyzer::forEachObjCProperty(uint64_t propertyListVMAddr, bool contentRebased,
+                                        void (^handler)(uint64_t propertyVMAddr, const ObjCProperty& property)) const {
+    if ( propertyListVMAddr == 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 property_list_t {
+            uint32_t    entsize;
+            uint32_t    count;
+            PtrTy       propertyArrayBase; // Note this is the start the array property_t[0]
+
+            uint32_t getEntsize() const {
+                return (entsize) & ~(uint32_t)3;
+            }
+        };
+
+        struct property_t {
+            PtrTy nameVMAddr;   // SEL
+            PtrTy attributesVMAddr;  // const char *
+        };
+
+        const property_list_t* propertyList = (const property_list_t*)(propertyListVMAddr + slide);
+        uint64_t propertyListArrayBaseVMAddr = propertyListVMAddr + offsetof(property_list_t, propertyArrayBase);
+        for (unsigned i = 0; i != propertyList->count; ++i) {
+            uint64_t propertyEntryOffset = i * propertyList->getEntsize();
+            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);
+            handler(propertyVMAddr, property);
+        }
+    } else {
+        typedef uint32_t PtrTy;
+        struct property_list_t {
+            uint32_t    entsize;
+            uint32_t    count;
+            PtrTy       propertyArrayBase; // Note this is the start the array property_t[0]
+
+            uint32_t getEntsize() const {
+                return (entsize) & ~(uint32_t)3;
+            }
+        };
+
+        struct property_t {
+            PtrTy nameVMAddr;   // SEL
+            PtrTy attributesVMAddr;  // const char *
+        };
+
+        const property_list_t* propertyList = (const property_list_t*)(propertyListVMAddr + slide);
+        uint64_t propertyListArrayBaseVMAddr = propertyListVMAddr + offsetof(property_list_t, propertyArrayBase);
+        for (unsigned i = 0; i != propertyList->count; ++i) {
+            uint64_t propertyEntryOffset = i * propertyList->getEntsize();
+            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);
+            handler(propertyVMAddr, property);
+        }
+    }
+}
+
 
 void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics& diag, bool contentRebased,
                                                  void (^handler)(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr)) const {
index 757ab6eb70c84057239d582fa0dd2f69d99168fe..62a322539328398470a9f7501855172045803c8b 100644 (file)
@@ -59,8 +59,7 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
     uint64_t            preferredLoadAddress() const;
     void                forEachLocalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const;
     void                forEachRPath(void (^callback)(const char* rPath, bool& stop)) const;
-
-    bool                isEncrypted() const;
+    bool                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;
@@ -115,7 +114,9 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
     void                withChainStarts(Diagnostics& diag, uint64_t startsStructOffsetHint, void (^callback)(const dyld_chained_starts_in_image*)) const;
     uint64_t            chainStartsOffset() const;
     uint16_t            chainedPointerFormat() const;
+    static uint16_t     chainedPointerFormat(const dyld_chained_fixups_header* chainHeader);
     bool                hasUnalignedPointerFixups() const;
+    const dyld_chained_fixups_header* chainedFixupsHeader() const;
 
     const MachOAnalyzer*    remapIfZeroFill(Diagnostics& diag, const closure::FileSystem& fileSystem, closure::LoadedFileInfo& info) const;
 
@@ -170,7 +171,9 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
         // These are from the class_ro_t which data points to
         enum class ReadOnlyDataField {
             name,
-            baseMethods
+            baseMethods,
+            baseProperties,
+            flags
         };
 
         uint64_t getReadOnlyDataField(ReadOnlyDataField field, uint32_t pointerSize) const;
@@ -180,6 +183,12 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
         uint64_t baseMethodsVMAddr(uint32_t pointerSize) const {
             return getReadOnlyDataField(ReadOnlyDataField::baseMethods, pointerSize);
         }
+        uint64_t basePropertiesVMAddr(uint32_t pointerSize) const {
+            return getReadOnlyDataField(ReadOnlyDataField::baseProperties, pointerSize);
+        }
+        uint64_t flags(uint32_t pointerSize) const {
+            return getReadOnlyDataField(ReadOnlyDataField::flags, pointerSize);
+        }
 
         // These are embedded in the Mach-O itself by the compiler
         enum FastDataBits {
@@ -225,6 +234,11 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
         uint64_t nameLocationVMAddr;
     };
 
+    struct ObjCProperty {
+        uint64_t nameVMAddr;        // & const char *
+        uint64_t attributesVMAddr;  // & const char *
+    };
+
     struct ObjCCategory {
         uint64_t nameVMAddr;
         uint64_t clsVMAddr;
@@ -284,6 +298,9 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
     void forEachObjCMethod(uint64_t methodListVMAddr, bool contentRebased,
                            void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method)) const;
 
+    void forEachObjCProperty(uint64_t propertyListVMAddr, bool contentRebased,
+                             void (^handler)(uint64_t propertyVMAddr, const ObjCProperty& property)) const;
+
     void forEachObjCSelectorReference(Diagnostics& diag, bool contentRebased,
                                       void (^handler)(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr)) const;
 
@@ -320,6 +337,7 @@ private:
     bool                    validBindInfo(Diagnostics& diag, const char* path) const;
     bool                    validMain(Diagnostics& diag, const char* path) const;
     bool                    validChainedFixupsInfo(Diagnostics& diag, const char* path) const;
+    bool                    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;
index ce8b8f678010bd728e4cd941977638b1ff2c78f0..7c26440518763475e9619bd849c07f5bd1a40613 100644 (file)
@@ -430,6 +430,12 @@ bool MachOFile::supportsPlatform(Platform reqPlatform) const
             return true;
     }
 
+#if BUILDING_DYLDINFO
+    // Allow offline tools to analyze binaries dyld doesn't load, ie, those with platforms
+    if ( !foundOtherPlatform && (reqPlatform == Platform::unknown) )
+        return true;
+#endif
+
     return false;
 }
 
index 74c845c7ec79151e2d216e9f2598c90b5fc2fefd..35c61ac2046a64983ec74a8b814a5a5ab728733d 100644 (file)
@@ -583,6 +583,15 @@ bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, u
         return false;
     uint64_t targetUnslidAddress = address - leInfo.layout.slide;
 
+    // find section index the address is in to validate n_sect
+    __block uint32_t sectionIndexForTargetAddress = 0;
+    forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+        ++sectionIndexForTargetAddress;
+        if ( (sectInfo.sectAddr <= targetUnslidAddress) && (targetUnslidAddress < sectInfo.sectAddr+sectInfo.sectSize) ) {
+            stop = true;
+        }
+    });
+
     uint32_t               maxStringOffset  = leInfo.symTab->strsize;
     const char*            stringPool       =             (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff);
     const struct nlist*    symbols          = (struct nlist*)   (getLinkEditContent(leInfo.layout, leInfo.symTab->symoff));
@@ -595,10 +604,10 @@ bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, u
         for (const struct nlist_64* s = globalsStart; s < globalsEnd; ++s) {
             if ( (s->n_type & N_TYPE) == N_SECT ) {
                 if ( bestSymbol == nullptr ) {
-                    if ( s->n_value <= targetUnslidAddress )
+                    if ( (s->n_value <= targetUnslidAddress) && (s->n_sect == sectionIndexForTargetAddress) )
                         bestSymbol = s;
                 }
-                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) && (s->n_sect == sectionIndexForTargetAddress) ) {
                     bestSymbol = s;
                 }
             }
@@ -609,10 +618,10 @@ bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, u
         for (const struct nlist_64* s = localsStart; s < localsEnd; ++s) {
              if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
                 if ( bestSymbol == nullptr ) {
-                    if ( s->n_value <= targetUnslidAddress )
+                    if ( (s->n_value <= targetUnslidAddress) && (s->n_sect == sectionIndexForTargetAddress) )
                         bestSymbol = s;
                 }
-                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) && (s->n_sect == sectionIndexForTargetAddress) ) {
                     bestSymbol = s;
                 }
             }
@@ -632,10 +641,10 @@ bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, u
         for (const struct nlist* s = globalsStart; s < globalsEnd; ++s) {
             if ( (s->n_type & N_TYPE) == N_SECT ) {
                 if ( bestSymbol == nullptr ) {
-                    if ( s->n_value <= targetUnslidAddress )
+                    if ( (s->n_value <= targetUnslidAddress) && (s->n_sect == sectionIndexForTargetAddress) )
                         bestSymbol = s;
                 }
-                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) && (s->n_sect == sectionIndexForTargetAddress) ) {
                     bestSymbol = s;
                 }
             }
@@ -646,10 +655,10 @@ bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, u
         for (const struct nlist* s = localsStart; s < localsEnd; ++s) {
              if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
                 if ( bestSymbol == nullptr ) {
-                    if ( s->n_value <= targetUnslidAddress )
+                    if ( (s->n_value <= targetUnslidAddress) && (s->n_sect == sectionIndexForTargetAddress) )
                         bestSymbol = s;
                 }
-                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) && (s->n_sect == sectionIndexForTargetAddress) ) {
                     bestSymbol = s;
                 }
             }
@@ -1026,12 +1035,31 @@ bool MachOLoaded::ChainedFixupPointerOnDisk::isRebase(uint16_t pointerFormat, ui
                 return true;
             }
             break;
+       case DYLD_CHAINED_PTR_ARM64E_OFFSET:
+       case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+            if ( this->arm64e.bind.bind )
+                return false;
+            if ( this->arm64e.authRebase.auth ) {
+                targetRuntimeOffset = this->arm64e.authRebase.target;
+                return true;
+            }
+            else {
+                targetRuntimeOffset = this->arm64e.unpackTarget();
+                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();
+            return true;
+            break;
         case DYLD_CHAINED_PTR_32:
             if ( this->generic32.bind.bind )
                 return false;
@@ -1048,6 +1076,8 @@ bool MachOLoaded::ChainedFixupPointerOnDisk::isBind(uint16_t pointerFormat, uint
 {
     switch (pointerFormat) {
         case DYLD_CHAINED_PTR_ARM64E:
+        case DYLD_CHAINED_PTR_ARM64E_OFFSET:
+        case DYLD_CHAINED_PTR_ARM64E_USERLAND:
             if ( !this->arm64e.authBind.bind )
                 return false;
             if ( this->arm64e.authBind.auth ) {
@@ -1060,6 +1090,7 @@ bool MachOLoaded::ChainedFixupPointerOnDisk::isBind(uint16_t pointerFormat, uint
             }
             break;
         case DYLD_CHAINED_PTR_64:
+        case DYLD_CHAINED_PTR_64_OFFSET:
             if ( !this->generic64.bind.bind )
                 return false;
             bindOrdinal = this->generic64.bind.ordinal;
@@ -1127,6 +1158,48 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st
                     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 ) {
@@ -1146,6 +1219,24 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st
                     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() ) {
+                        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*)((uintptr_t)this + fixupLoc->generic64.unpackedTarget());
+                }
+                if ( logFixup )
+                    logFixup(fixupLoc, newValue);
+                fixupLoc->raw64 = (uintptr_t)newValue;
+               break;
 #else
             case DYLD_CHAINED_PTR_32:
                 if ( fixupLoc->generic32.bind.bind ) {
@@ -1196,12 +1287,15 @@ bool MachOLoaded::walkChain(Diagnostics& diag, const dyld_chained_starts_in_segm
         if ( !stop ) {
             switch (segInfo->pointer_format) {
                 case DYLD_CHAINED_PTR_ARM64E:
+                case DYLD_CHAINED_PTR_ARM64E_USERLAND:
                     if ( chainContent.arm64e.rebase.next == 0 )
                         chainEnd = true;
                     else
                         chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.arm64e.rebase.next*8);
                     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
index 45770aa4d9220033408668a24009bde6bb1bf864..e23f890aee922bb023f9fe11b3d470d857af5e92 100644 (file)
@@ -30,7 +30,7 @@
 #include "MachOFile.h"
 
 
-class CacheBuilder;
+class SharedCacheBuilder;
 
 namespace dyld3 {
 
@@ -156,7 +156,7 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile
     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;
 protected:
-    friend CacheBuilder;
+    friend SharedCacheBuilder;
 
     struct FoundSymbol {
         enum class Kind { headerOffset, absolute, resolverOffset };
@@ -169,10 +169,6 @@ protected:
         const char*         foundSymbolName;
     };
 
-
-protected:
-    friend CacheBuilder;
-
     bool                    findExportedSymbol(Diagnostics& diag, const char* symbolName, bool weakImport, FoundSymbol& foundInfo, DependentToMachOLoaded finder) const;
 
     void                    getLinkEditLoadCommands(Diagnostics& diag, LinkEditInfo& result) const;
index 0c1a14a3eb067c7cfc87d21b85778c1309501123..3adaf482b1bae0b04189ed1c599b7037139b55e1 100644 (file)
@@ -307,7 +307,7 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa
         ::close(fd);
         return false;
     }
-    if ( (cache->header.mappingCount != 3) || (cache->header.mappingOffset > 0x138) ) {
+    if ( (cache->header.mappingCount != 3) || (cache->header.mappingOffset > 0x148) ) {
         results->errorMessage = "shared cache file mappings are invalid";
         ::close(fd);
         return false;
index 8879971949b2bf052559d930a20666028f17bd01..a35ffdd646f0b2f0117f8f6c47201d6ffa8ae400 100644 (file)
@@ -147,6 +147,12 @@ static void entry_setHasCacheOverrides(bool someCacheImageOverriden)
     gAllImages.setHasCacheOverrides(someCacheImageOverriden);
 }
 
+
+static void entry_setProgramVars(ProgramVars* progVars)
+{
+    gAllImages.setProgramVars((AllImages::ProgramVars*)progVars);
+}
+
 static_assert((closure::kFormatVersion & LibDyldEntryVector::kBinaryFormatVersionMask) == closure::kFormatVersion, "binary format version overflow");
 
 const LibDyldEntryVector entryVectorForDyld = {
@@ -163,7 +169,8 @@ const LibDyldEntryVector entryVectorForDyld = {
     &entry_setRestrictions,
     &entry_setNotifyMonitoringDyldMain,
     &entry_setNotifyMonitoringDyld,
-    &entry_setHasCacheOverrides
+    &entry_setHasCacheOverrides,
+    &entry_setProgramVars,
 };
 
 VIS_HIDDEN void _dyld_atfork_prepare()
index bcef2fc9ccbf04010b3380983f3484f71a100618..d6a8529273fc3e242341a723bcccd70f373b8fea 100644 (file)
 
 struct dyld_all_image_infos;
 class DyldSharedCache;
+struct ProgramVars;
 
 namespace dyld3 {
 
 
 struct LibDyldEntryVector
 {
-    enum { kCurrentVectorVersion = 7 };
+    enum { kCurrentVectorVersion = 8 };
     // 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 };
 
@@ -66,6 +67,9 @@ struct LibDyldEntryVector
                                                                             const char* imagePaths[]));
     // added in version 7
     void        (*setHasCacheOverrides)(bool someCacheImageOverriden);
+
+    // added in version 8
+    void        (*setProgramVars)(struct ProgramVars* progVars);
 };
 
 extern const LibDyldEntryVector entryVectorForDyld;
index c01f2c9b13a2bcec8bf525f3e74233c2577cfb98..22f39d8f563b33b50150e79631a2353316d8abbc 100644 (file)
@@ -43,6 +43,7 @@
 #include "MachOFileAbstraction.hpp"
 #include "MachOLoaded.h"
 #include "MachOAnalyzer.h"
+#include "mach-o/fixup-chains.h"
 
 
 #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
@@ -69,6 +70,7 @@ private:
                                     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();
@@ -78,6 +80,9 @@ private:
     void            adjustInstruction(uint8_t kind, uint8_t* textLoc, uint64_t codeToDataDelta);
     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);
+
 
     typedef typename P::uint_t pint_t;
     typedef typename P::E E;
@@ -98,6 +103,7 @@ private:
     macho_linkedit_data_command<P>*                         _dataInCodeCmd      = nullptr;
     macho_linkedit_data_command<P>*                         _exportTrieCmd      = nullptr;
     macho_linkedit_data_command<P>*                         _chainedFixupsCmd   = nullptr;
+    uint16_t                                                _chainedFixupsFormat = 0;
     std::vector<uint64_t>                                   _segOrigStartAddresses;
     std::vector<uint64_t>                                   _segSlides;
     std::vector<macho_segment_command<P>*>                  _segCmds;
@@ -140,6 +146,7 @@ Adjustor<P>::Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const s
                 break;
             case LC_DYLD_CHAINED_FIXUPS:
                 _chainedFixupsCmd = (macho_linkedit_data_command<P>*)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;
@@ -178,6 +185,13 @@ void Adjustor<P>::adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker&
         return;
     if ( _splitSegInfoV2 ) {
         adjustReferencesUsingInfoV2(aslrTracker, lohTracker, coalescedText, textCoalescer);
+        adjustChainedFixups();
+    }
+    else if ( _chainedFixupsCmd != nullptr ) {
+        // need to adjust the chain fixup segment_offset fields in LINKEDIT before chains can be walked
+        adjustChainedFixups();
+        adjustRebaseChains(aslrTracker);
+        adjustCode();
     }
     else {
         adjustDataPointers(aslrTracker);
@@ -188,7 +202,6 @@ void Adjustor<P>::adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker&
     adjustSymbolTable();
     if ( _diagnostics.hasError() )
         return;
-    adjustChainedFixups();
     if ( _diagnostics.hasError() )
         return;
     rebuildLinkEditAndLoadCommands(textCoalescer);
@@ -539,6 +552,80 @@ static uint32_t setArmWord(uint32_t instruction, uint16_t word) {
     return (instruction & 0xFFF0F000) | (imm4 << 16) | imm12;
 }
 
+
+template <typename P>
+void Adjustor<P>::convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide)
+{
+    assert(chainPtr->arm64e.authRebase.bind == 0);
+    dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgPtr = *chainPtr;
+    dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp;
+    if ( chainPtr->arm64e.authRebase.auth ) {
+        uint64_t targetVMAddr = orgPtr.arm64e.authRebase.target + _segOrigStartAddresses[0] + targetSlide;
+        // 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 ) {
+            // everything fits, just update target
+            chainPtr->arm64e.authRebase.target = targetVMAddr;
+            return;
+        }
+        // see if it fits in a plain rebase
+        tmp.arm64e.rebase.target = targetVMAddr;
+        if ( tmp.arm64e.rebase.target == targetVMAddr ) {
+            // does fit in plain rebase, so convert to that and store auth data in side table
+            aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key);
+            chainPtr->arm64e.rebase.target = targetVMAddr;
+            chainPtr->arm64e.rebase.high8  = 0;
+            chainPtr->arm64e.rebase.next   = orgPtr.arm64e.rebase.next;
+            chainPtr->arm64e.rebase.bind   = 0;
+            chainPtr->arm64e.rebase.auth   = 0;
+            return;
+        }
+        // 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.high8  = 0;
+        chainPtr->arm64e.rebase.next   = orgPtr.arm64e.rebase.next;
+        chainPtr->arm64e.rebase.bind   = 0;
+        chainPtr->arm64e.rebase.auth   = 0;
+        return;
+    }
+    else {
+        uint64_t targetVMAddr = orgPtr.arm64e.rebase.target + targetSlide;
+        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
+    }
+}
+
+
+template <typename P>
+void Adjustor<P>::convertGeneric64RebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide)
+{
+    dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgPtr = *chainPtr;
+    dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp;
+
+    uint64_t targetVMAddr = orgPtr.generic64.rebase.target + targetSlide;
+    // 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 ) {
+        // everything fits, just update target
+        chainPtr->generic64.rebase.target = targetVMAddr;
+        return;
+    }
+
+    // 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
+}
+
+
 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,
@@ -550,7 +637,7 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
     uint32_t value32;
     uint32_t* mappedAddr32 = 0;
     uint32_t instruction;
-    dyld3::MachOLoaded::ChainedFixupPointerOnDisk chainPtr;
+    dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr;
     int64_t offsetAdjust;
     int64_t delta;
     switch ( kind ) {
@@ -567,37 +654,84 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
             break;
         case DYLD_CACHE_ADJ_V2_POINTER_32:
             mappedAddr32 = (uint32_t*)mappedAddr;
-            if ( toNewAddress != (uint64_t)(E::get32(*mappedAddr32) + targetSlide) ) {
-                _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX in %s", fromNewAddress, _installName);
-                return;
+            if ( _chainedFixupsCmd != nullptr ) {
+                chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr32;
+                switch (_chainedFixupsFormat) {
+                    case DYLD_CHAINED_PTR_32:
+                        // ignore binds, fix up rebases to have new targets
+                        if ( chainPtr->generic32.rebase.bind == 0 ) {
+                            // there is not enough space in 32-bit pointer to store new vmaddr in cache in 26-bit target
+                            // so store target in side table that will be applied when binds are resolved
+                            aslrTracker.add(mappedAddr32);
+                            uint32_t target = (uint32_t)(chainPtr->generic32.rebase.target + targetSlide);
+                            aslrTracker.setRebaseTarget32(chainPtr, target);
+                            chainPtr->generic32.rebase.target = CacheBuilder::kRebaseTargetInSideTableGeneric32;
+                        }
+                        break;
+                    default:
+                        _diagnostics.error("unknown 32-bit chained fixup format %d in %s", _chainedFixupsFormat, _installName);
+                        break;
+                }
+            }
+            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);
+                    return;
+                }
+                E::set32(*mappedAddr32, (uint32_t)toNewAddress);
+                aslrTracker.add(mappedAddr32);
             }
-            E::set32(*mappedAddr32, (uint32_t)toNewAddress);
-            aslrTracker.add(mappedAddr32);
             break;
         case DYLD_CACHE_ADJ_V2_POINTER_64:
             mappedAddr64 = (uint64_t*)mappedAddr;
-            if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) {
-                _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _installName);
-                return;
+            if ( _chainedFixupsCmd != nullptr ) {
+                chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr64;
+                switch (_chainedFixupsFormat) {
+                    case DYLD_CHAINED_PTR_ARM64E:
+                        // ignore binds and adjust rebases to new segment locations
+                        if ( chainPtr->arm64e.authRebase.bind == 0 ) {
+                            convertArm64eRebaseToIntermediate(chainPtr, aslrTracker, targetSlide);
+                            // 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:
+                        // ignore binds and adjust rebases to new segment locations
+                        if ( chainPtr->generic64.rebase.bind == 0 ) {
+                            convertGeneric64RebaseToIntermediate(chainPtr, aslrTracker, targetSlide);
+                            // 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_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);
+                        break;
+                }
+            }
+            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);
+                    return;
+                }
+                E::set64(*mappedAddr64, toNewAddress);
+                aslrTracker.add(mappedAddr64);
+                uint8_t high8 = toNewAddress >> 56;
+                if ( high8 )
+                    aslrTracker.setHigh8(mappedAddr64, high8);
             }
-            E::set64(*mappedAddr64, toNewAddress);
-            aslrTracker.add(mappedAddr64);
             break;
         case DYLD_CACHE_ADJ_V2_THREADED_POINTER_64:
-            mappedAddr64 = (uint64_t*)mappedAddr;
-            chainPtr.raw64 = E::get64(*mappedAddr64);
-            // ignore binds, fix up rebases to have new targets
-            if ( chainPtr.arm64e.authRebase.bind == 0 ) {
-                if ( chainPtr.arm64e.authRebase.auth ) {
-                    // auth pointer target is offset in dyld cache
-                    chainPtr.arm64e.authRebase.target += (((dyld3::MachOAnalyzer*)_mh)->preferredLoadAddress() + targetSlide - _cacheBuffer->header.sharedRegionStart);
-                }
-                else {
-                    // plain pointer target is unslid address of target
-                    chainPtr.arm64e.rebase.target += targetSlide;
-                }
+            // old style arm64e binary
+            chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr;
+            // ignore binds, they are proccessed later
+            if ( chainPtr->arm64e.authRebase.bind == 0 ) {
+                convertArm64eRebaseToIntermediate(chainPtr, aslrTracker, targetSlide);
                 // Note, the pointer remains a chain with just the target of the rebase adjusted to the new target location
-                E::set64(*mappedAddr64, chainPtr.raw64);
+                aslrTracker.add(chainPtr);
             }
             break;
        case DYLD_CACHE_ADJ_V2_DELTA_64:
@@ -919,7 +1053,7 @@ 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, deltaAdjust, toSectionSlide);
+                    //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;
@@ -959,6 +1093,36 @@ 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_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)chainHeader + chainHeader->starts_offset);
+    ma->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:
+                    // only look at rebases
+                    if ( fixupLoc->generic64.rebase.bind == 0 ) {
+                        uint64_t rebaseTargetInDylib = fixupLoc->generic64.rebase.target;
+                        uint64_t rebaseTargetInDyldcache = fixupLoc->generic64.rebase.target + slideForOrigAddress(rebaseTargetInDylib);
+                        convertGeneric64RebaseToIntermediate(fixupLoc, aslrTracker, rebaseTargetInDyldcache);
+                        aslrTracker.add(fixupLoc);
+                    }
+                    break;
+                case DYLD_CHAINED_PTR_64_OFFSET:
+                    _diagnostics.error("unhandled 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _installName);
+                    break;
+                default:
+                    _diagnostics.error("unsupported chained fixup format %d", segInfo->pointer_format);
+                    stop = true;
+            }
+    });
+}
+
+
 template <typename P>
 void Adjustor<P>::adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker)
 {
@@ -1231,7 +1395,7 @@ void Adjustor<P>::adjustExportsTrie(std::vector<uint8_t>& newTrieBytes)
 void CacheBuilder::adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const
 {
     DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    if ( _archLayout->is64 ) {
+    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);
     }
diff --git a/dyld3/shared-cache/BuilderUtils.h b/dyld3/shared-cache/BuilderUtils.h
deleted file mode 100644 (file)
index dc2c5e3..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2017 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#ifndef BuilderUtils_h
-#define BuilderUtils_h
-
-dispatch_group_t buildGroup();
-void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot);
-bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose, bool skipWrites, bool agileChooseSHA256CdHash,
-           bool emitDevCaches, bool isLocallyBuiltCache);
-
-#endif /* BuilderUtils_h */
diff --git a/dyld3/shared-cache/BuilderUtils.mm b/dyld3/shared-cache/BuilderUtils.mm
deleted file mode 100644 (file)
index 7d76766..0000000
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (c) 2017 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#include <set>
-#include <array>
-#include <string>
-#include <sstream>
-#include <iomanip> // std::setfill, std::setw
-#include <pthread.h>
-#include <mach/mach.h>
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <dispatch/dispatch.h>
-
-#include <Bom/Bom.h>
-#include <Security/Security.h>
-#include <Security/SecCodeSigner.h>
-#include <CommonCrypto/CommonCrypto.h>
-
-#include "Manifest.h"
-#include "Diagnostics.h"
-#include "FileUtils.h"
-
-#include "BuilderUtils.h"
-
-static dispatch_queue_t write_queue = dispatch_queue_create("com.apple.dyld.cache-builder.write", DISPATCH_QUEUE_CONCURRENT);
-static dispatch_group_t build_group = dispatch_group_create();
-
-dispatch_group_t buildGroup() {
-    return build_group;
-}
-
-void insertFileInBom(const std::string& path, BOMBom bom)
-{
-    std::vector<std::string> components;
-    std::vector<std::string> processed_components;
-    std::stringstream ss(path);
-    std::string item;
-
-    while (std::getline(ss, item, '/')) {
-        if (!item.empty()) {
-            components.push_back(item);
-        }
-    }
-
-    std::string partialPath = ".";
-    std::string lastComponent = components.back();
-    components.pop_back();
-    BOMFSObject fso = BOMFSObjectNew(BOMDirectoryType);
-    BOMFSObjectSetFlags(fso, B_PATHONLY);
-    BOMFSObjectSetPathName(fso, ".", true);
-    BOMFSObjectSetShortName(fso, ".", true);
-    (void)BOMBomInsertFSObject(bom, fso, false);
-    BOMFSObjectFree(fso);
-
-    for (const auto& component : components) {
-        partialPath = partialPath + "/" + component;
-        fso = BOMFSObjectNew(BOMDirectoryType);
-        BOMFSObjectSetFlags(fso, B_PATHONLY);
-        BOMFSObjectSetPathName(fso, partialPath.c_str(), true);
-        BOMFSObjectSetShortName(fso, component.c_str(), true);
-        (void)BOMBomInsertFSObject(bom, fso, false);
-        BOMFSObjectFree(fso);
-    }
-
-    partialPath = partialPath + "/" + lastComponent;
-    fso = BOMFSObjectNew(BOMFileType);
-    BOMFSObjectSetFlags(fso, B_PATHONLY);
-    BOMFSObjectSetPathName(fso, partialPath.c_str(), true);
-    BOMFSObjectSetShortName(fso, lastComponent.c_str(), true);
-    (void)BOMBomInsertFSObject(bom, fso, false);
-    BOMFSObjectFree(fso);
-}
-
-void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot)
-{
-    mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755);
-
-    manifest.forEachConfiguration([&manifest, &masterDstRoot](const std::string& configName) {
-        auto                     config = manifest.configuration(configName);
-        std::vector<std::string> prodBomPaths;
-        std::vector<std::string> devBomPaths;
-
-        std::string runtimePath =  "/System/Library/Caches/com.apple.dyld/";
-        if (manifest.platform() == dyld3::Platform::macOS) {
-            runtimePath =  "/private/var/db/dyld/";
-        }
-
-        for (auto& arch : config.architectures) {
-            std::string cachePath = "dyld_shared_cache_" + arch.first;
-            prodBomPaths.push_back(cachePath);
-            if (manifest.platform() != dyld3::Platform::macOS) {
-                cachePath += ".development";
-            }
-            devBomPaths.push_back(cachePath);
-            char buffer[MAXPATHLEN];
-            sprintf(buffer, "%s/Boms/%s.prod.bom", masterDstRoot.c_str(), configName.c_str());
-            BOMBom bom = BOMBomNew(buffer);
-            for (auto& path : prodBomPaths) {
-                insertFileInBom(runtimePath + path, bom);
-            }
-            BOMBomFree(bom);
-
-            sprintf(buffer, "%s/Boms/%s.dev.bom", masterDstRoot.c_str(), configName.c_str());
-            bom = BOMBomNew(buffer);
-            for (auto& path : devBomPaths) {
-                insertFileInBom(runtimePath + path, bom);
-            }
-            BOMBomFree(bom);
-            
-            sprintf(buffer, "%s/Boms/%s.full.bom", masterDstRoot.c_str(), configName.c_str());
-            bom = BOMBomNew(buffer);
-            for (auto& path : prodBomPaths) {
-                insertFileInBom(runtimePath + path, bom);
-            }
-            for (auto& path : devBomPaths) {
-                insertFileInBom(runtimePath + path, bom);
-            }
-            BOMBomFree(bom);
-        }
-    });
-}
-
-bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose,
-           bool skipWrites, bool agileChooseSHA256CdHash, bool emitDevCaches, bool isLocallyBuiltCache)
-{
-    dispatch_queue_t                   queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-    dispatch_queue_t                   warningQueue = dispatch_queue_create("com.apple.dyld.cache-builder.warnings", DISPATCH_QUEUE_SERIAL);
-    std::vector<std::set<std::string>> dedupedCacheSets;
-    if (dedupe) {
-        manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) {
-            auto config = manifest.configuration(configName);
-            bool dupeFound = false;
-            
-            for (auto& cacheSet : dedupedCacheSets) {
-                if (config == manifest.configuration(*cacheSet.begin())) {
-                    cacheSet.insert(configName);
-                    dupeFound = true;
-                    break;
-                }
-            }
-            
-            if (!dupeFound) {
-                std::set<std::string> temp;
-                temp.insert(configName);
-                dedupedCacheSets.push_back(temp);
-            }
-        });
-    } else {
-        manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) {
-            std::set<std::string> temp;
-            temp.insert(configName);
-            dedupedCacheSets.push_back(temp);
-        });
-    }
-    
-    std::vector<dyld3::BuildQueueEntry> buildQueue;
-    
-    for (auto& cacheSet : dedupedCacheSets) {
-        //FIXME we may want to consider moving to hashes of UUID sets
-        std::string setName;
-        
-        for (auto& archName : cacheSet) {
-            if (!setName.empty()) {
-                setName += "|";
-            }
-            setName += archName;
-        }
-        
-        std::stringstream fileNameStream;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-        std::array<uint8_t, CC_SHA1_DIGEST_LENGTH> digest = { 0 };
-        CC_SHA1(setName.c_str(), (unsigned int)setName.length(), &digest[0]);
-#pragma clang diagnostic pop
-
-        fileNameStream << std::hex << std::uppercase << std::setfill('0');
-        for (int c : digest) {
-            fileNameStream << std::setw(2) << c;
-        }
-        
-        std::string fileName(fileNameStream.str());
-        
-        if (dedupe) {
-            for (auto& config : cacheSet) {
-                if (!skipWrites) {
-                    int err = symlink(("DedupedConfigs/" + fileName).c_str(), (masterDstRoot + "/" + config).c_str());
-                    if (err) {
-                        diags.warning("Could not create symlink '%s' -> 'DedupedConfigs/%s' (%d)", config.c_str(), fileName.c_str(), err);
-                    }
-                }
-            }
-        }
-        
-        manifest.configuration(*cacheSet.begin()).forEachArchitecture([&masterDstRoot, &dedupe, &fileName, &setName, &manifest,
-                                                                       &buildQueue, &cacheSet, skipWrites, verbose, emitDevCaches, isLocallyBuiltCache](const std::string& arch) {
-            std::string configPath;
-            std::string runtimePath =  "/System/Library/Caches/com.apple.dyld/";
-            if (manifest.platform() == dyld3::Platform::macOS) {
-                runtimePath =  "/private/var/db/dyld/";
-            }
-            if (dedupe) {
-                configPath = masterDstRoot + "/DedupedConfigs/" + fileName + runtimePath;
-            } else {
-                configPath = masterDstRoot + runtimePath;
-            }
-
-            mkpath_np(configPath.c_str(), 0755);
-            if (manifest.platform() == dyld3::Platform::macOS) {
-                buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, false, setName + "/" + arch,
-                                                             isLocallyBuiltCache, skipWrites, verbose));
-            } else {
-                if (emitDevCaches)
-                    buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch + ".development", cacheSet, arch, false, setName + "/" + arch,
-                                                                 isLocallyBuiltCache, skipWrites, verbose));
-                buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, true, setName + "/" + arch,
-                                                             isLocallyBuiltCache, skipWrites, verbose));
-            }
-        });
-    }
-
-    __block bool cacheBuildFailure = false;
-    __block std::set<std::string> warnings;
-    __block std::set<std::string> errors;
-
-    dispatch_sync(warningQueue, ^{
-        auto manifestWarnings = diags.warnings();
-        //warnings.insert(manifestWarnings.begin(), manifestWarnings.end());
-    });
-
-    bool requuiresConcurrencyLimit = false;
-    dispatch_semaphore_t concurrencyLimit = NULL;
-    // Limit cuncurrency to 8 threads for machines with 32GB of RAM and to 1 thread if we have 4GB or less of memory
-    uint64_t memSize = 0;
-    size_t sz = sizeof(memSize);;
-    if ( sysctlbyname("hw.memsize", &memSize, &sz, NULL, 0) == 0 ) {
-        if ( memSize <= 0x100000000ULL ) {
-            fprintf(stderr, "Detected 4Gb or less of memory, limiting concurrency to 1 thread\n");
-            requuiresConcurrencyLimit = true;
-            concurrencyLimit = dispatch_semaphore_create(1);
-        } else if ( memSize <= 0x800000000ULL ) {
-            fprintf(stderr, "Detected 32Gb or less of memory, limiting concurrency to 8 threads\n");
-            requuiresConcurrencyLimit = true;
-            concurrencyLimit = dispatch_semaphore_create(8);
-        }
-    }
-
-    dispatch_apply(buildQueue.size(), queue, ^(size_t index) {
-        auto queueEntry = buildQueue[index];
-        pthread_setname_np(queueEntry.options.loggingPrefix.substr(0, MAXTHREADNAMESIZE - 1).c_str());
-        
-        // Horrible hack to limit concurrency in low spec build machines.
-        if (requuiresConcurrencyLimit) { dispatch_semaphore_wait(concurrencyLimit, DISPATCH_TIME_FOREVER); }
-        DyldSharedCache::CreateResults results = DyldSharedCache::create(queueEntry.options, queueEntry.fileSystem, queueEntry.dylibsForCache, queueEntry.otherDylibsAndBundles, queueEntry.mainExecutables);
-        if (requuiresConcurrencyLimit) { dispatch_semaphore_signal(concurrencyLimit); }
-
-        dispatch_sync(warningQueue, ^{
-            warnings.insert(results.warnings.begin(), results.warnings.end());
-            bool chooseSecondCdHash = agileChooseSHA256CdHash;
-            if (agileChooseSHA256CdHash && !results.agileSignature) {
-                // Ignore this option for caches that are not signed agile (which is the majority).
-                chooseSecondCdHash = false;
-            }
-            for (const auto& configName : queueEntry.configNames) {
-                auto& configResults = manifest.configuration(configName).architecture(queueEntry.options.archs->name()).results;
-                for (const auto& mh : results.evictions) {
-                    configResults.exclude(mh, "VM overflow, evicting");
-                }
-                configResults.warnings = results.warnings;
-                if (queueEntry.options.optimizeStubs) {
-                    configResults.productionCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
-                } else {
-                    configResults.developmentCache.cdHash =  chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
-                }
-            }
-        });
-        if (!results.errorMessage.empty()) {
-            fprintf(stderr, "[%s] ERROR: %s\n", queueEntry.options.loggingPrefix.c_str(), results.errorMessage.c_str());
-            cacheBuildFailure = true;
-        } else if (skipWrites) {
-            fprintf(stderr, "[%s] Skipped writing cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str());
-        }
-    });
-    
-    // print any warnings
-    for (const std::string& warn : warnings) {
-        fprintf(stderr, "[WARNING] %s\n", warn.c_str());
-    }
-    
-    int err = sync_volume_np(masterDstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT);
-    if (err) {
-        fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err));
-    }
-    
-    return !cacheBuildFailure;
-}
index 0666b0c0a2a1183300e5849912cc32fd1f28f38e..2e0d5e155c255e75de73d0297a8396d58ae57009 100644 (file)
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#include <assert.h>
 
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/errno.h>
-#include <sys/fcntl.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <mach/mach_vm.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <mach/shared_region.h>
-#include <assert.h>
-#include <CommonCrypto/CommonHMAC.h>
-#include <CommonCrypto/CommonDigest.h>
-#include <CommonCrypto/CommonDigestSPI.h>
-#include <pthread/pthread.h>
-#include <apfs/apfs_fsctl.h>
-
-#include <string>
-#include <vector>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "MachOFileAbstraction.hpp"
-#include "CodeSigningTypes.h"
-#include "DyldSharedCache.h"
-#include "CacheBuilder.h"
-#include "FileAbstraction.hpp"
-#include "Trie.hpp"
-#include "FileUtils.h"
-#include "Diagnostics.h"
-#include "ClosureBuilder.h"
-#include "Closure.h"
-#include "ClosureFileSystemNull.h"
-#include "StringUtils.h"
-
-#if __has_include("dyld_cache_config.h")
-    #include "dyld_cache_config.h"
-#else
-    #define ARM_SHARED_REGION_START      0x1A000000ULL
-    #define ARM_SHARED_REGION_SIZE       0x26000000ULL
-    #define ARM64_SHARED_REGION_START   0x180000000ULL
-    #define ARM64_SHARED_REGION_SIZE     0x100000000ULL
-#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
-#else
-  #define  ARMV7K_CHAIN_BITS    0xE0000000
-  #define  ARMV7K_MAX           0x20000000
-#endif
-
-const CacheBuilder::ArchLayout CacheBuilder::_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 },
-#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 },
-#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  },
-#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 CacheBuilder::_s_neverStubEliminateDylibs[] = {
-    "/usr/lib/system/libdispatch.dylib",
-    nullptr
-};
-
-// These are functions that are interposed by Instruments.app or ASan
-const char* const CacheBuilder::_s_neverStubEliminateSymbols[] = {
-    "___bzero",
-    "___cxa_atexit",
-    "___cxa_throw",
-    "__longjmp",
-    "__objc_autoreleasePoolPop",
-    "_accept",
-    "_access",
-    "_asctime",
-    "_asctime_r",
-    "_asprintf",
-    "_atoi",
-    "_atol",
-    "_atoll",
-    "_calloc",
-    "_chmod",
-    "_chown",
-    "_close",
-    "_confstr",
-    "_ctime",
-    "_ctime_r",
-    "_dispatch_after",
-    "_dispatch_after_f",
-    "_dispatch_async",
-    "_dispatch_async_f",
-    "_dispatch_barrier_async_f",
-    "_dispatch_group_async",
-    "_dispatch_group_async_f",
-    "_dispatch_source_set_cancel_handler",
-    "_dispatch_source_set_event_handler",
-    "_dispatch_sync_f",
-    "_dlclose",
-    "_dlopen",
-    "_dup",
-    "_dup2",
-    "_endgrent",
-    "_endpwent",
-    "_ether_aton",
-    "_ether_hostton",
-    "_ether_line",
-    "_ether_ntoa",
-    "_ether_ntohost",
-    "_fchmod",
-    "_fchown",
-    "_fclose",
-    "_fdopen",
-    "_fflush",
-    "_fopen",
-    "_fork",
-    "_fprintf",
-    "_free",
-    "_freopen",
-    "_frexp",
-    "_frexpf",
-    "_frexpl",
-    "_fscanf",
-    "_fstat",
-    "_fstatfs",
-    "_fstatfs64",
-    "_fsync",
-    "_ftime",
-    "_getaddrinfo",
-    "_getattrlist",
-    "_getcwd",
-    "_getgrent",
-    "_getgrgid",
-    "_getgrgid_r",
-    "_getgrnam",
-    "_getgrnam_r",
-    "_getgroups",
-    "_gethostbyaddr",
-    "_gethostbyname",
-    "_gethostbyname2",
-    "_gethostent",
-    "_getifaddrs",
-    "_getitimer",
-    "_getnameinfo",
-    "_getpass",
-    "_getpeername",
-    "_getpwent",
-    "_getpwnam",
-    "_getpwnam_r",
-    "_getpwuid",
-    "_getpwuid_r",
-    "_getsockname",
-    "_getsockopt",
-    "_gmtime",
-    "_gmtime_r",
-    "_if_indextoname",
-    "_if_nametoindex",
-    "_index",
-    "_inet_aton",
-    "_inet_ntop",
-    "_inet_pton",
-    "_initgroups",
-    "_ioctl",
-    "_lchown",
-    "_lgamma",
-    "_lgammaf",
-    "_lgammal",
-    "_link",
-    "_listxattr",
-    "_localtime",
-    "_localtime_r",
-    "_longjmp",
-    "_lseek",
-    "_lstat",
-    "_malloc",
-    "_malloc_create_zone",
-    "_malloc_default_purgeable_zone",
-    "_malloc_default_zone",
-    "_malloc_good_size",
-    "_malloc_make_nonpurgeable",
-    "_malloc_make_purgeable",
-    "_malloc_set_zone_name",
-    "_mbsnrtowcs",
-    "_mbsrtowcs",
-    "_mbstowcs",
-    "_memchr",
-    "_memcmp",
-    "_memcpy",
-    "_memmove",
-    "_memset",
-    "_mktime",
-    "_mlock",
-    "_mlockall",
-    "_modf",
-    "_modff",
-    "_modfl",
-    "_munlock",
-    "_munlockall",
-    "_objc_autoreleasePoolPop",
-    "_objc_setProperty",
-    "_objc_setProperty_atomic",
-    "_objc_setProperty_atomic_copy",
-    "_objc_setProperty_nonatomic",
-    "_objc_setProperty_nonatomic_copy",
-    "_objc_storeStrong",
-    "_open",
-    "_opendir",
-    "_poll",
-    "_posix_memalign",
-    "_pread",
-    "_printf",
-    "_pthread_attr_getdetachstate",
-    "_pthread_attr_getguardsize",
-    "_pthread_attr_getinheritsched",
-    "_pthread_attr_getschedparam",
-    "_pthread_attr_getschedpolicy",
-    "_pthread_attr_getscope",
-    "_pthread_attr_getstack",
-    "_pthread_attr_getstacksize",
-    "_pthread_condattr_getpshared",
-    "_pthread_create",
-    "_pthread_getschedparam",
-    "_pthread_join",
-    "_pthread_mutex_lock",
-    "_pthread_mutex_unlock",
-    "_pthread_mutexattr_getprioceiling",
-    "_pthread_mutexattr_getprotocol",
-    "_pthread_mutexattr_getpshared",
-    "_pthread_mutexattr_gettype",
-    "_pthread_rwlockattr_getpshared",
-    "_pwrite",
-    "_rand_r",
-    "_read",
-    "_readdir",
-    "_readdir_r",
-    "_readv",
-    "_readv$UNIX2003",
-    "_realloc",
-    "_realpath",
-    "_recv",
-    "_recvfrom",
-    "_recvmsg",
-    "_remquo",
-    "_remquof",
-    "_remquol",
-    "_scanf",
-    "_send",
-    "_sendmsg",
-    "_sendto",
-    "_setattrlist",
-    "_setgrent",
-    "_setitimer",
-    "_setlocale",
-    "_setpwent",
-    "_shm_open",
-    "_shm_unlink",
-    "_sigaction",
-    "_sigemptyset",
-    "_sigfillset",
-    "_siglongjmp",
-    "_signal",
-    "_sigpending",
-    "_sigprocmask",
-    "_sigwait",
-    "_snprintf",
-    "_sprintf",
-    "_sscanf",
-    "_stat",
-    "_statfs",
-    "_statfs64",
-    "_strcasecmp",
-    "_strcat",
-    "_strchr",
-    "_strcmp",
-    "_strcpy",
-    "_strdup",
-    "_strerror",
-    "_strerror_r",
-    "_strlen",
-    "_strncasecmp",
-    "_strncat",
-    "_strncmp",
-    "_strncpy",
-    "_strptime",
-    "_strtoimax",
-    "_strtol",
-    "_strtoll",
-    "_strtoumax",
-    "_tempnam",
-    "_time",
-    "_times",
-    "_tmpnam",
-    "_tsearch",
-    "_unlink",
-    "_valloc",
-    "_vasprintf",
-    "_vfprintf",
-    "_vfscanf",
-    "_vprintf",
-    "_vscanf",
-    "_vsnprintf",
-    "_vsprintf",
-    "_vsscanf",
-    "_wait",
-    "_wait$UNIX2003",
-    "_wait3",
-    "_wait4",
-    "_waitid",
-    "_waitid$UNIX2003",
-    "_waitpid",
-    "_waitpid$UNIX2003",
-    "_wcslen",
-    "_wcsnrtombs",
-    "_wcsrtombs",
-    "_wcstombs",
-    "_wordexp",
-    "_write",
-    "_writev",
-    "_writev$UNIX2003",
-    // <rdar://problem/22050956> always use stubs for C++ symbols that can be overridden
-    "__ZdaPv",
-    "__ZdlPv",
-    "__Znam",
-    "__Znwm",
-
-    nullptr
-};
-
-
-CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem)
-    : _options(options)
-    , _fileSystem(fileSystem)
-    , _fullAllocatedBuffer(0)
-    , _diagnostics(options.loggingPrefix, options.verbose)
-    , _archLayout(nullptr)
-    , _aliasCount(0)
-    , _slideInfoFileOffset(0)
-    , _slideInfoBufferSizeAllocated(0)
-    , _allocatedBufferSize(0)
-    , _selectorStringsFromExecutables(0)
-{
-
-    std::string targetArch = options.archs->name();
-    if ( options.forSimulator && (options.archs == &dyld3::GradedArchs::i386) )
-        targetArch = "sim-x86";
-
-    for (const ArchLayout& layout : _s_archLayout) {
-        if ( layout.archName == targetArch ) {
-            _archLayout = &layout;
-            break;
-        }
-    }
-
-    if (!_archLayout) {
-        _diagnostics.error("Tool was built without support for: '%s'", targetArch.c_str());
-    }
-}
-
-
-std::string CacheBuilder::errorMessage()
-{
-    return _diagnostics.errorMessage();
-}
-
-const std::set<std::string> CacheBuilder::warnings()
-{
-    return _diagnostics.warnings();
-}
-
-const std::set<const dyld3::MachOAnalyzer*> CacheBuilder::evictions()
-{
-    return _evictions;
-}
-
-void CacheBuilder::deleteBuffer()
-{
-    // Cache buffer
-    vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _archLayout->sharedMemorySize);
-    _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;
-}
-
-
-void CacheBuilder::makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder)
-{
-    for (const LoadedMachO& dylib : dylibs) {
-        _sortedDylibs.push_back({ &dylib, dylib.mappedFile.runtimePath, {} });
-    }
-
-    std::sort(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& a, const DylibInfo& b) {
-        const auto& orderA = sortOrder.find(a.input->mappedFile.runtimePath);
-        const auto& orderB = sortOrder.find(b.input->mappedFile.runtimePath);
-        bool foundA = (orderA != sortOrder.end());
-        bool foundB = (orderB != sortOrder.end());
-
-        // 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;
-        
-        // Sort mac before iOSMac
-        bool isIOSMacA = strncmp(a.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0;
-        bool isIOSMacB = strncmp(b.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0;
-        if (isIOSMacA != isIOSMacB)
-            return !isIOSMacA;
-        
-        // Finally sort by path
-        return a.input->mappedFile.runtimePath < b.input->mappedFile.runtimePath;
-    });
-}
-
-
-inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
-{
-    return (uint32_t)(abstime/1000/1000);
-}
-
-struct DylibAndSize
-{
-    const CacheBuilder::LoadedMachO*    input;
-    const char*                         installName;
-    uint64_t                            size;
-};
-
-uint64_t CacheBuilder::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 ( _readWriteRegion.sizeInUse > 0x40000000 )
-            return (_readWriteRegion.sizeInUse - 0x40000000);
-
-        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;
-    }
-    else {
-        bool alreadyOptimized = (_readOnlyRegion.sizeInUse != _readOnlyRegion.bufferSize);
-        uint64_t vmSize = _readOnlyRegion.unslidLoadAddress - _readExecuteRegion.unslidLoadAddress;
-        if ( alreadyOptimized )
-            vmSize += _readOnlyRegion.sizeInUse;
-        else if ( _options.excludeLocalSymbols )
-            vmSize += (_readOnlyRegion.sizeInUse * 37/100); // assume locals removal and LINKEDIT optimzation reduces LINKEDITs %37 of original size
-        else
-            vmSize += (_readOnlyRegion.sizeInUse * 80/100); // assume LINKEDIT optimzation reduces LINKEDITs to %80 of original size
-        if ( vmSize > _archLayout->sharedMemorySize )
-            return vmSize - _archLayout->sharedMemorySize;
-    }
-    // fits in shared region
-    return 0;
-}
-
-size_t CacheBuilder::evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs)
-{
-    // build a reverse map of all dylib dependencies
-    __block std::map<std::string, std::set<std::string>> references;
-    std::map<std::string, std::set<std::string>>* referencesPtr = &references;
-    for (const DylibInfo& dylib : _sortedDylibs) {
-        // Esnure we have an entry (even if it is empty)
-        if (references.count(dylib.input->mappedFile.mh->installName()) == 0) {
-            references[dylib.input->mappedFile.mh->installName()] = std::set<std::string>();
-        };
-        dylib.input->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
-            references[loadPath].insert(dylib.input->mappedFile.mh->installName());
-        });
-    }
-
-    // Find the sizes of all the dylibs
-    std::vector<DylibAndSize> dylibsToSort;
-    std::vector<DylibAndSize> sortedDylibs;
-    for (const DylibInfo& dylib : _sortedDylibs) {
-        const char* installName = dylib.input->mappedFile.mh->installName();
-        __block uint64_t segsSize = 0;
-        dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
-            if ( strcmp(info.segName, "__LINKEDIT") != 0 )
-                segsSize += info.vmSize;
-        });
-        dylibsToSort.push_back({ dylib.input, installName, segsSize });
-    }
-
-    // Build an ordered list of what to remove. At each step we do following
-    // 1) Find all dylibs that nothing else depends on
-    // 2a) If any of those dylibs are not in the order select the largest one of them
-    // 2b) If all the leaf dylibs are in the order file select the last dylib that appears last in the order file
-    // 3) Remove all entries to the removed file from the reverse dependency map
-    // 4) Go back to one and repeat until there are no more evictable dylibs
-    // This results in us always choosing the locally optimal selection, and then taking into account how that impacts
-    // the dependency graph for subsequent selections
-    
-    bool candidateFound = true;
-    while (candidateFound) {
-        candidateFound = false;
-        DylibAndSize candidate;
-        uint64_t candidateOrder = 0;
-        for(const auto& dylib : dylibsToSort) {
-            const auto &i = referencesPtr->find(dylib.installName);
-            assert(i != referencesPtr->end());
-            if (!i->second.empty()) {
-                continue;
-            }
-            const auto& j = _options.dylibOrdering.find(dylib.input->mappedFile.runtimePath);
-            uint64_t order = 0;
-            if (j != _options.dylibOrdering.end()) {
-                order = j->second;
-            } else {
-                // Not in the order file, set order sot it goes to the front of the list
-                order = UINT64_MAX;
-            }
-            if (order > candidateOrder ||
-                (order == UINT64_MAX && candidate.size < dylib.size)) {
-                    // The new file is either a lower priority in the order file
-                    // or the same priority as the candidate but larger
-                    candidate = dylib;
-                    candidateOrder = order;
-                    candidateFound = true;
-            }
-        }
-        if (candidateFound) {
-            sortedDylibs.push_back(candidate);
-            referencesPtr->erase(candidate.installName);
-            for (auto& dependent : references) {
-                (void)dependent.second.erase(candidate.installName);
-            }
-            auto j = std::find_if(dylibsToSort.begin(), dylibsToSort.end(), [&candidate](const DylibAndSize& dylib) {
-                return (strcmp(candidate.installName, dylib.installName) == 0);
-            });
-            if (j != dylibsToSort.end()) {
-                dylibsToSort.erase(j);
-            }
-        }
-    }
-
-     // build set of dylibs that if removed will allow cache to build
-    for (DylibAndSize& dylib : sortedDylibs) {
-        if ( _options.verbose )
-            _diagnostics.warning("to prevent cache overflow, not caching %s", dylib.installName);
-        _evictions.insert(dylib.input->mappedFile.mh);
-        // Track the evicted dylibs so we can try build "other" dlopen closures for them.
-        overflowDylibs.push_back(dylib.input);
-        if ( dylib.size > reductionTarget )
-            break;
-        reductionTarget -= dylib.size;
-    }
-
-    // prune _sortedDylibs
-    _sortedDylibs.erase(std::remove_if(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& dylib) {
-        return (_evictions.count(dylib.input->mappedFile.mh) != 0);
-    }),_sortedDylibs.end());
-
-    return _evictions.size();
-}
-
-// Handles building a list of input files to the CacheBuilder itself.
-class CacheInputBuilder {
-public:
-    CacheInputBuilder(const dyld3::closure::FileSystem& fileSystem,
-                      const dyld3::GradedArchs& archs, dyld3::Platform reqPlatform)
-    : fileSystem(fileSystem), reqArchs(archs), reqPlatform(reqPlatform) { }
-
-    // Loads and maps any MachOs in the given list of files.
-    void loadMachOs(std::vector<CacheBuilder::InputFile>& inputFiles,
-                    std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
-                    std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
-                    std::vector<CacheBuilder::LoadedMachO>& executables,
-                    std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles) {
-
-        std::map<std::string, uint64_t> dylibInstallNameMap;
-        for (CacheBuilder::InputFile& inputFile : inputFiles) {
-            char realerPath[MAXPATHLEN];
-            dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchs, reqPlatform, realerPath);
-            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
-            if (ma == nullptr) {
-                couldNotLoadFiles.emplace_back((CacheBuilder::LoadedMachO){ DyldSharedCache::MappedMachO(), loadedFileInfo, &inputFile });
-                continue;
-            }
-
-            DyldSharedCache::MappedMachO mappedFile(inputFile.path, ma, loadedFileInfo.sliceLen, false, false,
-                                                    loadedFileInfo.sliceOffset, loadedFileInfo.mtime, loadedFileInfo.inode);
-
-            // The file can be loaded with the given slice, but we may still want to exlude it from the cache.
-            if (ma->isDylib()) {
-                std::string installName = ma->installName();
-
-                // Let the platform exclude the file before we do anything else.
-                if (platformExcludesInstallName(installName)) {
-                    inputFile.diag.verbose("Platform excluded file\n");
-                    fileSystem.unloadFile(loadedFileInfo);
-                    continue;
-                }
-
-                if (!ma->canBePlacedInDyldCache(inputFile.path, ^(const char* msg) {
-                    inputFile.diag.warning("Dylib located at '%s' cannot be placed in cache because: %s", inputFile.path, msg);
-                })) {
-                    // TODO: Add exclusion lists here?
-                    // Probably not as we already applied the dylib exclusion list.
-                    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);
-                    }) ) {
-                        fileSystem.unloadFile(loadedFileInfo);
-                        continue;
-                    }
-                    otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
-                    continue;
-                }
-
-                // Otherwise see if we have another file with this install name
-                auto iteratorAndInserted = dylibInstallNameMap.insert(std::make_pair(installName, dylibsToCache.size()));
-                if (iteratorAndInserted.second) {
-                    // We inserted the dylib so we haven't seen another with this name.
-                    if (installName[0] != '@' && installName != inputFile.path) {
-                        inputFile.diag.warning("Dylib located at '%s' has installname '%s'", inputFile.path, installName.c_str());
-                    }
-
-                    dylibsToCache.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
-                } else {
-                    // We didn't insert this one so we've seen it before.
-                    CacheBuilder::LoadedMachO& previousLoadedMachO = dylibsToCache[iteratorAndInserted.first->second];
-                    inputFile.diag.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), inputFile.path, previousLoadedMachO.mappedFile.runtimePath.c_str());
-
-                    // This is the "Good" one, overwrite
-                    if (inputFile.path == installName) {
-                        // Unload the old one
-                        fileSystem.unloadFile(previousLoadedMachO.loadedFileInfo);
-
-                        // And replace with this one.
-                        previousLoadedMachO.mappedFile = mappedFile;
-                        previousLoadedMachO.loadedFileInfo = loadedFileInfo;
-                    }
-                }
-            } else if (ma->isBundle()) {
-                // TODO: Add exclusion lists here?
-                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);
-                }) ) {
-                    fileSystem.unloadFile(loadedFileInfo);
-                    continue;
-                }
-                otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
-            } else if (ma->isDynamicExecutable()) {
-                if (platformExcludesExecutablePath_macOS(inputFile.path)) {
-                    inputFile.diag.verbose("Platform excluded file\n");
-                    fileSystem.unloadFile(loadedFileInfo);
-                    continue;
-                }
-                executables.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
-            } else {
-                inputFile.diag.verbose("Unsupported mach file type\n");
-                fileSystem.unloadFile(loadedFileInfo);
-            }
-        }
-    }
-
-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") {
-            return true;
-        }
-        return false;
-    }
-
-    static bool platformExcludesExecutablePath_tvOS(const std::string& path) {
-        return platformExcludesExecutablePath_iOS(path);
-    }
-
-    static bool platformExcludesExecutablePath_watchOS(const std::string& path) {
-        return platformExcludesExecutablePath_iOS(path);
-    }
-
-    static bool platformExcludesExecutablePath_bridgeOS(const std::string& path) {
-        return platformExcludesExecutablePath_iOS(path);
-    }
-
-    // Returns true if the current platform requires that this path be excluded from the shared cache
-    // Note that this overrides any exclusion from anywhere else.
-    bool platformExcludesExecutablePath(const std::string& path) {
-        switch (reqPlatform) {
-            case dyld3::Platform::unknown:
-                return false;
-            case dyld3::Platform::macOS:
-                return platformExcludesExecutablePath_macOS(path);
-            case dyld3::Platform::iOS:
-                return platformExcludesExecutablePath_iOS(path);
-            case dyld3::Platform::tvOS:
-                return platformExcludesExecutablePath_tvOS(path);
-            case dyld3::Platform::watchOS:
-                return platformExcludesExecutablePath_watchOS(path);
-            case dyld3::Platform::bridgeOS:
-                return platformExcludesExecutablePath_bridgeOS(path);
-            case dyld3::Platform::iOSMac:
-                return false;
-            case dyld3::Platform::iOS_simulator:
-                return false;
-            case dyld3::Platform::tvOS_simulator:
-                return false;
-            case dyld3::Platform::watchOS_simulator:
-                return false;
-            case dyld3::Platform::driverKit:
-                return false;
-        }
-    }
-
-    const dyld3::closure::FileSystem&                   fileSystem;
-    const dyld3::GradedArchs&                           reqArchs;
-    dyld3::Platform                                     reqPlatform;
-};
-
-static void verifySelfContained(std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
-                                std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
-                                std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles)
-{
-    // build map of dylibs
-    __block std::map<std::string, const CacheBuilder::LoadedMachO*> knownDylibs;
-    __block std::map<std::string, const CacheBuilder::LoadedMachO*> allDylibs;
-    for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
-        knownDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
-        allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
-        if (const char* installName = dylib.mappedFile.mh->installName()) {
-            knownDylibs.insert({ installName, &dylib });
-            allDylibs.insert({ installName, &dylib });
-        }
-    }
-
-    for (const CacheBuilder::LoadedMachO& dylib : otherDylibs) {
-        allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
-        if (const char* installName = dylib.mappedFile.mh->installName())
-            allDylibs.insert({ installName, &dylib });
-    }
-
-    for (const CacheBuilder::LoadedMachO& dylib : couldNotLoadFiles) {
-        allDylibs.insert({ dylib.inputFile->path, &dylib });
-    }
-
-    // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
-    __block std::map<std::string, std::set<std::string>> badDylibs;
-    __block bool doAgain = true;
-    while ( doAgain ) {
-        doAgain = false;
-        // scan dylib list making sure all dependents are in dylib list
-        for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
-            if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
-                continue;
-            dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
-                if (isWeak)
-                    return;
-                if ( knownDylibs.count(loadPath) == 0 ) {
-                    badDylibs[dylib.mappedFile.runtimePath].insert(std::string("Could not find dependency '") + loadPath + "'");
-                    knownDylibs.erase(dylib.mappedFile.runtimePath);
-                    knownDylibs.erase(dylib.mappedFile.mh->installName());
-                    doAgain = true;
-                }
-            });
-        }
-    }
-
-    // Now walk the dylibs which depend on missing dylibs and see if any of them are required binaries.
-    for (auto badDylibsIterator : badDylibs) {
-        const std::string& dylibRuntimePath = badDylibsIterator.first;
-        auto requiredDylibIterator = allDylibs.find(dylibRuntimePath);
-        if (requiredDylibIterator == allDylibs.end())
-            continue;
-        if (!requiredDylibIterator->second->inputFile->mustBeIncluded())
-            continue;
-        // This dylib is required so mark all dependencies as requried too
-        __block std::vector<const CacheBuilder::LoadedMachO*> worklist;
-        worklist.push_back(requiredDylibIterator->second);
-        while (!worklist.empty()) {
-            const CacheBuilder::LoadedMachO* dylib = worklist.back();
-            worklist.pop_back();
-            if (!dylib->mappedFile.mh)
-                continue;
-            dylib->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
-                if (isWeak)
-                    return;
-                auto dylibIterator = allDylibs.find(loadPath);
-                if (dylibIterator != allDylibs.end()) {
-                    if (dylibIterator->second->inputFile->state == CacheBuilder::InputFile::Unset) {
-                        dylibIterator->second->inputFile->state = CacheBuilder::InputFile::MustBeIncludedForDependent;
-                        worklist.push_back(dylibIterator->second);
-                    }
-                }
-            });
-        }
-    }
-
-    // FIXME: Make this an option we can pass in
-    const bool evictLeafDylibs = true;
-    if (evictLeafDylibs) {
-        doAgain = true;
-        while ( doAgain ) {
-            doAgain = false;
-
-            // build count of how many references there are to each dylib
-            __block std::set<std::string> referencedDylibs;
-            for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
-                if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
-                    continue;
-                dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
-                    referencedDylibs.insert(loadPath);
-                });
-            }
-
-            // find all dylibs not referenced
-            for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
-                if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
-                    continue;
-                const char* installName = dylib.mappedFile.mh->installName();
-                if ( (referencedDylibs.count(installName) == 0) && (dylib.inputFile->state == CacheBuilder::InputFile::MustBeExcludedIfUnused) ) {
-                    badDylibs[dylib.mappedFile.runtimePath].insert(std::string("It has been explicitly excluded as it is unused"));
-                    doAgain = true;
-                }
-            }
-        }
-    }
-
-    // Move bad dylibs from dylibs to cache to other dylibs.
-    for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
-        auto i = badDylibs.find(dylib.mappedFile.runtimePath);
-        if ( i != badDylibs.end()) {
-            otherDylibs.push_back(dylib);
-            for (const std::string& reason : i->second )
-                otherDylibs.back().inputFile->diag.warning("Dylib located at '%s' not placed in shared cache because: %s", dylib.mappedFile.runtimePath.c_str(), reason.c_str());
-        }
-    }
-
-    const auto& badDylibsLambdaRef = badDylibs;
-    dylibsToCache.erase(std::remove_if(dylibsToCache.begin(), dylibsToCache.end(), [&](const CacheBuilder::LoadedMachO& dylib) {
-        if (badDylibsLambdaRef.find(dylib.mappedFile.runtimePath) != badDylibsLambdaRef.end())
-            return true;
-        return false;
-    }), dylibsToCache.end());
-}
-
-// This is the new build API which takes the raw files (which could be FAT) and tries to build a cache from them.
-// We should remove the other build() method, or make it private so that this can wrap it.
-void CacheBuilder::build(std::vector<CacheBuilder::InputFile>& inputFiles,
-                         std::vector<DyldSharedCache::FileAlias>& aliases) {
-    // First filter down to files which are actually MachO's
-    CacheInputBuilder cacheInputBuilder(_fileSystem, *_options.archs, _options.platform);
-
-    std::vector<LoadedMachO> dylibsToCache;
-    std::vector<LoadedMachO> otherDylibs;
-    std::vector<LoadedMachO> executables;
-    std::vector<LoadedMachO> couldNotLoadFiles;
-    cacheInputBuilder.loadMachOs(inputFiles, dylibsToCache, otherDylibs, executables, couldNotLoadFiles);
-
-    verifySelfContained(dylibsToCache, otherDylibs, couldNotLoadFiles);
-
-    // Check for required binaries before we try to build the cache
-    if (!_diagnostics.hasError()) {
-        // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
-        std::string errorString;
-        for (const LoadedMachO& dylib : otherDylibs) {
-            if (dylib.inputFile->mustBeIncluded()) {
-                // An error loading a required file must be propagated up to the top level diagnostic handler.
-                bool gotWarning = false;
-                for (const std::string& warning : dylib.inputFile->diag.warnings()) {
-                    gotWarning = true;
-                    std::string message = warning;
-                    if (message.back() == '\n')
-                        message.pop_back();
-                    if (!errorString.empty())
-                        errorString += "ERROR: ";
-                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + message + "\n";
-                }
-                if (!gotWarning) {
-                    if (!errorString.empty())
-                        errorString += "ERROR: ";
-                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error.  Please report to dyld'\n";
-                }
-            }
-        }
-        for (const LoadedMachO& dylib : couldNotLoadFiles) {
-            if (dylib.inputFile->mustBeIncluded()) {
-                if (dylib.inputFile->diag.hasError()) {
-                    if (!errorString.empty())
-                        errorString += "ERROR: ";
-                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + dylib.inputFile->diag.errorMessage() + "\n";
-                } else {
-                    if (!errorString.empty())
-                        errorString += "ERROR: ";
-                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error.  Please report to dyld'\n";
-
-                }
-            }
-        }
-        if (!errorString.empty()) {
-            _diagnostics.error("%s", errorString.c_str());
-        }
-    }
-
-    if (!_diagnostics.hasError())
-        build(dylibsToCache, otherDylibs, executables, aliases);
-
-    if (!_diagnostics.hasError()) {
-        // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
-        std::string errorString;
-        for (CacheBuilder::InputFile& inputFile : inputFiles) {
-            if (inputFile.mustBeIncluded() && inputFile.diag.hasError()) {
-                // An error loading a required file must be propagated up to the top level diagnostic handler.
-                std::string message = inputFile.diag.errorMessage();
-                if (message.back() == '\n')
-                    message.pop_back();
-                errorString += "Required binary was not included in the shared cache '" + std::string(inputFile.path) + "' because: " + message + "\n";
-            }
-        }
-        if (!errorString.empty()) {
-            _diagnostics.error("%s", errorString.c_str());
-        }
-    }
-
-    // Add all the warnings from the input files to the top level warnings on the main diagnostics object.
-    for (CacheBuilder::InputFile& inputFile : inputFiles) {
-        for (const std::string& warning : inputFile.diag.warnings())
-            _diagnostics.warning("%s", warning.c_str());
-    }
-
-    // Clean up the loaded files
-    for (LoadedMachO& loadedMachO : dylibsToCache)
-        _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
-    for (LoadedMachO& loadedMachO : otherDylibs)
-        _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
-    for (LoadedMachO& loadedMachO : executables)
-        _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
-}
-
-void CacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs,
-                         const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibsInput,
-                         const std::vector<DyldSharedCache::MappedMachO>& osExecutables,
-                         std::vector<DyldSharedCache::FileAlias>& aliases) {
-
-    std::vector<LoadedMachO> dylibsToCache;
-    std::vector<LoadedMachO> otherDylibs;
-    std::vector<LoadedMachO> executables;
-
-    for (const DyldSharedCache::MappedMachO& mappedMachO : dylibs) {
-        dyld3::closure::LoadedFileInfo loadedFileInfo;
-        loadedFileInfo.fileContent      = mappedMachO.mh;
-        loadedFileInfo.fileContentLen   = mappedMachO.length;
-        loadedFileInfo.sliceOffset      = mappedMachO.sliceFileOffset;
-        loadedFileInfo.sliceLen         = mappedMachO.length;
-        loadedFileInfo.inode            = mappedMachO.inode;
-        loadedFileInfo.mtime            = mappedMachO.modTime;
-        loadedFileInfo.path             = mappedMachO.runtimePath.c_str();
-        dylibsToCache.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
-    }
-
-    for (const DyldSharedCache::MappedMachO& mappedMachO : otherOsDylibsInput) {
-        dyld3::closure::LoadedFileInfo loadedFileInfo;
-        loadedFileInfo.fileContent      = mappedMachO.mh;
-        loadedFileInfo.fileContentLen   = mappedMachO.length;
-        loadedFileInfo.sliceOffset      = mappedMachO.sliceFileOffset;
-        loadedFileInfo.sliceLen         = mappedMachO.length;
-        loadedFileInfo.inode            = mappedMachO.inode;
-        loadedFileInfo.mtime            = mappedMachO.modTime;
-        loadedFileInfo.path             = mappedMachO.runtimePath.c_str();
-        otherDylibs.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
-    }
-
-    for (const DyldSharedCache::MappedMachO& mappedMachO : osExecutables) {
-        dyld3::closure::LoadedFileInfo loadedFileInfo;
-        loadedFileInfo.fileContent      = mappedMachO.mh;
-        loadedFileInfo.fileContentLen   = mappedMachO.length;
-        loadedFileInfo.sliceOffset      = mappedMachO.sliceFileOffset;
-        loadedFileInfo.sliceLen         = mappedMachO.length;
-        loadedFileInfo.inode            = mappedMachO.inode;
-        loadedFileInfo.mtime            = mappedMachO.modTime;
-        loadedFileInfo.path             = mappedMachO.runtimePath.c_str();
-        executables.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
-    }
-
-    build(dylibsToCache, otherDylibs, executables, aliases);
-}
-
-void CacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
-                         const std::vector<LoadedMachO>& otherOsDylibsInput,
-                         const std::vector<LoadedMachO>& osExecutables,
-                         std::vector<DyldSharedCache::FileAlias>& aliases)
-{
-    // <rdar://problem/21317611> error out instead of crash if cache has no dylibs
-    // FIXME: plist should specify required vs optional dylibs
-    if ( dylibs.size() < 30 ) {
-        _diagnostics.error("missing required minimum set of dylibs");
-        return;
-    }
-    uint64_t t1 = mach_absolute_time();
-
-    // make copy of dylib list and sort
-    makeSortedDylibs(dylibs, _options.dylibOrdering);
-
-    // allocate space used by largest possible cache plus room for LINKEDITS before optimization
-    _allocatedBufferSize = _archLayout->sharedMemorySize * 1.50;
-    if ( vm_allocate(mach_task_self(), &_fullAllocatedBuffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) {
-        _diagnostics.error("could not allocate buffer");
-        return;
-    }
-
-    // assign addresses for each segment of each dylib in new cache
-    parseCoalescableSegments();
-    processSelectorStrings(osExecutables);
-    assignSegmentAddresses();
-    std::vector<const LoadedMachO*> overflowDylibs;
-    while ( cacheOverflowAmount() != 0 ) {
-        if ( !_options.evictLeafDylibsOnOverflow ) {
-            _diagnostics.error("cache overflow by %lluMB", cacheOverflowAmount() / 1024 / 1024);
-            return;
-        }
-        size_t evictionCount = evictLeafDylibs(cacheOverflowAmount(), overflowDylibs);
-        // re-layout cache
-        for (DylibInfo& dylib : _sortedDylibs)
-            dylib.cacheLocation.clear();
-        _coalescedText.clear();
-        parseCoalescableSegments();
-        processSelectorStrings(osExecutables);
-        assignSegmentAddresses();
-
-        _diagnostics.verbose("cache overflow, evicted %lu leaf dylibs\n", evictionCount);
-    }
-    markPaddingInaccessible();
-
-     // copy all segments into cache
-    uint64_t t2 = mach_absolute_time();
-    writeCacheHeader();
-    copyRawSegments();
-
-    // rebase all dylibs for new location in cache
-    uint64_t t3 = mach_absolute_time();
-    _aslrTracker.setDataRegion(_readWriteRegion.buffer, _readWriteRegion.sizeInUse);
-    if ( !_options.cacheSupportsASLR )
-        _aslrTracker.disable();
-    adjustAllImagesForNewSegmentLocations();
-    if ( _diagnostics.hasError() )
-        return;
-
-    // build ImageArray for dyld3, which has side effect of binding all cached dylibs
-    uint64_t t4 = mach_absolute_time();
-    buildImageArray(aliases);
-    if ( _diagnostics.hasError() )
-        return;
-
-    // optimize ObjC
-    uint64_t t5 = mach_absolute_time();
-    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    if ( _options.optimizeObjC )
-        optimizeObjC();
-    if ( _diagnostics.hasError() )
-        return;
-
-
-    // optimize away stubs
-    uint64_t t6 = mach_absolute_time();
-    if ( _options.optimizeStubs )
-         optimizeAwayStubs();
-
-
-    // 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();
-    optimizeLinkedit();
-
-    // 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) ) {
-        // compute and add dlopen closures for all other dylibs
-        addOtherImageArray(otherOsDylibsInput, overflowDylibs);
-        if ( _diagnostics.hasError() )
-            return;
-
-        // compute and add launch closures to end of read-only region
-        addClosures(osExecutables);
-        if ( _diagnostics.hasError() )
-            return;
-    }
-
-    // update final readOnly region size
-    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCache->header.mappingOffset);
-    mappings[2].size = _readOnlyRegion.sizeInUse;
-    if ( _options.excludeLocalSymbols )
-        dyldCache->header.localSymbolsOffset = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse;
-
-    // record 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;
-        dyldCache->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2);
-    }
-    else {
-        // <rdar://problem/49852839> branch predictor on arm64 currently only looks at low 32-bits, so don't slide cache more than 2GB
-        if ( (_archLayout->sharedMemorySize == 0x100000000) && (_readExecuteRegion.sizeInUse < 0x80000000) )
-            dyldCache->header.maxSlide = 0x80000000 - _readExecuteRegion.sizeInUse;
-        else
-            dyldCache->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (_readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
-    }
-
-    uint64_t t9 = mach_absolute_time();
-    
-    // fill in slide info at start of region[2]
-    // do this last because it modifies pointers in DATA segments
-    if ( _options.cacheSupportsASLR ) {
-#if SUPPORT_ARCH_arm64e
-        if ( strcmp(_archLayout->archName, "arm64e") == 0 )
-            writeSlideInfoV3(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
-        else
-#endif
-        if ( _archLayout->is64 )
-            writeSlideInfoV2<Pointer64<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
-#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
-        else if ( _archLayout->pointerDeltaMask == 0xC0000000 )
-            writeSlideInfoV4<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
-#endif
-        else
-            writeSlideInfoV2<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
-    }
-
-    uint64_t t10 = mach_absolute_time();
-
-    // last sanity check on size
-    if ( cacheOverflowAmount() != 0 ) {
-        _diagnostics.error("cache overflow after optimizations 0x%llX -> 0x%llX", _readExecuteRegion.unslidLoadAddress, _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
-        return;
-    }
-
-    // codesignature is part of file, but is not mapped
-    codeSign();
-    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 build %lu closures: %ums\n", osExecutables.size(), absolutetime_to_milliseconds(t9-t8));
-        fprintf(stderr, "time to compute slide info: %ums\n", absolutetime_to_milliseconds(t10-t9));
-        fprintf(stderr, "time to compute UUID and codesign cache file: %ums\n", absolutetime_to_milliseconds(t11-t10));
-    }
-
-    return;
-}
-
-
-void CacheBuilder::writeCacheHeader()
-{
-    // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
-    std::string magic = "dyld_v1";
-    magic.append(15 - magic.length() - strlen(_options.archs->name()), ' ');
-    magic.append(_options.archs->name());
-    assert(magic.length() == 15);
-
-    // 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->imagesCount          = (uint32_t)_sortedDylibs.size() + _aliasCount;
-    dyldCacheHeader->dyldBaseAddress      = 0;
-    dyldCacheHeader->codeSignatureOffset  = 0;
-    dyldCacheHeader->codeSignatureSize    = 0;
-    dyldCacheHeader->slideInfoOffset      = _slideInfoFileOffset;
-    dyldCacheHeader->slideInfoSize        = _slideInfoBufferSizeAllocated;
-    dyldCacheHeader->localSymbolsOffset   = 0;
-    dyldCacheHeader->localSymbolsSize     = 0;
-    dyldCacheHeader->cacheType            = _options.optimizeStubs ? kDyldSharedCacheTypeProduction : kDyldSharedCacheTypeDevelopment;
-    dyldCacheHeader->accelerateInfoAddr   = 0;
-    dyldCacheHeader->accelerateInfoSize   = 0;
-    bzero(dyldCacheHeader->uuid, 16);// overwritten later by recomputeCacheUUID()
-    dyldCacheHeader->branchPoolsOffset    = 0;
-    dyldCacheHeader->branchPoolsCount     = 0;
-    dyldCacheHeader->imagesTextOffset     = dyldCacheHeader->imagesOffset + sizeof(dyld_cache_image_info)*dyldCacheHeader->imagesCount;
-    dyldCacheHeader->imagesTextCount      = _sortedDylibs.size();
-    dyldCacheHeader->patchInfoAddr        = 0;
-    dyldCacheHeader->patchInfoSize        = 0;
-    dyldCacheHeader->otherImageGroupAddrUnused  = 0;
-    dyldCacheHeader->otherImageGroupSizeUnused  = 0;
-    dyldCacheHeader->progClosuresAddr     = 0;
-    dyldCacheHeader->progClosuresSize     = 0;
-    dyldCacheHeader->progClosuresTrieAddr = 0;
-    dyldCacheHeader->progClosuresTrieSize = 0;
-    dyldCacheHeader->platform             = (uint8_t)_options.platform;
-    dyldCacheHeader->formatVersion        = dyld3::closure::kFormatVersion;
-    dyldCacheHeader->dylibsExpectedOnDisk = !_options.dylibsRemovedDuringMastering;
-    dyldCacheHeader->simulator            = _options.forSimulator;
-    dyldCacheHeader->locallyBuiltCache    = _options.isLocallyBuiltCache;
-    dyldCacheHeader->builtFromChainedFixups= (strcmp(_options.archs->name(), "arm64e") == 0); // FIXME
-    dyldCacheHeader->formatVersion        = dyld3::closure::kFormatVersion;
-    dyldCacheHeader->sharedRegionStart    = _archLayout->sharedMemoryStart;
-    dyldCacheHeader->sharedRegionSize     = _archLayout->sharedMemorySize;
-
-   // fill in mappings
-    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCacheHeader->mappingOffset);
-    mappings[0].address    = _readExecuteRegion.unslidLoadAddress;
-    mappings[0].fileOffset = 0;
-    mappings[0].size       = _readExecuteRegion.sizeInUse;
-    mappings[0].maxProt    = VM_PROT_READ | VM_PROT_EXECUTE;
-    mappings[0].initProt   = VM_PROT_READ | VM_PROT_EXECUTE;
-    mappings[1].address    = _readWriteRegion.unslidLoadAddress;
-    mappings[1].fileOffset = _readExecuteRegion.sizeInUse;
-    mappings[1].size       = _readWriteRegion.sizeInUse;
-    mappings[1].maxProt    = VM_PROT_READ | VM_PROT_WRITE;
-    mappings[1].initProt   = VM_PROT_READ | VM_PROT_WRITE;
-    mappings[2].address    = _readOnlyRegion.unslidLoadAddress;
-    mappings[2].fileOffset = _readExecuteRegion.sizeInUse + _readWriteRegion.sizeInUse;
-    mappings[2].size       = _readOnlyRegion.sizeInUse;
-    mappings[2].maxProt    = VM_PROT_READ;
-    mappings[2].initProt   = VM_PROT_READ;
-
-    // fill in image table
-    dyld_cache_image_info* images = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
-    for (const DylibInfo& dylib : _sortedDylibs) {
-        const char* installName = dylib.input->mappedFile.mh->installName();
-        images->address = dylib.cacheLocation[0].dstCacheUnslidAddress;
-        if ( _options.dylibsRemovedDuringMastering ) {
-            images->modTime = 0;
-            images->inode   = pathHash(installName);
-        }
-        else {
-            images->modTime = dylib.input->mappedFile.modTime;
-            images->inode   = dylib.input->mappedFile.inode;
-        }
-        uint32_t installNameOffsetInTEXT =  (uint32_t)(installName - (char*)dylib.input->mappedFile.mh);
-        images->pathFileOffset = (uint32_t)dylib.cacheLocation[0].dstCacheFileOffset + installNameOffsetInTEXT;
-        ++images;
-    }
-    // append aliases image records and strings
-/*
-    for (auto &dylib : _dylibs) {
-        if (!dylib->installNameAliases.empty()) {
-            for (const std::string& alias : dylib->installNameAliases) {
-                images->set_address(_segmentMap[dylib][0].address);
-                if (_manifest.platform() == "osx") {
-                    images->modTime = dylib->lastModTime;
-                    images->inode = dylib->inode;
-                }
-                else {
-                    images->modTime = 0;
-                    images->inode = pathHash(alias.c_str());
-                }
-                images->pathFileOffset = offset;
-                //fprintf(stderr, "adding alias %s for %s\n", alias.c_str(), dylib->installName.c_str());
-                ::strcpy((char*)&_buffer[offset], alias.c_str());
-                offset += alias.size() + 1;
-                ++images;
-            }
-        }
-    }
-*/
-    // calculate start of text image array and trailing string pool
-    dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesTextOffset);
-    uint32_t stringOffset = (uint32_t)(dyldCacheHeader->imagesTextOffset + sizeof(dyld_cache_image_text_info) * _sortedDylibs.size());
-
-    // write text image array and image names pool at same time
-    for (const DylibInfo& dylib : _sortedDylibs) {
-        dylib.input->mappedFile.mh->getUuid(textImages->uuid);
-        textImages->loadAddress     = dylib.cacheLocation[0].dstCacheUnslidAddress;
-        textImages->textSegmentSize = (uint32_t)dylib.cacheLocation[0].dstCacheSegmentSize;
-        textImages->pathOffset      = stringOffset;
-        const char* installName = dylib.input->mappedFile.mh->installName();
-        ::strcpy((char*)_readExecuteRegion.buffer + stringOffset, installName);
-        stringOffset += (uint32_t)strlen(installName)+1;
-        ++textImages;
-    }
-
-    // make sure header did not overflow into first mapped image
-    const dyld_cache_image_info* firstImage = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
-    assert(stringOffset <= (firstImage->address - mappings[0].address));
-}
-
-void CacheBuilder::copyRawSegments()
-{
-    const bool log = false;
-    dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
-        const DylibInfo& dylib = _sortedDylibs[index];
-        for (const SegmentMappingInfo& info : dylib.cacheLocation) {
-            if (log) fprintf(stderr, "copy %s segment %s (0x%08X bytes) from %p to %p (logical addr 0x%llX) for %s\n",
-                             _options.archs->name(), info.segName, info.copySegmentSize, info.srcSegment, info.dstSegment, info.dstCacheUnslidAddress, dylib.input->mappedFile.runtimePath.c_str());
-            ::memcpy(info.dstSegment, info.srcSegment, info.copySegmentSize);
-        }
-    });
-
-    // Copy the coalesced 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]);
-        if (log) fprintf(stderr, "copy %s __TEXT_COAL section %s (0x%08X bytes) to %p (logical addr 0x%llX)\n",
-                         _options.archs->name(), CacheCoalescedText::SupportedSections[index],
-                         cacheStringSection.bufferSize, cacheStringSection.bufferAddr, cacheStringSection.bufferVMAddr);
-        for (const auto& stringAndOffset : cacheStringSection.stringsToOffsets)
-            ::memcpy(cacheStringSection.bufferAddr + stringAndOffset.second, stringAndOffset.first.data(), stringAndOffset.first.size() + 1);
-    });
-}
-
-void CacheBuilder::adjustAllImagesForNewSegmentLocations()
-{
-    __block std::vector<Diagnostics> diags;
-    diags.resize(_sortedDylibs.size());
-
-    if (_options.platform == dyld3::Platform::macOS) {
-        dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
-            const DylibInfo& dylib = _sortedDylibs[index];
-            adjustDylibSegments(dylib, diags[index]);
-        });
-    } else {
-        // Note this has to be done in serial because the LOH Tracker isn't thread safe
-        for (size_t index = 0; index != _sortedDylibs.size(); ++index) {
-            const DylibInfo& dylib = _sortedDylibs[index];
-            adjustDylibSegments(dylib, diags[index]);
-        }
-    }
-
-    for (const Diagnostics& diag : diags) {
-        if ( diag.hasError() ) {
-            _diagnostics.error("%s", diag.errorMessage().c_str());
-            break;
-        }
-    }
-}
-
-void CacheBuilder::processSelectorStrings(const std::vector<LoadedMachO>& executables) {
-    const bool log = false;
-
-    _selectorStringsFromExecutables = 0;
-
-    // Don't do this optimisation on watchOS where the shared cache is too small
-    if (_options.platform == dyld3::Platform::watchOS)
-        return;
-
-    // Get the method name coalesced section as that is where we need to put these strings
-    CacheBuilder::CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData("__objc_methname");
-    for (const LoadedMachO& executable : executables) {
-        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)executable.loadedFileInfo.fileContent;
-
-        uint64_t sizeBeforeProcessing = cacheStringSection.bufferSize;
-
-        ma->forEachObjCMethodName(^(const char *methodName) {
-            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 (log) printf("Selector: %s -> %s\n", ma->installName(), methodName);
-                ++_selectorStringsFromExecutables;
-            }
-        });
-
-        uint64_t sizeAfterProcessing = cacheStringSection.bufferSize;
-        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);
-}
-
-void CacheBuilder::parseCoalescableSegments() {
-    const bool log = false;
-
-    for (DylibInfo& dylib : _sortedDylibs)
-        _coalescedText.parseCoalescableText(dylib.input->mappedFile.mh, dylib.textCoalescer);
-
-    if (log) {
-        for (const char* section : CacheCoalescedText::SupportedSections) {
-            CacheCoalescedText::StringSection& sectionData = _coalescedText.getSectionData(section);
-            printf("Coalesced %s from % 10lld -> % 10d, saving % 10lld bytes\n", section,
-                   sectionData.bufferSize + sectionData.savedSpace, sectionData.bufferSize, sectionData.savedSpace);
-        }
-    }
-}
-
-void CacheBuilder::assignSegmentAddresses()
-{
-    // calculate size of header info and where first dylib's mach_header should start
-    size_t startOffset = sizeof(dyld_cache_header) + 3*sizeof(dyld_cache_mapping_info);
-    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);
-
-    // 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)
-                    return;
-                if ( dylib.textCoalescer.sectionWasCoalesced(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             = _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) {
-            __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 - _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)(_readExecuteRegion.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;
-            });
-        }
-    }
-
-    // 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);
-    _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 = _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;
-        });
-    }
-
-    // layout all __DATA segments (and other r/w non-dirty, non-const) segments
-    for (DylibInfo& dylib : _sortedDylibs) {
-        __block uint64_t textSegVmAddr = 0;
-       dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
-            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
-                textSegVmAddr = segInfo.vmAddr;
-            if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
-                return;
-            if ( _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;
-        });
-    }
-
-    // layout all __DATA_DIRTY segments, sorted (FIXME)
-       const size_t dylibCount = _sortedDylibs.size();
-    uint32_t dirtyDataSortIndexes[dylibCount];
-    for (size_t i=0; i < dylibCount; ++i)
-        dirtyDataSortIndexes[i] = (uint32_t)i;
-    std::sort(&dirtyDataSortIndexes[0], &dirtyDataSortIndexes[dylibCount], [&](const uint32_t& a, const uint32_t& b) {
-        const auto& orderA = _options.dirtyDataSegmentOrdering.find(_sortedDylibs[a].input->mappedFile.runtimePath);
-        const auto& orderB = _options.dirtyDataSegmentOrdering.find(_sortedDylibs[b].input->mappedFile.runtimePath);
-        bool foundA = (orderA != _options.dirtyDataSegmentOrdering.end());
-        bool foundB = (orderB != _options.dirtyDataSegmentOrdering.end());
-
-        // Order all __DATA_DIRTY segments specified in the order file first, in the order specified in the file,
-        // followed by any other __DATA_DIRTY segments in lexicographic order.
-        if ( foundA && foundB )
-            return orderA->second < orderB->second;
-        else if ( foundA )
-            return true;
-        else if ( foundB )
-             return false;
-        else
-             return _sortedDylibs[a].input->mappedFile.runtimePath < _sortedDylibs[b].input->mappedFile.runtimePath;
-    });
-    addr = align(addr, 12);
-    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)
-                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 __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;
-            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;
-        });
-    }
-
-    // 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;
-
-    // align DATA region end
-    uint64_t endDataAddress = align(addr, _archLayout->sharedRegionAlignP2);
-    _readWriteRegion.bufferSize   = endDataAddress - _readWriteRegion.unslidLoadAddress;
-    _readWriteRegion.sizeInUse    = _readWriteRegion.bufferSize;
-
-    // start read-only region
-    if ( _archLayout->sharedRegionsAreDiscontiguous )
-        addr = _archLayout->sharedMemoryStart + 0xA0000000;
-    else
-        addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2);
-    _readOnlyRegion.buffer               = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart;
-    _readOnlyRegion.bufferSize           = 0;
-    _readOnlyRegion.sizeInUse            = 0;
-    _readOnlyRegion.unslidLoadAddress    = addr;
-    _readOnlyRegion.cacheFileOffset      = _readWriteRegion.cacheFileOffset + _readWriteRegion.sizeInUse;
-
-    // reserve space for kernel ASLR slide info at start of r/o region
-    if ( _options.cacheSupportsASLR ) {
-        size_t slideInfoSize = sizeof(dyld_cache_slide_info);
-        slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info2));
-        slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info3));
-        slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info4));
-        _slideInfoBufferSizeAllocated = align(slideInfoSize + (_readWriteRegion.sizeInUse/4096) * _archLayout->slideInfoBytesPerPage, _archLayout->sharedRegionAlignP2);
-        _slideInfoFileOffset = _readOnlyRegion.cacheFileOffset;
-        addr += _slideInfoBufferSizeAllocated;
-    }
-
-    // layout all read-only (but not LINKEDIT) segments
-    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;
-
-                // 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;
-    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));
-            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
-            uint64_t offsetInRegion = addr - _readOnlyRegion.unslidLoadAddress;
-            SegmentMappingInfo loc;
-            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
-            loc.segName                = segInfo.segName;
-            loc.dstSegment             = _readOnlyRegion.buffer + offsetInRegion;
-            loc.dstCacheUnslidAddress  = addr;
-            loc.dstCacheFileOffset     = (uint32_t)(_readOnlyRegion.cacheFileOffset + offsetInRegion);
-            loc.dstCacheSegmentSize    = (uint32_t)align(segInfo.sizeOfSections, 12);
-            loc.dstCacheFileSize       = (uint32_t)copySize;
-            loc.copySegmentSize        = (uint32_t)copySize;
-            loc.srcSegmentIndex        = segInfo.segIndex;
-            dylib.cacheLocation.push_back(loc);
-            addr += loc.dstCacheSegmentSize;
-        });
-    }
-
-    // align r/o region end
-    uint64_t endReadOnlyAddress = align(addr, _archLayout->sharedRegionAlignP2);
-    _readOnlyRegion.bufferSize  = endReadOnlyAddress - _readOnlyRegion.unslidLoadAddress;
-    _readOnlyRegion.sizeInUse   = _readOnlyRegion.bufferSize;
-
-    //fprintf(stderr, "RX region=%p -> %p, logical addr=0x%llX\n", _readExecuteRegion.buffer, _readExecuteRegion.buffer+_readExecuteRegion.bufferSize, _readExecuteRegion.unslidLoadAddress);
-    //fprintf(stderr, "RW region=%p -> %p, logical addr=0x%llX\n", _readWriteRegion.buffer,   _readWriteRegion.buffer+_readWriteRegion.bufferSize, _readWriteRegion.unslidLoadAddress);
-    //fprintf(stderr, "RO region=%p -> %p, logical addr=0x%llX\n", _readOnlyRegion.buffer,    _readOnlyRegion.buffer+_readOnlyRegion.bufferSize, _readOnlyRegion.unslidLoadAddress);
-
-    // sort SegmentMappingInfo for each image to be in the same order as original segments
-    for (DylibInfo& dylib : _sortedDylibs) {
-        std::sort(dylib.cacheLocation.begin(), dylib.cacheLocation.end(), [&](const SegmentMappingInfo& a, const SegmentMappingInfo& b) {
-            return a.srcSegmentIndex < b.srcSegmentIndex;
-        });
-    }
-}
-
-void CacheBuilder::markPaddingInaccessible()
-{
-    // region between RX and RW
-    uint8_t* startPad1 = _readExecuteRegion.buffer+_readExecuteRegion.sizeInUse;
-    uint8_t* endPad1   = _readWriteRegion.buffer;
-    ::vm_protect(mach_task_self(), (vm_address_t)startPad1, endPad1-startPad1, false, 0);
-
-    // region between RW and RO
-    uint8_t* startPad2 = _readWriteRegion.buffer+_readWriteRegion.sizeInUse;
-    uint8_t* endPad2   = _readOnlyRegion.buffer;
-    ::vm_protect(mach_task_self(), (vm_address_t)startPad2, endPad2-startPad2, false, 0);
-}
-
-
-uint64_t CacheBuilder::pathHash(const char* path)
-{
-    uint64_t sum = 0;
-    for (const char* s=path; *s != '\0'; ++s)
-        sum += sum*4 + *s;
-    return sum;
-}
-
-
-void CacheBuilder::findDylibAndSegment(const void* contentPtr, std::string& foundDylibName, std::string& foundSegName)
-{
-    foundDylibName = "???";
-    foundSegName   = "???";
-    uint64_t unslidVmAddr = ((uint8_t*)contentPtr - _readExecuteRegion.buffer) + _readExecuteRegion.unslidLoadAddress;
-    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    cache->forEachImage(^(const mach_header* mh, const char* installName) {
-        ((dyld3::MachOLoaded*)mh)->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool &stop) {
-            if ( (unslidVmAddr >= info.vmAddr) && (unslidVmAddr < (info.vmAddr+info.vmSize)) ) {
-                foundDylibName = installName;
-                foundSegName   = info.segName;
-                stop           = true;
-            }
-        });
-    });
-}
-
-
-template <typename P>
-bool CacheBuilder::makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info2* info)
-{
-    typedef typename P::uint_t     pint_t;
-
-    const pint_t   deltaMask    = (pint_t)(info->delta_mask);
-    const pint_t   valueMask    = ~deltaMask;
-    const pint_t   valueAdd     = (pint_t)(info->value_add);
-    const unsigned deltaShift   = __builtin_ctzll(deltaMask) - 2;
-    const uint32_t maxDelta     = (uint32_t)(deltaMask >> deltaShift);
-
-    pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0];
-    pint_t lastValue = (pint_t)P::getP(*lastLoc);
-    if ( (lastValue - valueAdd) & deltaMask ) {
-        std::string dylibName;
-        std::string segName;
-        findDylibAndSegment((void*)pageContent, dylibName, segName);
-        _diagnostics.error("rebase pointer (0x%0lX) does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
-                            (long)lastValue, lastLocationOffset, segName.c_str(), dylibName.c_str());
-        return false;
-    }
-    if ( offset <= (lastLocationOffset+maxDelta) ) {
-        // previous location in range, make link from it
-        // encode this location into last value
-        pint_t delta = offset - lastLocationOffset;
-        pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift);
-        //warning("  add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
-        //                    offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
-        P::setP(*lastLoc, newLastValue);
-        return true;
-    }
-    //fprintf(stderr, "  too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
-
-    // distance between rebase locations is too far
-    // see if we can make a chain from non-rebase locations
-    uint16_t nonRebaseLocationOffsets[1024];
-    unsigned nrIndex = 0;
-    for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) {
-        nonRebaseLocationOffsets[nrIndex] = 0;
-        for (int j=maxDelta; j > 0; j -= 4) {
-            pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]);
-            if ( value == 0 ) {
-                // Steal values of 0 to be used in the rebase chain
-                nonRebaseLocationOffsets[nrIndex] = i+j;
-                break;
-            }
-        }
-        if ( nonRebaseLocationOffsets[nrIndex] == 0 ) {
-            lastValue = (pint_t)P::getP(*lastLoc);
-            pint_t newValue = ((lastValue - valueAdd) & valueMask);
-            //warning("   no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX", lastLocationOffset, (long)value, (long)newValue);
-            P::setP(*lastLoc, newValue);
-            return false;
-        }
-        i = nonRebaseLocationOffsets[nrIndex];
-        ++nrIndex;
-    }
-
-    // we can make chain. go back and add each non-rebase location to chain
-    uint16_t prevOffset = lastLocationOffset;
-    pint_t* prevLoc = (pint_t*)&pageContent[prevOffset];
-    for (unsigned n=0; n < nrIndex; ++n) {
-        uint16_t nOffset = nonRebaseLocationOffsets[n];
-        assert(nOffset != 0);
-        pint_t* nLoc = (pint_t*)&pageContent[nOffset];
-        uint32_t delta2 = nOffset - prevOffset;
-        pint_t value = (pint_t)P::getP(*prevLoc);
-        pint_t newValue;
-        if ( value == 0 )
-            newValue = (delta2 << deltaShift);
-        else
-            newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift);
-        //warning("    non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
-        P::setP(*prevLoc, newValue);
-        prevOffset = nOffset;
-        prevLoc = nLoc;
-    }
-    uint32_t delta3 = offset - prevOffset;
-    pint_t value = (pint_t)P::getP(*prevLoc);
-    pint_t newValue;
-    if ( value == 0 )
-        newValue = (delta3 << deltaShift);
-    else
-        newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift);
-    //warning("    non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
-    P::setP(*prevLoc, newValue);
-
-    return true;
-}
-
-
-template <typename P>
-void CacheBuilder::addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info,
-                                std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
-{
-    typedef typename P::uint_t     pint_t;
-
-    const pint_t   deltaMask    = (pint_t)(info->delta_mask);
-    const pint_t   valueMask    = ~deltaMask;
-    const uint32_t pageSize     = info->page_size;
-    const pint_t   valueAdd     = (pint_t)(info->value_add);
-
-    uint16_t startValue = DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE;
-    uint16_t lastLocationOffset = 0xFFFF;
-    for(uint32_t i=0; i < pageSize/4; ++i) {
-        unsigned offset = i*4;
-        if ( bitmap[i] ) {
-            if ( startValue == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
-                // found first rebase location in page
-                startValue = i;
-            }
-            else if ( !makeRebaseChainV2<P>(pageContent, lastLocationOffset, offset, info) ) {
-                // can't record all rebasings in one chain
-                if ( (startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 ) {
-                    // switch page_start to "extras" which is a list of chain starts
-                    unsigned indexInExtras = (unsigned)pageExtras.size();
-                    if ( indexInExtras > 0x3FFF ) {
-                        _diagnostics.error("rebase overflow in v2 page extras");
-                        return;
-                    }
-                    pageExtras.push_back(startValue);
-                    startValue = indexInExtras | DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA;
-                }
-                pageExtras.push_back(i);
-            }
-            lastLocationOffset = offset;
-        }
-    }
-    if ( lastLocationOffset != 0xFFFF ) {
-        // mark end of chain
-        pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset];
-        pint_t lastValue = (pint_t)P::getP(*lastLoc);
-        pint_t newValue = ((lastValue - valueAdd) & valueMask);
-        P::setP(*lastLoc, newValue);
-    }
-    if ( startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
-        // add end bit to extras
-        pageExtras.back() |= DYLD_CACHE_SLIDE_PAGE_ATTR_END;
-    }
-    pageStarts.push_back(startValue);
-}
-
-template <typename P>
-void CacheBuilder::writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount)
-{
-    typedef typename P::uint_t    pint_t;
-    typedef typename P::E         E;
-    const uint32_t pageSize = 4096;
-
-    // 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;
-        }
-        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());
-}
-
-#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
-// fits in to int16_t
-static bool smallValue(uint64_t value)
-{
-    uint32_t high = (value & 0xFFFF8000);
-    return (high == 0) || (high == 0xFFFF8000);
-}
-
-template <typename P>
-bool CacheBuilder::makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info4* info)
-{
-    typedef typename P::uint_t     pint_t;
-
-    const pint_t   deltaMask    = (pint_t)(info->delta_mask);
-    const pint_t   valueMask    = ~deltaMask;
-    const pint_t   valueAdd     = (pint_t)(info->value_add);
-    const unsigned deltaShift   = __builtin_ctzll(deltaMask) - 2;
-    const uint32_t maxDelta     = (uint32_t)(deltaMask >> deltaShift);
-
-    pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0];
-    pint_t lastValue = (pint_t)P::getP(*lastLoc);
-    if ( (lastValue - valueAdd) & deltaMask ) {
-        std::string dylibName;
-        std::string segName;
-        findDylibAndSegment((void*)pageContent, dylibName, segName);
-        _diagnostics.error("rebase pointer does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
-                            lastLocationOffset, segName.c_str(), dylibName.c_str());
-        return false;
-    }
-    if ( offset <= (lastLocationOffset+maxDelta) ) {
-        // previous location in range, make link from it
-        // encode this location into last value
-        pint_t delta = offset - lastLocationOffset;
-        pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift);
-        //warning("  add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
-        //                    offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
-        P::setP(*lastLoc, newLastValue);
-        return true;
-    }
-    //fprintf(stderr, "  too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
-
-    // distance between rebase locations is too far
-    // see if we can make a chain from non-rebase locations
-    uint16_t nonRebaseLocationOffsets[1024];
-    unsigned nrIndex = 0;
-    for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) {
-        nonRebaseLocationOffsets[nrIndex] = 0;
-        for (int j=maxDelta; j > 0; j -= 4) {
-            pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]);
-            if ( smallValue(value) ) {
-                // Steal values of 0 to be used in the rebase chain
-                nonRebaseLocationOffsets[nrIndex] = i+j;
-                break;
-            }
-        }
-        if ( nonRebaseLocationOffsets[nrIndex] == 0 ) {
-            lastValue = (pint_t)P::getP(*lastLoc);
-            pint_t newValue = ((lastValue - valueAdd) & valueMask);
-            //fprintf(stderr, "   no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX\n",
-            //                lastLocationOffset, (long)lastValue, (long)newValue);
-            P::setP(*lastLoc, newValue);
-            return false;
-        }
-        i = nonRebaseLocationOffsets[nrIndex];
-        ++nrIndex;
-    }
-
-    // we can make chain. go back and add each non-rebase location to chain
-    uint16_t prevOffset = lastLocationOffset;
-    pint_t* prevLoc = (pint_t*)&pageContent[prevOffset];
-    for (unsigned n=0; n < nrIndex; ++n) {
-        uint16_t nOffset = nonRebaseLocationOffsets[n];
-        assert(nOffset != 0);
-        pint_t* nLoc = (pint_t*)&pageContent[nOffset];
-        uint32_t delta2 = nOffset - prevOffset;
-        pint_t value = (pint_t)P::getP(*prevLoc);
-        pint_t newValue;
-        if ( smallValue(value) )
-            newValue = (value & valueMask) | (delta2 << deltaShift);
-        else
-            newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift);
-        //warning("    non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
-        P::setP(*prevLoc, newValue);
-        prevOffset = nOffset;
-        prevLoc = nLoc;
-    }
-    uint32_t delta3 = offset - prevOffset;
-    pint_t value = (pint_t)P::getP(*prevLoc);
-    pint_t newValue;
-    if ( smallValue(value) )
-        newValue = (value & valueMask) | (delta3 << deltaShift);
-    else
-        newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift);
-    //warning("    non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
-    P::setP(*prevLoc, newValue);
-
-    return true;
-}
-
-
-template <typename P>
-void CacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info,
-                                std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
-{
-    typedef typename P::uint_t     pint_t;
-
-    const pint_t   deltaMask    = (pint_t)(info->delta_mask);
-    const pint_t   valueMask    = ~deltaMask;
-    const uint32_t pageSize     = info->page_size;
-    const pint_t   valueAdd     = (pint_t)(info->value_add);
-
-    uint16_t startValue = DYLD_CACHE_SLIDE4_PAGE_NO_REBASE;
-    uint16_t lastLocationOffset = 0xFFFF;
-    for(uint32_t i=0; i < pageSize/4; ++i) {
-        unsigned offset = i*4;
-        if ( bitmap[i] ) {
-            if ( startValue == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
-                // found first rebase location in page
-                startValue = i;
-            }
-            else if ( !makeRebaseChainV4<P>(pageContent, lastLocationOffset, offset, info) ) {
-                // can't record all rebasings in one chain
-                if ( (startValue & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) == 0 ) {
-                    // switch page_start to "extras" which is a list of chain starts
-                    unsigned indexInExtras = (unsigned)pageExtras.size();
-                    if ( indexInExtras >= DYLD_CACHE_SLIDE4_PAGE_INDEX ) {
-                        _diagnostics.error("rebase overflow in v4 page extras");
-                        return;
-                    }
-                    pageExtras.push_back(startValue);
-                    startValue = indexInExtras | DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA;
-                }
-                pageExtras.push_back(i);
-            }
-            lastLocationOffset = offset;
-        }
-    }
-    if ( lastLocationOffset != 0xFFFF ) {
-        // mark end of chain
-        pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset];
-        pint_t lastValue = (pint_t)P::getP(*lastLoc);
-        pint_t newValue = ((lastValue - valueAdd) & valueMask);
-        P::setP(*lastLoc, newValue);
-    }
-    if ( startValue & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
-        // add end bit to extras
-        pageExtras.back() |= DYLD_CACHE_SLIDE4_PAGE_EXTRA_END;
-    }
-    pageStarts.push_back(startValue);
-}
-
-
-
-template <typename P>
-void CacheBuilder::writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount)
-{
-    typedef typename P::uint_t    pint_t;
-    typedef typename P::E         E;
-    const uint32_t pageSize = 4096;
-
-    // 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;
-        }
-        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");
-    }
-    ((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());
-}
-#endif
-
-/*
-void CacheBuilder::writeSlideInfoV1()
-{
-    // build one 128-byte bitmap per page (4096) of DATA
-    uint8_t* const dataStart = (uint8_t*)_buffer.get() + regions[1].fileOffset;
-    uint8_t* const dataEnd   = dataStart + regions[1].size;
-    const long bitmapSize = (dataEnd - dataStart)/(4*8);
-    uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1);
-    for (void* p : _pointersForASLR) {
-        if ( (p < dataStart) || ( p > dataEnd) )
-            terminate("DATA pointer for sliding, out of range\n");
-        long offset = (long)((uint8_t*)p - dataStart);
-        if ( (offset % 4) != 0 )
-            terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset);
-        long byteIndex = offset / (4*8);
-        long bitInByte =  (offset % 32) >> 2;
-        bitmap[byteIndex] |= (1 << bitInByte);
-    }
-
-    // allocate worst case size block of all slide info
-    const unsigned entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes.
-    const unsigned toc_count = (unsigned)bitmapSize/entry_size;
-    dyld_cache_slide_info* slideInfo = (dyld_cache_slide_info*)((uint8_t*)_buffer + _slideInfoFileOffset);
-    slideInfo->version          = 1;
-    slideInfo->toc_offset       = sizeof(dyld_cache_slide_info);
-    slideInfo->toc_count        = toc_count;
-    slideInfo->entries_offset   = (slideInfo->toc_offset+2*toc_count+127)&(-128);
-    slideInfo->entries_count    = 0;
-    slideInfo->entries_size     = entry_size;
-    // append each unique entry
-    const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap;
-    dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset());
-    int entry_count = 0;
-    for (int i=0; i < toc_count; ++i) {
-        const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i];
-        // see if it is same as one already added
-        bool found = false;
-        for (int j=0; j < entry_count; ++j) {
-            if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) {
-                slideInfo->set_toc(i, j);
-                found = true;
-                break;
-            }
-        }
-        if ( !found ) {
-            // append to end
-            memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size);
-            slideInfo->set_toc(i, entry_count++);
-        }
-    }
-    slideInfo->entries_count  = entry_count;
-    ::free((void*)bitmap);
-
-    _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2);
-}
-
-*/
-
-
-
-uint16_t CacheBuilder::pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[])
-{
-    const int maxPerPage = pageSize / 4;
-    uint16_t result = DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE;
-    dyld3::MachOLoaded::ChainedFixupPointerOnDisk* lastLoc = nullptr;
-    for (int i=0; i < maxPerPage; ++i) {
-        if ( bitmap[i] ) {
-            if ( result == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) {
-                // found first rebase location in page
-                result = i * 4;
-            }
-            dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)(pageContent + i*4);;
-            if ( lastLoc != nullptr ) {
-                // update chain (original chain may be wrong because of segment packing)
-                lastLoc->arm64e.rebase.next = loc - lastLoc;
-            }
-            lastLoc = loc;
-        }
-    }
-    if ( lastLoc != nullptr ) {
-        // mark last one as end of chain
-        lastLoc->arm64e.rebase.next = 0;
-       }
-    return result;
-}
-
-
-void CacheBuilder::writeSlideInfoV3(const bool bitmap[], unsigned dataPageCount)
-{
-       const uint32_t pageSize = 4096;
-
-    // fill in fixed info
-    assert(_slideInfoFileOffset != 0);
-    dyld_cache_slide_info3* info = (dyld_cache_slide_info3*)_readOnlyRegion.buffer;
-    info->version           = 3;
-    info->page_size         = pageSize;
-    info->page_starts_count = dataPageCount;
-    info->auth_value_add    = _archLayout->sharedMemoryStart;
-    
-    // fill in per-page starts
-    uint8_t* pageContent = _readWriteRegion.buffer;
-    const bool* bitmapForPage = bitmap;
-    for (unsigned i=0; i < dataPageCount; ++i) {
-        info->page_starts[i] = pageStartV3(pageContent, pageSize, bitmapForPage);
-        pageContent += pageSize;
-        bitmapForPage += (sizeof(bool)*(pageSize/4));
-    }
-
-    // update header with final size
-    dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
-    dyldCacheHeader->slideInfoSize = align(__offsetof(dyld_cache_slide_info3, page_starts[dataPageCount]), _archLayout->sharedRegionAlignP2);
-    if ( dyldCacheHeader->slideInfoSize > _slideInfoBufferSizeAllocated ) {
-        _diagnostics.error("kernel slide info overflow buffer");
-    }
-}
-
-
-void CacheBuilder::fipsSign()
-{
-    // find libcorecrypto.dylib in cache being built
-    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    __block const dyld3::MachOLoaded* ml = nullptr;
-    dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
-        if ( strcmp(installName, "/usr/lib/system/libcorecrypto.dylib") == 0 )
-            ml = (dyld3::MachOLoaded*)mh;
-    });
-    if ( ml == nullptr ) {
-        _diagnostics.warning("Could not find libcorecrypto.dylib, skipping FIPS sealing");
-        return;
-    }
-
-    // find location in libcorecrypto.dylib to store hash of __text section
-    uint64_t hashStoreSize;
-    const void* hashStoreLocation = ml->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize);
-    if ( hashStoreLocation == nullptr ) {
-        _diagnostics.warning("Could not find __TEXT/__fips_hmacs section in libcorecrypto.dylib, skipping FIPS sealing");
-        return;
-    }
-    if ( hashStoreSize != 32 ) {
-        _diagnostics.warning("__TEXT/__fips_hmacs section in libcorecrypto.dylib is not 32 bytes in size, skipping FIPS sealing");
-        return;
-    }
-
-    // compute hmac hash of __text section
-    uint64_t textSize;
-    const void* textLocation = ml->findSectionContent("__TEXT", "__text", textSize);
-    if ( textLocation == nullptr ) {
-        _diagnostics.warning("Could not find __TEXT/__text section in libcorecrypto.dylib, skipping FIPS sealing");
-        return;
-    }
-    unsigned char hmac_key = 0;
-    CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, textLocation, textSize, (void*)hashStoreLocation); // store hash directly into hashStoreLocation
-}
-
-void CacheBuilder::codeSign()
-{
-    uint8_t  dscHashType;
-    uint8_t  dscHashSize;
-    uint32_t dscDigestFormat;
-    bool agile = false;
-
-    // select which codesigning hash
-    switch (_options.codeSigningDigestMode) {
-        case DyldSharedCache::Agile:
-            agile = true;
-            // Fall through to SHA1, because the main code directory remains SHA1 for compatibility.
-            [[clang::fallthrough]];
-        case DyldSharedCache::SHA1only:
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-            dscHashType     = CS_HASHTYPE_SHA1;
-            dscHashSize     = CS_HASH_SIZE_SHA1;
-            dscDigestFormat = kCCDigestSHA1;
-#pragma clang diagnostic pop
-            break;
-        case DyldSharedCache::SHA256only:
-            dscHashType     = CS_HASHTYPE_SHA256;
-            dscHashSize     = CS_HASH_SIZE_SHA256;
-            dscDigestFormat = kCCDigestSHA256;
-            break;
-        default:
-            _diagnostics.error("codeSigningDigestMode has unknown, unexpected value %d, bailing out.",
-                               _options.codeSigningDigestMode);
-            return;
-    }
-
-    std::string cacheIdentifier = "com.apple.dyld.cache.";
-    cacheIdentifier +=  _options.archs->name();
-    if ( _options.dylibsRemovedDuringMastering ) {
-        if ( _options.optimizeStubs  )
-            cacheIdentifier +=  ".release";
-        else
-            cacheIdentifier += ".development";
-    }
-    // get pointers into shared cache buffer
-    size_t          inBbufferSize = _readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse;
-
-    const uint16_t pageSize = _archLayout->csPageSize;
-
-    // layout code signature contents
-    uint32_t blobCount     = agile ? 4 : 3;
-    size_t   idSize        = cacheIdentifier.size()+1; // +1 for terminating 0
-    uint32_t slotCount     = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize);
-    uint32_t xSlotCount    = CSSLOT_REQUIREMENTS;
-    size_t   idOffset      = offsetof(CS_CodeDirectory, end_withExecSeg);
-    size_t   hashOffset    = idOffset+idSize + dscHashSize*xSlotCount;
-    size_t   hash256Offset = idOffset+idSize + CS_HASH_SIZE_SHA256*xSlotCount;
-    size_t   cdSize        = hashOffset + (slotCount * dscHashSize);
-    size_t   cd256Size     = agile ? hash256Offset + (slotCount * CS_HASH_SIZE_SHA256) : 0;
-    size_t   reqsSize      = 12;
-    size_t   cmsSize       = sizeof(CS_Blob);
-    size_t   cdOffset      = sizeof(CS_SuperBlob) + blobCount*sizeof(CS_BlobIndex);
-    size_t   cd256Offset   = cdOffset + cdSize;
-    size_t   reqsOffset    = cd256Offset + cd256Size; // equals cdOffset + cdSize if not agile
-    size_t   cmsOffset     = reqsOffset + reqsSize;
-    size_t   sbSize        = cmsOffset + cmsSize;
-    size_t   sigSize       = align(sbSize, 14);       // keep whole cache 16KB aligned
-
-    // allocate space for blob
-    vm_address_t codeSigAlloc;
-    if ( vm_allocate(mach_task_self(), &codeSigAlloc, sigSize, VM_FLAGS_ANYWHERE) != 0 ) {
-        _diagnostics.error("could not allocate code signature buffer");
-        return;
-    }
-    _codeSignatureRegion.buffer     = (uint8_t*)codeSigAlloc;
-    _codeSignatureRegion.bufferSize = sigSize;
-    _codeSignatureRegion.sizeInUse  = sigSize;
-
-    // create overall code signature which is a superblob
-    CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(_codeSignatureRegion.buffer);
-    sb->magic           = htonl(CSMAGIC_EMBEDDED_SIGNATURE);
-    sb->length          = htonl(sbSize);
-    sb->count           = htonl(blobCount);
-    sb->index[0].type   = htonl(CSSLOT_CODEDIRECTORY);
-    sb->index[0].offset = htonl(cdOffset);
-    sb->index[1].type   = htonl(CSSLOT_REQUIREMENTS);
-    sb->index[1].offset = htonl(reqsOffset);
-    sb->index[2].type   = htonl(CSSLOT_CMS_SIGNATURE);
-    sb->index[2].offset = htonl(cmsOffset);
-    if ( agile ) {
-        sb->index[3].type = htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES + 0);
-        sb->index[3].offset = htonl(cd256Offset);
-    }
-
-    // fill in empty requirements
-    CS_RequirementsBlob* reqs = (CS_RequirementsBlob*)(((char*)sb)+reqsOffset);
-    reqs->magic  = htonl(CSMAGIC_REQUIREMENTS);
-    reqs->length = htonl(sizeof(CS_RequirementsBlob));
-    reqs->data   = 0;
-
-    // initialize fixed fields of Code Directory
-    CS_CodeDirectory* cd = (CS_CodeDirectory*)(((char*)sb)+cdOffset);
-    cd->magic           = htonl(CSMAGIC_CODEDIRECTORY);
-    cd->length          = htonl(cdSize);
-    cd->version         = htonl(0x20400);               // supports exec segment
-    cd->flags           = htonl(kSecCodeSignatureAdhoc);
-    cd->hashOffset      = htonl(hashOffset);
-    cd->identOffset     = htonl(idOffset);
-    cd->nSpecialSlots   = htonl(xSlotCount);
-    cd->nCodeSlots      = htonl(slotCount);
-    cd->codeLimit       = htonl(inBbufferSize);
-    cd->hashSize        = dscHashSize;
-    cd->hashType        = dscHashType;
-    cd->platform        = 0;                            // not platform binary
-    cd->pageSize        = __builtin_ctz(pageSize);      // log2(CS_PAGE_SIZE);
-    cd->spare2          = 0;                            // unused (must be zero)
-    cd->scatterOffset   = 0;                            // not supported anymore
-    cd->teamOffset      = 0;                            // no team ID
-    cd->spare3          = 0;                            // unused (must be zero)
-    cd->codeLimit64     = 0;                            // falls back to codeLimit
-
-    // executable segment info
-    cd->execSegBase     = htonll(_readExecuteRegion.cacheFileOffset); // base of TEXT segment
-    cd->execSegLimit    = htonll(_readExecuteRegion.sizeInUse);       // size of TEXT segment
-    cd->execSegFlags    = 0;                                          // not a main binary
-
-    // initialize dynamic fields of Code Directory
-    strcpy((char*)cd + idOffset, cacheIdentifier.c_str());
-
-    // add special slot hashes
-    uint8_t* hashSlot = (uint8_t*)cd + hashOffset;
-    uint8_t* reqsHashSlot = &hashSlot[-CSSLOT_REQUIREMENTS*dscHashSize];
-    CCDigest(dscDigestFormat, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHashSlot);
-
-    CS_CodeDirectory* cd256;
-    uint8_t* hash256Slot;
-    uint8_t* reqsHash256Slot;
-    if ( agile ) {
-        // Note that the assumption here is that the size up to the hashes is the same as for
-        // sha1 code directory, and that they come last, after everything else.
-
-        cd256 = (CS_CodeDirectory*)(((char*)sb)+cd256Offset);
-        cd256->magic           = htonl(CSMAGIC_CODEDIRECTORY);
-        cd256->length          = htonl(cd256Size);
-        cd256->version         = htonl(0x20400);               // supports exec segment
-        cd256->flags           = htonl(kSecCodeSignatureAdhoc);
-        cd256->hashOffset      = htonl(hash256Offset);
-        cd256->identOffset     = htonl(idOffset);
-        cd256->nSpecialSlots   = htonl(xSlotCount);
-        cd256->nCodeSlots      = htonl(slotCount);
-        cd256->codeLimit       = htonl(inBbufferSize);
-        cd256->hashSize        = CS_HASH_SIZE_SHA256;
-        cd256->hashType        = CS_HASHTYPE_SHA256;
-        cd256->platform        = 0;                            // not platform binary
-        cd256->pageSize        = __builtin_ctz(pageSize);      // log2(CS_PAGE_SIZE);
-        cd256->spare2          = 0;                            // unused (must be zero)
-        cd256->scatterOffset   = 0;                            // not supported anymore
-        cd256->teamOffset      = 0;                            // no team ID
-        cd256->spare3          = 0;                            // unused (must be zero)
-        cd256->codeLimit64     = 0;                            // falls back to codeLimit
-
-        // executable segment info
-        cd256->execSegBase     = cd->execSegBase;
-        cd256->execSegLimit    = cd->execSegLimit;
-        cd256->execSegFlags    = cd->execSegFlags;
-
-        // initialize dynamic fields of Code Directory
-        strcpy((char*)cd256 + idOffset, cacheIdentifier.c_str());
-
-        // add special slot hashes
-        hash256Slot = (uint8_t*)cd256 + hash256Offset;
-        reqsHash256Slot = &hash256Slot[-CSSLOT_REQUIREMENTS*CS_HASH_SIZE_SHA256];
-        CCDigest(kCCDigestSHA256, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHash256Slot);
-    }
-    else {
-        cd256 = NULL;
-        hash256Slot = NULL;
-        reqsHash256Slot = NULL;
-    }
-
-    // fill in empty CMS blob for ad-hoc signing
-    CS_Blob* cms = (CS_Blob*)(((char*)sb)+cmsOffset);
-    cms->magic  = htonl(CSMAGIC_BLOBWRAPPER);
-    cms->length = htonl(sizeof(CS_Blob));
-
-
-    // alter header of cache to record size and location of code signature
-    // do this *before* hashing each page
-    dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer;
-    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);
-    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);
-
-        CCDigest(dscDigestFormat, code, pageSize, hashSlot + (i * dscHashSize));
-
-        if ( agile ) {
-            CCDigest(kCCDigestSHA256, code, pageSize, hash256Slot + (i * CS_HASH_SIZE_SHA256));
-        }
-    };
-
-    // compute hashes
-    dispatch_apply(slotCount, DISPATCH_APPLY_AUTO, ^(size_t i) {
-        codeSignPage(i);
-    });
-
-    // Now that we have a code signature, compute a cache UUID by hashing the code signature blob
-    {
-        uint8_t* uuidLoc = cache->uuid;
-        assert(uuid_is_null(uuidLoc));
-        static_assert(offsetof(dyld_cache_header, uuid) / CS_PAGE_SIZE_4K == 0, "uuid is expected in the first page of the cache");
-        uint8_t fullDigest[CC_SHA256_DIGEST_LENGTH];
-        CC_SHA256((const void*)cd, (unsigned)cdSize, fullDigest);
-        memcpy(uuidLoc, fullDigest, 16);
-        // <rdar://problem/6723729> uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
-        uuidLoc[6] = ( uuidLoc[6] & 0x0F ) | ( 3 << 4 );
-        uuidLoc[8] = ( uuidLoc[8] & 0x3F ) | 0x80;
-
-        // Now codesign page 0 again, because we modified it by setting uuid in header
-        codeSignPage(0);
-    }
-
-    // hash of entire code directory (cdHash) uses same hash as each page
-    uint8_t fullCdHash[dscHashSize];
-    CCDigest(dscDigestFormat, (const uint8_t*)cd, cdSize, fullCdHash);
-    // Note: cdHash is defined as first 20 bytes of hash
-    memcpy(_cdHashFirst, fullCdHash, 20);
-    if ( agile ) {
-        uint8_t fullCdHash256[CS_HASH_SIZE_SHA256];
-        CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, cd256Size, fullCdHash256);
-        // Note: cdHash is defined as first 20 bytes of hash, even for sha256
-        memcpy(_cdHashSecond, fullCdHash256, 20);
-    }
-    else {
-        memset(_cdHashSecond, 0, 20);
-    }
-}
-
-const bool CacheBuilder::agileSignature()
-{
-    return _options.codeSigningDigestMode == DyldSharedCache::Agile;
-}
-
-static const std::string cdHash(uint8_t hash[20])
-{
-    char buff[48];
-    for (int i = 0; i < 20; ++i)
-        sprintf(&buff[2*i], "%2.2x", hash[i]);
-    return buff;
-}
-
-const std::string CacheBuilder::cdHashFirst()
-{
-    return cdHash(_cdHashFirst);
-}
+#include "MachOFileAbstraction.hpp"
+#include "DyldSharedCache.h"
+#include "CacheBuilder.h"
+#include "Diagnostics.h"
 
-const std::string CacheBuilder::cdHashSecond()
-{
-    return cdHash(_cdHashSecond);
-}
 
-const std::string CacheBuilder::uuid() const
+CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem)
+    : _options(options)
+    , _fileSystem(fileSystem)
+    , _fullAllocatedBuffer(0)
+    , _diagnostics(options.loggingPrefix, options.verbose)
+    , _allocatedBufferSize(0)
 {
-    dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer;
-    uuid_string_t uuidStr;
-    uuid_unparse(cache->uuid, uuidStr);
-    return uuidStr;
-}
-
-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;
-}
-
-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;
-    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;
-    return patch;
 }
 
 
-void CacheBuilder::buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases)
+std::string CacheBuilder::errorMessage()
 {
-    typedef dyld3::closure::ClosureBuilder::CachedDylibInfo         CachedDylibInfo;
-
-    // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray()
-    __block std::vector<CachedDylibInfo> dylibInfos;
-    __block std::unordered_map<dyld3::closure::ImageNum, const dyld3::MachOLoaded*> imageNumToML;
-    DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    cache->forEachImage(^(const mach_header* mh, const char* installName) {
-        uint64_t mtime;
-        uint64_t inode;
-        cache->getIndexedImageEntry((uint32_t)dylibInfos.size(), mtime, inode);
-        CachedDylibInfo entry;
-        entry.fileInfo.fileContent  = mh;
-        entry.fileInfo.path         = installName;
-        entry.fileInfo.sliceOffset  = 0;
-        entry.fileInfo.inode        = inode;
-        entry.fileInfo.mtime        = mtime;
-        dylibInfos.push_back(entry);
-        imageNumToML[(dyld3::closure::ImageNum)(dylibInfos.size())] = (dyld3::MachOLoaded*)mh;
-    });
-
-    // Convert symlinks from STL to simple char pointers.
-    std::vector<dyld3::closure::ClosureBuilder::CachedDylibAlias> dylibAliases;
-    dylibAliases.reserve(aliases.size());
-    for (const auto& alias : aliases)
-        dylibAliases.push_back({ alias.realPath.c_str(), alias.aliasPath.c_str() });
-
-    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) {
-        imageLoadAddress->forEachFixupInAllChains(_diagnostics, starts, false, ^(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;
-            switch (segInfo->pointer_format) {
-                case DYLD_CHAINED_PTR_ARM64E:
-                    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;
-                                if ( fixupLoc->arm64e.authBind.auth ) {
-                                    // turn this auth bind into an auth rebase into the cache
-                                    fixupLoc->arm64e.authRebase.bind   = 0;
-                                    fixupLoc->arm64e.authRebase.target = target.sharedCache.offset;
-                                    _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend, *fixupLoc));
-                                }
-                                else {
-                                    // turn this plain bind into an plain rebase into the cache
-                                    fixupLoc->arm64e.rebase.bind   = 0;
-                                    fixupLoc->arm64e.rebase.target = _archLayout->sharedMemoryStart + target.sharedCache.offset;
-                                    _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend));
-                                }
-                                _aslrTracker.add(fixupLoc);
-                                break;
-                            case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
-                                fixupLoc->raw64 = 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)(imageLoadAddress->dependentDylibCount())) ) {
-                                    _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1);
-                                }
-                               break;
-                            default:
-                                assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
-                        }
-                    } else {
-                        _aslrTracker.add(fixupLoc);
-                    }
-                    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;
-                                // turn this bind into a rebase into the cache
-                                fixupLoc->generic64.rebase.bind     = 0;
-                                fixupLoc->generic64.rebase.next     = 0; // rechained later
-                                fixupLoc->generic64.rebase.reserved = 0;
-                                fixupLoc->generic64.rebase.high8    = 0;
-                                fixupLoc->generic64.rebase.target   = target.sharedCache.offset;
-                                _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend));
-                                _aslrTracker.add(fixupLoc);
-                                break;
-                            case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
-                                fixupLoc->raw64 = target.absolute.value;
-                                 // don't record absolute targets for ASLR
-                                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");
-                        }
-                    }
-                    break;
-                case DYLD_CHAINED_PTR_32:
-                    if ( fixupLoc->generic32.bind.bind ) {
-                        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;
-                                // turn this bind into a rebase into the cache
-                                fixupLoc->cache32.next   = 0; // rechained later
-                                fixupLoc->cache32.target = (uint32_t)(target.sharedCache.offset);
-                                _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend));
-                                _aslrTracker.add(fixupLoc);
-                                break;
-                            case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
-                                fixupLoc->raw32 = (uint32_t)target.absolute.value;
-                                 // don't record absolute targets for ASLR
-                                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");
-                        }
-                    }
-                   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);
-                }
-                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);
-                }
-                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::Array<CachedDylibInfo> dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size());
-    const dyld3::Array<dyld3::closure::ClosureBuilder::CachedDylibAlias> aliasesArray(dylibAliases.data(), dylibAliases.size(), dylibAliases.size());
-    _imageArray = cb.makeDyldCacheImageArray(_options.optimizeStubs, dylibs, aliasesArray);
-    if ( cb.diagnostics().hasError() ) {
-        _diagnostics.error("%s", cb.diagnostics().errorMessage().c_str());
-        return;
-    }
-}
-
-static bool operator==(const dyld_cache_patchable_location& a, const dyld_cache_patchable_location& b) {
-    return a.cacheOffset == b.cacheOffset;
+    return _diagnostics.errorMessage();
 }
 
-void CacheBuilder::addImageArray()
+void CacheBuilder::copyRawSegments()
 {
-    // build trie of dylib paths
-    __block std::vector<DylibIndexTrie::Entry> dylibEntrys;
-    _imageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
-        dylibEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum()-1)));
-        image->forEachAlias(^(const char *aliasPath, bool &innerStop) {
-            dylibEntrys.push_back(DylibIndexTrie::Entry(aliasPath, DylibIndex(image->imageNum()-1)));
-        });
-    });
-    DylibIndexTrie dylibsTrie(dylibEntrys);
-    std::vector<uint8_t> trieBytes;
-    dylibsTrie.emit(trieBytes);
-    while ( (trieBytes.size() % 4) != 0 )
-        trieBytes.push_back(0);
-
-    // build set of functions to never stub-eliminate because tools may need to override them
-    std::unordered_set<std::string> alwaysGeneratePatch;
-    for (const char* const* p=_s_neverStubEliminateSymbols; *p != nullptr; ++p)
-        alwaysGeneratePatch.insert(*p);
-
-    // Add the patches for the image array.
-    __block uint64_t numPatchImages             = _imageArray->size();
-    __block uint64_t numPatchExports            = 0;
-    __block uint64_t numPatchLocations          = 0;
-    __block uint64_t numPatchExportNameBytes    = 0;
-
-    auto needsPatch = [&](bool dylibNeedsPatching, const dyld3::MachOLoaded* mh,
-                          CacheOffset offset) -> bool {
-        if (dylibNeedsPatching)
-            return true;
-        if (_dylibWeakExports.find({ mh, offset }) != _dylibWeakExports.end())
-            return true;
-        const std::string& exportName = _exportsToName[offset];
-        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) {
-        const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh;
-        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);
-
-        uint64_t numDylibExports = 0;
-        for (CacheOffset exportCacheOffset : dylibExports) {
-            if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset))
-                continue;
-            std::vector<dyld_cache_patchable_location>& uses = _exportsToUses[exportCacheOffset];
-            uses.erase(std::unique(uses.begin(), uses.end()), uses.end());
-            numPatchLocations += uses.size();
-
-            std::string exportName = _exportsToName[exportCacheOffset];
-            numPatchExportNameBytes += exportName.size() + 1;
-        }
-        numPatchExports += numDylibExports;
-    });
-
-    // Now reserve the space
-    __block std::vector<dyld_cache_image_patches>       patchImages;
-    __block std::vector<dyld_cache_patchable_export>    patchExports;
-    __block std::vector<dyld_cache_patchable_location>  patchLocations;
-    __block std::vector<char>                           patchExportNames;
-
-    patchImages.reserve(numPatchImages);
-    patchExports.reserve(numPatchExports);
-    patchLocations.reserve(numPatchLocations);
-    patchExportNames.reserve(numPatchExportNameBytes);
-
-    // And now fill it with the patch data
-    cache->forEachImage(^(const mach_header* mh, const char* installName) {
-        const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh;
-        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);
-
-        // Add the patch image which points in to the exports
-        dyld_cache_image_patches patchImage;
-        patchImage.patchExportsStartIndex   = (uint32_t)patchExports.size();
-        patchImage.patchExportsCount        = 0;
-
-        // Then add each export which points to a list of locations and a name
-        for (CacheOffset exportCacheOffset : dylibExports) {
-            if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset))
-                continue;
-            ++patchImage.patchExportsCount;
-            std::vector<dyld_cache_patchable_location>& uses = _exportsToUses[exportCacheOffset];
-
-            dyld_cache_patchable_export cacheExport;
-            cacheExport.cacheOffsetOfImpl           = (uint32_t)exportCacheOffset;
-            cacheExport.patchLocationsStartIndex    = (uint32_t)patchLocations.size();
-            cacheExport.patchLocationsCount         = (uint32_t)uses.size();
-            cacheExport.exportNameOffset            = (uint32_t)patchExportNames.size();
-            patchExports.push_back(cacheExport);
-
-            // Now add the list of locations.
-            patchLocations.insert(patchLocations.end(), uses.begin(), uses.end());
-
-            // And add the export name
-            const std::string& exportName = _exportsToName[exportCacheOffset];
-            patchExportNames.insert(patchExportNames.end(), &exportName[0], &exportName[0] + exportName.size() + 1);
+    const bool log = false;
+    dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+        const DylibInfo& dylib = _sortedDylibs[index];
+        for (const SegmentMappingInfo& info : dylib.cacheLocation) {
+            if (log) fprintf(stderr, "copy %s segment %s (0x%08X bytes) from %p to %p (logical addr 0x%llX) for %s\n",
+                             _options.archs->name(), info.segName, info.copySegmentSize, info.srcSegment, info.dstSegment, info.dstCacheUnslidAddress, dylib.input->mappedFile.runtimePath.c_str());
+            ::memcpy(info.dstSegment, info.srcSegment, info.copySegmentSize);
         }
-        patchImages.push_back(patchImage);
     });
 
-    while ( (patchExportNames.size() % 4) != 0 )
-        patchExportNames.push_back('\0');
-
-    uint64_t patchInfoSize = sizeof(dyld_cache_patch_info);
-    patchInfoSize += sizeof(dyld_cache_image_patches) * patchImages.size();
-    patchInfoSize += sizeof(dyld_cache_patchable_export) * patchExports.size();
-    patchInfoSize += sizeof(dyld_cache_patchable_location) * patchLocations.size();
-    patchInfoSize += patchExportNames.size();
-
-    // check for fit
-    uint64_t imageArraySize = _imageArray->size();
-    size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
-    if ( (imageArraySize+trieBytes.size()+patchInfoSize) > freeSpace ) {
-        _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, patch size=%lluKB, free space=%ldMB)",
-                            _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, patchInfoSize/1024, freeSpace/1024/1024);
-        return;
-    }
-
-    // copy into cache and update header
-    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    dyldCache->header.dylibsImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
-    dyldCache->header.dylibsImageArraySize = imageArraySize;
-    dyldCache->header.dylibsTrieAddr       = dyldCache->header.dylibsImageArrayAddr + imageArraySize;
-    dyldCache->header.dylibsTrieSize       = trieBytes.size();
-    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, _imageArray, imageArraySize);
-    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
-
-    // Also write out the patch info
-    dyldCache->header.patchInfoAddr = dyldCache->header.dylibsTrieAddr + dyldCache->header.dylibsTrieSize;
-    dyldCache->header.patchInfoSize = patchInfoSize;
-    dyld_cache_patch_info patchInfo;
-    patchInfo.patchTableArrayAddr = dyldCache->header.patchInfoAddr + sizeof(dyld_cache_patch_info);
-    patchInfo.patchTableArrayCount = patchImages.size();
-    patchInfo.patchExportArrayAddr = patchInfo.patchTableArrayAddr + (patchInfo.patchTableArrayCount * sizeof(dyld_cache_image_patches));
-    patchInfo.patchExportArrayCount = patchExports.size();
-    patchInfo.patchLocationArrayAddr = patchInfo.patchExportArrayAddr + (patchInfo.patchExportArrayCount * sizeof(dyld_cache_patchable_export));
-    patchInfo.patchLocationArrayCount = patchLocations.size();
-    patchInfo.patchExportNamesAddr = patchInfo.patchLocationArrayAddr + (patchInfo.patchLocationArrayCount * sizeof(dyld_cache_patchable_location));
-    patchInfo.patchExportNamesSize = patchExportNames.size();
-    ::memcpy(_readOnlyRegion.buffer + dyldCache->header.patchInfoAddr - _readOnlyRegion.unslidLoadAddress,
-             &patchInfo, sizeof(dyld_cache_patch_info));
-    ::memcpy(_readOnlyRegion.buffer + patchInfo.patchTableArrayAddr - _readOnlyRegion.unslidLoadAddress,
-             &patchImages[0], sizeof(patchImages[0]) * patchImages.size());
-    ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportArrayAddr - _readOnlyRegion.unslidLoadAddress,
-             &patchExports[0], sizeof(patchExports[0]) * patchExports.size());
-    ::memcpy(_readOnlyRegion.buffer + patchInfo.patchLocationArrayAddr - _readOnlyRegion.unslidLoadAddress,
-             &patchLocations[0], sizeof(patchLocations[0]) * patchLocations.size());
-    ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportNamesAddr - _readOnlyRegion.unslidLoadAddress,
-             &patchExportNames[0], patchExportNames.size());
-
-    _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size()+patchInfoSize,14);
-
-    // Free the underlying image array buffer
-    _imageArray->deallocate();
-}
-
-void CacheBuilder::addOtherImageArray(const std::vector<LoadedMachO>& otherDylibsAndBundles, std::vector<const LoadedMachO*>& overflowDylibs)
-{
-    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::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform);
-
-    // make ImageArray for other dylibs and bundles
-    STACK_ALLOC_ARRAY(dyld3::closure::LoadedFileInfo, others, otherDylibsAndBundles.size() + overflowDylibs.size());
-    for (const LoadedMachO& other : otherDylibsAndBundles) {
-        if ( !contains(other.loadedFileInfo.path, "staged_system_apps/") )
-            others.push_back(other.loadedFileInfo);
-    }
-
-    for (const LoadedMachO* dylib : overflowDylibs) {
-        if (dylib->mappedFile.mh->canHavePrecomputedDlopenClosure(dylib->mappedFile.runtimePath.c_str(), ^(const char*) {}) )
-            others.push_back(dylib->loadedFileInfo);
-    }
-
-    // Sort the others array by name so that it is deterministic
-    std::sort(others.begin(), others.end(),
-              [](const dyld3::closure::LoadedFileInfo& a, const dyld3::closure::LoadedFileInfo& b) {
-                  // Sort mac before iOSMac
-                  bool isIOSMacA = strncmp(a.path, "/System/iOSSupport/", 19) == 0;
-                  bool isIOSMacB = strncmp(b.path, "/System/iOSSupport/", 19) == 0;
-                  if (isIOSMacA != isIOSMacB)
-                      return !isIOSMacA;
-                  return strcmp(a.path, b.path) < 0;
-              });
-
-    const dyld3::closure::ImageArray* otherImageArray = cb.makeOtherDylibsImageArray(others, (uint32_t)_sortedDylibs.size());
-
-    // build trie of paths
-    __block std::vector<DylibIndexTrie::Entry> otherEntrys;
-    otherImageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
-        if ( !image->isInvalid() )
-            otherEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum())));
+    // Copy the coalesced 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]);
+        if (log) fprintf(stderr, "copy %s __TEXT_COAL section %s (0x%08X bytes) to %p (logical addr 0x%llX)\n",
+                         _options.archs->name(), CacheCoalescedText::SupportedSections[index],
+                         cacheStringSection.bufferSize, cacheStringSection.bufferAddr, cacheStringSection.bufferVMAddr);
+        for (const auto& stringAndOffset : cacheStringSection.stringsToOffsets)
+            ::memcpy(cacheStringSection.bufferAddr + stringAndOffset.second, stringAndOffset.first.data(), stringAndOffset.first.size() + 1);
     });
-    DylibIndexTrie dylibsTrie(otherEntrys);
-    std::vector<uint8_t> trieBytes;
-    dylibsTrie.emit(trieBytes);
-    while ( (trieBytes.size() % 4) != 0 )
-        trieBytes.push_back(0);
-
-    // check for fit
-    uint64_t imageArraySize = otherImageArray->size();
-    size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
-    if ( imageArraySize+trieBytes.size() > freeSpace ) {
-        _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, free space=%ldMB)",
-                           _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, freeSpace/1024/1024);
-        return;
-    }
-
-    // copy into cache and update header
-    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    dyldCache->header.otherImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
-    dyldCache->header.otherImageArraySize = imageArraySize;
-    dyldCache->header.otherTrieAddr       = dyldCache->header.otherImageArrayAddr + imageArraySize;
-    dyldCache->header.otherTrieSize       = trieBytes.size();
-    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, otherImageArray, imageArraySize);
-    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
-    _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size(),14);
-
-    // Free the underlying buffer
-    otherImageArray->deallocate();
 }
 
-
-void CacheBuilder::addClosures(const std::vector<LoadedMachO>& osExecutables)
+void CacheBuilder::adjustAllImagesForNewSegmentLocations()
 {
-    const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
-
-    __block std::vector<Diagnostics> osExecutablesDiags;
-    __block std::vector<const dyld3::closure::LaunchClosure*> osExecutablesClosures;
-    osExecutablesDiags.resize(osExecutables.size());
-    osExecutablesClosures.resize(osExecutables.size());
-
-    dispatch_apply(osExecutables.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
-        const LoadedMachO& loadedMachO = osExecutables[index];
-        // don't pre-build closures for staged apps into dyld cache, since they won't run from that location
-        if ( startsWith(loadedMachO.mappedFile.runtimePath, "/private/var/staged_system_apps/") ) {
-            return;
-        }
-        dyld3::closure::PathOverrides pathOverrides;
-        dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, _fileSystem, dyldCache, false, *_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) )
-            _fileSystem.fileExists(loadedMachO.loadedFileInfo.path, nullptr, nullptr, &issetuid, nullptr);
-        const dyld3::closure::LaunchClosure* mainClosure = builder.makeLaunchClosure(loadedMachO.loadedFileInfo, issetuid);
-        if ( builder.diagnostics().hasError() ) {
-           osExecutablesDiags[index].error("%s", builder.diagnostics().errorMessage().c_str());
-        }
-        else {
-            assert(mainClosure != nullptr);
-            osExecutablesClosures[index] = mainClosure;
-        }
-    });
-
-    std::map<std::string, const dyld3::closure::LaunchClosure*> closures;
-    for (uint64_t i = 0, e = osExecutables.size(); i != e; ++i) {
-        const LoadedMachO& loadedMachO = osExecutables[i];
-        const Diagnostics& diag = osExecutablesDiags[i];
-        if (diag.hasError()) {
-            if ( _options.verbose ) {
-                _diagnostics.warning("building closure for '%s': %s", loadedMachO.mappedFile.runtimePath.c_str(), diag.errorMessage().c_str());
-                for (const std::string& warn : diag.warnings() )
-                    _diagnostics.warning("%s", warn.c_str());
-            }
-            if ( loadedMachO.inputFile && (loadedMachO.inputFile->mustBeIncluded()) ) {
-                loadedMachO.inputFile->diag.error("%s", diag.errorMessage().c_str());
-            }
-        } else {
-            // Note, a closure could be null here if it has a path we skip.
-            if (osExecutablesClosures[i] != nullptr)
-                closures[loadedMachO.mappedFile.runtimePath] = osExecutablesClosures[i];
-        }
-    }
-
-    osExecutablesDiags.clear();
-    osExecutablesClosures.clear();
-
-    // preflight space needed
-    size_t closuresSpace = 0;
-    for (const auto& entry : closures) {
-        closuresSpace += entry.second->size();
-    }
-    size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
-    if ( closuresSpace > freeSpace ) {
-        _diagnostics.error("cache buffer too small to hold all closures (buffer size=%lldMB, closures size=%ldMB, free space=%ldMB)",
-                            _allocatedBufferSize/1024/1024, closuresSpace/1024/1024, freeSpace/1024/1024);
-        return;
-    }
-    DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    cache->header.progClosuresAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
-    uint8_t* closuresBase = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse;
-    std::vector<DylibIndexTrie::Entry> closureEntrys;
-    uint32_t currentClosureOffset = 0;
-    for (const auto& entry : closures) {
-        const dyld3::closure::LaunchClosure* closure = entry.second;
-        closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset)));
-        size_t size = closure->size();
-        assert((size % 4) == 0);
-        memcpy(closuresBase+currentClosureOffset, closure, size);
-        currentClosureOffset += size;
-        freeSpace -= size;
-        closure->deallocate();
-    }
-    cache->header.progClosuresSize = currentClosureOffset;
-    _readOnlyRegion.sizeInUse += currentClosureOffset;
-    freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
-    // build trie of indexes into closures list
-    DylibIndexTrie closureTrie(closureEntrys);
-    std::vector<uint8_t> trieBytes;
-    closureTrie.emit(trieBytes);
-    while ( (trieBytes.size() % 8) != 0 )
-        trieBytes.push_back(0);
-    if ( trieBytes.size() > freeSpace ) {
-        _diagnostics.error("cache buffer too small to hold all closures trie (buffer size=%lldMB, trie size=%ldMB, free space=%ldMB)",
-                            _allocatedBufferSize/1024/1024, trieBytes.size()/1024/1024, freeSpace/1024/1024);
-        return;
-    }
-    memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, &trieBytes[0], trieBytes.size());
-    cache->header.progClosuresTrieAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
-    cache->header.progClosuresTrieSize = trieBytes.size();
-    _readOnlyRegion.sizeInUse += trieBytes.size();
-    _readOnlyRegion.sizeInUse = align(_readOnlyRegion.sizeInUse, 14);
-}
-
+    __block std::vector<Diagnostics> diags;
+    diags.resize(_sortedDylibs.size());
 
-bool CacheBuilder::writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset))
-{
-    const dyld_cache_header*       cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
-    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + cacheHeader->mappingOffset);
-    assert(_readExecuteRegion.sizeInUse       == mappings[0].size);
-    assert(_readWriteRegion.sizeInUse         == mappings[1].size);
-    assert(_readOnlyRegion.sizeInUse          == mappings[2].size);
-    assert(_readExecuteRegion.cacheFileOffset == mappings[0].fileOffset);
-    assert(_readWriteRegion.cacheFileOffset   == mappings[1].fileOffset);
-    assert(_readOnlyRegion.cacheFileOffset    == mappings[2].fileOffset);
-    assert(_codeSignatureRegion.sizeInUse     == cacheHeader->codeSignatureSize);
-    assert(cacheHeader->codeSignatureOffset   == mappings[2].fileOffset+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse);
-    cacheSizeCallback(_readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse+_codeSignatureRegion.sizeInUse);
-    bool fullyWritten = copyCallback(_readExecuteRegion.buffer, _readExecuteRegion.sizeInUse, mappings[0].fileOffset);
-    fullyWritten &= copyCallback(_readWriteRegion.buffer, _readWriteRegion.sizeInUse, mappings[1].fileOffset);
-    fullyWritten &= copyCallback(_readOnlyRegion.buffer, _readOnlyRegion.sizeInUse, mappings[2].fileOffset);
-    if ( _localSymbolsRegion.sizeInUse != 0 ) {
-        assert(cacheHeader->localSymbolsOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse);
-        fullyWritten &= copyCallback(_localSymbolsRegion.buffer, _localSymbolsRegion.sizeInUse, cacheHeader->localSymbolsOffset);
+    // 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]);
     }
-    fullyWritten &= copyCallback(_codeSignatureRegion.buffer, _codeSignatureRegion.sizeInUse, cacheHeader->codeSignatureOffset);
-    return fullyWritten;
-}
-
-
-void CacheBuilder::writeFile(const std::string& path)
-{
-    std::string pathTemplate = path + "-XXXXXX";
-    size_t templateLen = strlen(pathTemplate.c_str())+2;
-    BLOCK_ACCCESSIBLE_ARRAY(char, pathTemplateSpace, templateLen);
-    strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen);
-    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);
-        };
-        auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
-            uint64_t writtenSize = pwrite(fd, src, size, dstOffset);
-            return writtenSize == size;
-        };
-        // <rdar://problem/55370916> TOCTOU: verify path is still a realpath (not changed)
-        char tempPath[MAXPATHLEN];
-        if ( ::fcntl(fd, F_GETPATH, tempPath) == 0 ) {
-            size_t tempPathLen = strlen(tempPath);
-            if ( tempPathLen > 7 )
-                tempPath[tempPathLen-7] = '\0'; // remove trailing -xxxxxx
-            if ( path != tempPath ) {
-                _diagnostics.error("output file path changed from: '%s' to: '%s'", path.c_str(), tempPath);
-                ::close(fd);
-                return;
-            }
-        }
-        else {
-            _diagnostics.error("unable to fcntl(fd, F_GETPATH) on output file");
-            ::close(fd);
-            return;
-        }
-        bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
-        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;
-            }
-            if ( ::rename(pathTemplateSpace, path.c_str()) == 0) {
-                ::close(fd);
-                return; // success
-            }
-        }
-        else {
-            _diagnostics.error("could not write file %s", pathTemplateSpace);
+    for (const Diagnostics& diag : diags) {
+        if ( diag.hasError() ) {
+            _diagnostics.error("%s", diag.errorMessage().c_str());
+            break;
         }
-        ::close(fd);
-        ::unlink(pathTemplateSpace);
     }
-    else {
-        _diagnostics.error("could not open file %s", pathTemplateSpace);
-    }
-}
-
-void CacheBuilder::writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) {
-    auto cacheSizeCallback = ^(uint64_t size) {
-        buffer = (uint8_t*)malloc(size);
-        bufferSize = size;
-    };
-    auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
-        memcpy(buffer + dstOffset, src, size);
-        return true;
-    };
-    bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
-    assert(fullyWritten);
-}
-
-void CacheBuilder::writeMapFile(const std::string& path)
-{
-    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    std::string mapContent = cache->mapFile();
-    safeSave(mapContent.c_str(), mapContent.size(), path);
-}
-
-std::string CacheBuilder::getMapFileBuffer(const std::string& cacheDisposition) const
-{
-    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    return cache->generateJSONMap(cacheDisposition.c_str());
-}
-
-
-void CacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) {
-    for (const DylibInfo& dylibInfo : _sortedDylibs)
-        callback(dylibInfo.runtimePath);
 }
 
 
@@ -3475,7 +98,7 @@ void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart, size_t
 {
     _pageCount   = (unsigned)(rwRegionSize+_pageSize-1)/_pageSize;
     _regionStart = (uint8_t*)rwRegionStart;
-    _endStart    = (uint8_t*)rwRegionStart + rwRegionSize;
+    _regionEnd   = (uint8_t*)rwRegionStart + rwRegionSize;
     _bitmap      = (bool*)calloc(_pageCount*(_pageSize/4)*sizeof(bool), 1);
 }
 
@@ -3485,7 +108,7 @@ void CacheBuilder::ASLR_Tracker::add(void* loc)
         return;
     uint8_t* p = (uint8_t*)loc;
     assert(p >= _regionStart);
-    assert(p < _endStart);
+    assert(p < _regionEnd);
     _bitmap[(p-_regionStart)/4] = true;
 }
 
@@ -3495,7 +118,7 @@ void CacheBuilder::ASLR_Tracker::remove(void* loc)
         return;
     uint8_t* p = (uint8_t*)loc;
     assert(p >= _regionStart);
-    assert(p < _endStart);
+    assert(p < _regionEnd);
     _bitmap[(p-_regionStart)/4] = false;
 }
 
@@ -3505,10 +128,67 @@ bool CacheBuilder::ASLR_Tracker::has(void* loc)
         return true;
     uint8_t* p = (uint8_t*)loc;
     assert(p >= _regionStart);
-    assert(p < _endStart);
+    assert(p < _regionEnd);
     return _bitmap[(p-_regionStart)/4];
 }
 
+void CacheBuilder::ASLR_Tracker::setHigh8(void* p, uint8_t high8)
+{
+    _high8Map[p] = high8;
+}
+
+void CacheBuilder::ASLR_Tracker::setAuthData(void* p, uint16_t diversity, bool hasAddrDiv, uint8_t key)
+{
+    _authDataMap[p] = {diversity, hasAddrDiv, key};
+}
+
+void CacheBuilder::ASLR_Tracker::setRebaseTarget32(void*p, uint32_t targetVMAddr)
+{
+    _rebaseTarget32[p] = targetVMAddr;
+}
+
+void CacheBuilder::ASLR_Tracker::setRebaseTarget64(void*p, uint64_t targetVMAddr)
+{
+    _rebaseTarget64[p] = targetVMAddr;
+}
+
+bool CacheBuilder::ASLR_Tracker::hasHigh8(void* p, uint8_t* highByte)
+{
+    auto pos = _high8Map.find(p);
+    if ( pos == _high8Map.end() )
+        return false;
+    *highByte = pos->second;
+    return true;
+}
+
+bool CacheBuilder::ASLR_Tracker::hasAuthData(void* p, uint16_t* diversity, bool* hasAddrDiv, uint8_t* key)
+{
+    auto pos = _authDataMap.find(p);
+    if ( pos == _authDataMap.end() )
+        return false;
+    *diversity  = pos->second.diversity;
+    *hasAddrDiv = pos->second.addrDiv;
+    *key        = pos->second.key;
+    return true;
+}
+
+bool CacheBuilder::ASLR_Tracker::hasRebaseTarget32(void* p, uint32_t* vmAddr)
+{
+    auto pos = _rebaseTarget32.find(p);
+    if ( pos == _rebaseTarget32.end() )
+        return false;
+    *vmAddr = pos->second;
+    return true;
+}
+
+bool CacheBuilder::ASLR_Tracker::hasRebaseTarget64(void* p, uint64_t* vmAddr)
+{
+    auto pos = _rebaseTarget64.find(p);
+    if ( pos == _rebaseTarget64.end() )
+        return false;
+    *vmAddr = pos->second;
+    return true;
+}
 
 ////////////////////////////  DylibTextCoalescer ////////////////////////////////////
 
index 6d6ef41c7605a8a6e2f65cd1b9ec64626ca42922..8b2bdc3ca5566eb5f76c073d7eccd95653db370d 100644 (file)
@@ -69,30 +69,7 @@ public:
         InputFile*                      inputFile;
     };
 
-    void                                        build(std::vector<InputFile>& inputFiles,
-                                                      std::vector<DyldSharedCache::FileAlias>& aliases);
-    void                                        build(const std::vector<LoadedMachO>& dylibs,
-                                                      const std::vector<LoadedMachO>& otherOsDylibsInput,
-                                                      const std::vector<LoadedMachO>& osExecutables,
-                                                      std::vector<DyldSharedCache::FileAlias>& aliases);
-    void                                        build(const std::vector<DyldSharedCache::MappedMachO>&  dylibsToCache,
-                                                      const std::vector<DyldSharedCache::MappedMachO>&  otherOsDylibs,
-                                                      const std::vector<DyldSharedCache::MappedMachO>&  osExecutables,
-                                                      std::vector<DyldSharedCache::FileAlias>& aliases);
-    void                                        writeFile(const std::string& path);
-    void                                        writeBuffer(uint8_t*& buffer, uint64_t& size);
-    void                                        writeMapFile(const std::string& path);
-    std::string                                 getMapFileBuffer(const std::string& cacheDisposition) const;
-    void                                        deleteBuffer();
     std::string                                 errorMessage();
-    const std::set<std::string>                 warnings();
-    const std::set<const dyld3::MachOAnalyzer*> evictions();
-    const bool                                  agileSignature();
-    const std::string                           cdHashFirst();
-    const std::string                           cdHashSecond();
-    const std::string                           uuid() const;
-
-    void forEachCacheDylib(void (^callback)(const std::string& path));
 
     struct SegmentMappingInfo {
         const void*     srcSegment;
@@ -151,24 +128,47 @@ public:
 
         void        setDataRegion(const void* rwRegionStart, size_t rwRegionSize);
         void        add(void* p);
+        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);
         const bool* bitmap()        { return _bitmap; }
         unsigned    dataPageCount() { return _pageCount; }
-        void        disable() { _enabled = false; };
+        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);
 
     private:
 
         uint8_t*     _regionStart    = nullptr;
-        uint8_t*     _endStart       = nullptr;
+        uint8_t*     _regionEnd      = nullptr;
         bool*        _bitmap         = nullptr;
         unsigned     _pageCount      = 0;
         unsigned     _pageSize       = 4096;
         bool         _enabled        = true;
+
+        struct AuthData {
+            uint16_t    diversity;
+            bool        addrDiv;
+            uint8_t     key;
+        };
+        std::unordered_map<void*, uint8_t>  _high8Map;
+        std::unordered_map<void*, AuthData> _authDataMap;
+        std::unordered_map<void*, uint32_t> _rebaseTarget32;
+        std::unordered_map<void*, uint64_t> _rebaseTarget64;
     };
 
     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;
+
+
     struct Region
     {
         uint8_t*    buffer                 = nullptr;
@@ -178,29 +178,9 @@ public:
         uint64_t    cacheFileOffset        = 0;
     };
 
-private:
+protected:
     template <typename P>
     friend class LinkeditOptimizer;
-    
-    struct ArchLayout
-    {
-        uint64_t    sharedMemoryStart;
-        uint64_t    sharedMemorySize;
-        uint64_t    textAndDataMaxSize;
-        uint64_t    sharedRegionPadding;
-        uint64_t    pointerDeltaMask;
-        const char* archName;
-        uint16_t    csPageSize;
-        uint8_t     sharedRegionAlignP2;
-        uint8_t     slideInfoBytesPerPage;
-        bool        sharedRegionsAreDiscontiguous;
-        bool        is64;
-        bool        useValueAdd;
-    };
-
-    static const ArchLayout  _s_archLayout[];
-    static const char* const _s_neverStubEliminateDylibs[];
-    static const char* const _s_neverStubEliminateSymbols[];
 
     struct UnmappedRegion
     {
@@ -217,41 +197,8 @@ private:
         DylibTextCoalescer              textCoalescer;
     };
 
-    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        assignSegmentAddresses();
-
-    uint64_t    cacheOverflowAmount();
-    size_t      evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs);
-
-    void        fipsSign();
-    void        codeSign();
-    uint64_t    pathHash(const char* path);
-    void        writeCacheHeader();
     void        copyRawSegments();
     void        adjustAllImagesForNewSegmentLocations();
-    void        writeSlideInfoV1();
-    void        writeSlideInfoV3(const bool bitmap[], unsigned dataPageCoun);
-    uint16_t    pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[]);
-    void        findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
-    void        addImageArray();
-    void        buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases);
-    void        addOtherImageArray(const std::vector<LoadedMachO>&, std::vector<const LoadedMachO*>& overflowDylibs);
-    void        addClosures(const std::vector<LoadedMachO>&);
-    void        markPaddingInaccessible();
-
-    bool        writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset));
-
-    template <typename P> void writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount);
-    template <typename P> bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info);
-    template <typename P> void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info,
-                                             std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
-
-    template <typename P> void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount);
-    template <typename P> bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info);
-    template <typename P> void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info,
-                                             std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
 
     // implemented in AdjustDylibSegemnts.cpp
     void        adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const;
@@ -259,56 +206,23 @@ private:
     // implemented in OptimizerLinkedit.cpp
     void        optimizeLinkedit();
 
-    // implemented in OptimizerObjC.cpp
-    void        optimizeObjC();
-    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();
-
-    typedef std::unordered_map<std::string, const dyld3::MachOAnalyzer*> InstallNameToMA;
-
-    typedef uint64_t                                                CacheOffset;
-
     const DyldSharedCache::CreateOptions&       _options;
     const dyld3::closure::FileSystem&           _fileSystem;
     Region                                      _readExecuteRegion;
     Region                                      _readWriteRegion;
     Region                                      _readOnlyRegion;
     UnmappedRegion                              _localSymbolsRegion;
-    UnmappedRegion                              _codeSignatureRegion;
     vm_address_t                                _fullAllocatedBuffer;
     uint64_t                                    _nonLinkEditReadOnlySize;
     Diagnostics                                 _diagnostics;
-    std::set<const dyld3::MachOAnalyzer*>       _evictions;
-    const ArchLayout*                           _archLayout;
-    uint32_t                                    _aliasCount;
-    uint64_t                                    _slideInfoFileOffset;
-    uint64_t                                    _slideInfoBufferSizeAllocated;
-    uint8_t*                                    _objcReadOnlyBuffer;
-    uint64_t                                    _objcReadOnlyBufferSizeUsed;
-    uint64_t                                    _objcReadOnlyBufferSizeAllocated;
-    uint8_t*                                    _objcReadWriteBuffer;
-    uint64_t                                    _objcReadWriteBufferSizeAllocated;
     uint64_t                                    _allocatedBufferSize;
-    CacheCoalescedText                          _coalescedText;
-    uint64_t                                    _selectorStringsFromExecutables;
     std::vector<DylibInfo>                      _sortedDylibs;
-    InstallNameToMA                             _installNameToCacheDylib;
-    std::unordered_map<std::string, uint32_t>   _dataDirtySegsOrder;
+    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;
-    std::map<void*, std::string>                _missingWeakImports;
     mutable LOH_Tracker                         _lohTracker;
-    const dyld3::closure::ImageArray*           _imageArray;
-    uint32_t                                    _sharedStringsPoolVmOffset;
-    uint8_t                                     _cdHashFirst[20];
-    uint8_t                                     _cdHashSecond[20];
-    std::unordered_map<const dyld3::MachOLoaded*, std::set<CacheOffset>>        _dylibToItsExports;
-    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;
 };
 
 
index 2d39af1cc030796be373b4804d8265dfed922ebc..f0b179d74b82a8f99a22f84a7856584a40c88722 100644 (file)
@@ -41,7 +41,7 @@
 #include <vector>
 #include <unordered_map>
 #include <unordered_set>
-#include "CacheBuilder.h"
+#include "SharedCacheBuilder.h"
 #include "FileUtils.h"
 #endif
 
@@ -67,8 +67,8 @@ DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions&
                                                        const std::vector<MappedMachO>&    otherOsDylibs,
                                                        const std::vector<MappedMachO>&    osExecutables)
 {
-    CreateResults  results;
-    CacheBuilder   cache(options, fileSystem);
+    CreateResults       results;
+    SharedCacheBuilder  cache(options, fileSystem);
     if (!cache.errorMessage().empty()) {
         results.errorMessage = cache.errorMessage();
         return results;
index ffa275cbe8024c8c217eba4c280bd226a534467f..0657d6fb0340fd00151a7c911b3d6ffe6a779c0a 100644 (file)
@@ -775,8 +775,12 @@ public:
             }
         }
 
-               if (strcmp(segname, "__DATA") == 0)
-                       return getSection("__DATA_CONST", sectname);
+        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;
     }
 
diff --git a/dyld3/shared-cache/Manifest.h b/dyld3/shared-cache/Manifest.h
deleted file mode 100644 (file)
index c89d01b..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (c) 2017 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#ifndef Manifest_h
-#define Manifest_h
-
-#include <map>
-#include <set>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <unordered_map>
-#include <unordered_set>
-
-#include <assert.h>
-#include <uuid/uuid.h>
-
-#import <Foundation/Foundation.h>
-
-#include "DyldSharedCache.h"
-#include "Diagnostics.h"
-#include "MachOAnalyzer.h"
-#include "ClosureFileSystemPhysical.h"
-
-extern std::string toolDir();
-
-namespace dyld3 {
-
-struct VIS_HIDDEN UUID {
-    UUID() {}
-    UUID(const UUID& other) { uuid_copy(_bytes, other._bytes); }
-    UUID(const uuid_t other) { uuid_copy(&_bytes[0], other); }
-    UUID(const dyld3::MachOAnalyzer* ml) { ml->getUuid(_bytes); }
-    bool operator<(const UUID& other) const { return uuid_compare(_bytes, other._bytes) < 0; }
-    bool operator==(const UUID& other) const { return uuid_compare(_bytes, other._bytes) == 0; }
-    bool operator!=(const UUID& other) const { return !(*this == other); }
-
-    size_t hash() const
-    {
-        size_t retval = 0;
-        for (size_t i = 0; i < (16 / sizeof(size_t)); ++i) {
-            retval ^= ((size_t*)(&_bytes[0]))[i];
-        }
-        return retval;
-    }
-    const unsigned char* get() const { return _bytes; };
-
-private:
-    uuid_t _bytes;
-};
-
-struct BuildQueueEntry {
-    DyldSharedCache::CreateOptions            options;
-    dyld3::closure::FileSystemPhysical        fileSystem;
-    std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
-    std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles;
-    std::vector<DyldSharedCache::MappedMachO> mainExecutables;
-    std::string                               outputPath;
-    std::set<std::string>                     configNames;
-};
-
-struct Manifest {
-    struct UUIDInfo {
-        const MachOAnalyzer* mh;
-        uint64_t sliceFileOffset;
-        std::size_t size;
-        std::string runtimePath;
-        std::string buildPath;
-        std::string installName;
-        std::string arch;
-        UUID uuid;
-        UUIDInfo(const MachOAnalyzer* M, std::size_t S, uint64_t SO, UUID U, std::string A, std::string RP, std::string BP, std::string IN)
-            : mh(M), size(S), arch(A), uuid(U), runtimePath(RP), buildPath(BP), installName(IN), sliceFileOffset(SO) {}
-        UUIDInfo() : UUIDInfo(nullptr, 0, 0, UUID(), "", "", "", "") {}
-    };
-
-    struct Project {
-        std::vector<std::string> sources;
-    };
-
-
-    struct SegmentInfo {
-        std::string name;
-        uint64_t    startAddr;
-        uint64_t    endAddr;
-    };
-
-    struct CacheInfo {
-        std::vector<SegmentInfo> regions;
-        std::string              cdHash;
-    };
-
-    struct CacheImageInfo {
-        bool                     included;
-        std::string              exclusionInfo;
-        UUID                     uuid;
-        std::string              installname;
-        std::vector<SegmentInfo> segments;
-        CacheImageInfo(void)
-            : included(true)
-        {
-        }
-    };
-
-    struct Results {
-        std::string failure;
-        std::map<UUID, CacheImageInfo> dylibs;
-        std::map<UUID, CacheImageInfo> bundles;
-        std::map<UUID, CacheImageInfo> executables;
-
-        std::set<std::string> warnings;
-        CacheInfo             developmentCache;
-        CacheInfo             productionCache;
-        CacheImageInfo& dylibForInstallname(const std::string& installname);
-        void exclude(const dyld3::MachOAnalyzer* ml, const std::string& reason);
-        void exclude(Manifest& manifest, const UUID& uuid, const std::string& reason);
-    };
-
-    struct Architecture {
-        mutable Results     results;
-
-        bool operator==(const Architecture& O) const;
-        bool operator!=(const Architecture& other) const;
-    };
-
-    struct Configuration {
-        std::string                         platformName;
-        std::string                         device;
-        std::string                         disposition;
-        std::string                         metabomTag;
-        std::set<std::string>               metabomTags;
-        std::set<std::string>               metabomExcludeTags;
-        std::set<std::string>               metabomRestrictTags;
-        std::set<std::string>               restrictedInstallnames;
-        std::map<std::string, Architecture> architectures;
-
-        bool operator==(const Configuration& O) const;
-        bool operator!=(const Configuration& other) const;
-        const Architecture& architecture(const std::string& architecture) const;
-        void forEachArchitecture(std::function<void(const std::string& archName)> lambda) const;
-    };
-
-    const std::map<std::string, Project>& projects();
-    const Configuration& configuration(const std::string& configuration) const;
-    void forEachConfiguration(std::function<void(const std::string& configName)> lambda) const;
-
-    void addProjectSource(const std::string& project, const std::string& source, bool first = false);
-
-    const std::string projectPath(const std::string& projectName);
-    const bool        empty(void);
-    const std::string dylibOrderFile() const;
-    void setDylibOrderFile(const std::string& dylibOrderFile);
-
-    const std::string dirtyDataOrderFile() const;
-    void setDirtyDataOrderFile(const std::string& dirtyDataOrderFile);
-
-    const std::string metabomFile() const;
-    void setMetabomFile(const std::string& metabomFile);
-
-    const Platform platform() const;
-    void setPlatform(const Platform platform);
-
-    const std::string& build() const;
-    void setBuild(const std::string& build);
-    const uint32_t version() const;
-    void setVersion(const uint32_t manifestVersion);
-    bool normalized;
-
-    Manifest(Diagnostics& D, const std::string& path, bool populateIt = true);
-    void populate(const std::set<std::string>& overlays);
-
-    BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix,
-                                   bool isLocallyBuiltCache, bool skipWrites, bool verbose);
-
-    void write(const std::string& path);
-    void writeJSON(const std::string& path);
-    void        canonicalize(void);
-    void        calculateClosure();
-    const MachOAnalyzer* machOForUUID(const UUID& uuid) const;
-    const std::string buildPathForUUID(const UUID& uuid);
-    const std::string runtimePathForUUID(const UUID& uuid);
-    const std::string& installNameForUUID(const UUID& uuid);
-    DyldSharedCache::MappedMachO machoForPathAndArch(const std::string& path, const std::string& arch) const;
-    void remove(const std::string& config, const std::string& arch);
-    void runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function<void(const std::string configuration, const std::string architecture)> lambda);
-    bool filterForConfig(const std::string& configName);
-    std::set<std::string> resultsForConfiguration(const std::string& configName);
-
-    // These are used by MRM to support having the Manifest give us a list of files/symlinks from the BOM but we use MRM for the actual cache generation
-    void forEachMachO(std::string configuration, std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda);
-
-    void forEachSymlink(std::string configuration, std::function<void(const std::string &fromPath, const std::string &toPath)> lambda);
-
-private:
-    NSDictionary*    _manifestDict;
-    Diagnostics&      _diags;
-    std::map<UUID, UUIDInfo> _uuidMap;
-    std::map<std::pair<std::string, std::string>, UUID> _installNameMap;
-    std::vector<std::pair<std::string, std::string>>    _symlinks;
-    static dispatch_queue_t _identifierQueue;
-    uint32_t                _manifestVersion;
-    std::string             _build;
-    std::string             _dylibOrderFile;
-    std::string             _dirtyDataOrderFile;
-    std::string             _metabomFile;
-    Platform                _platform;
-    std::map<std::string, Project>               _projects;
-    std::map<std::string, Configuration>         _configurations;
-    std::map<std::string, std::set<std::string>> _metabomTagMap;
-    std::map<std::string, std::set<std::string>> _metabomSymlinkTagMap;
-    std::map<std::string, std::set<std::string>> _metabomExcludeTagMap;
-    std::map<std::string, std::set<std::string>> _metabomRestrictedTagMap;
-
-    std::vector<DyldSharedCache::MappedMachO> dylibsForCache(const std::string& configuration, const std::string& architecture);
-    std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles(const std::string& configuration, const std::string& architecture);
-    std::vector<DyldSharedCache::MappedMachO> mainExecutables(const std::string& configuration, const std::string& architecture);
-
-    const UUIDInfo& infoForUUID(const UUID& uuid) const;
-    const UUIDInfo infoForInstallNameAndarch(const std::string& installName, const std::string arch) const;
-    void insert(std::vector<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo);
-    bool loadParser(const void* p, size_t sliceLength, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set<std::string>& architectures);
-    bool loadParsers(const std::string& pathToMachO, const std::string& runtimePath, const std::set<std::string>& architectures);
-    void dedupeDispositions();
-    void calculateClosure(const std::string& configuration, const std::string& architecture);
-    void canonicalizeDylib(const std::string& installname);
-    template <typename P>
-    void canonicalizeDylib(const std::string& installname, const uint8_t* p);
-    void addImplicitAliases(void);
-};
-}
-
-namespace std {
-template <>
-struct hash<dyld3::UUID> {
-    size_t operator()(const dyld3::UUID& x) const
-    {
-        return x.hash();
-    }
-};
-}
-
-#endif /* Manifest_h */
diff --git a/dyld3/shared-cache/Manifest.mm b/dyld3/shared-cache/Manifest.mm
deleted file mode 100644 (file)
index 6d09811..0000000
+++ /dev/null
@@ -1,1227 +0,0 @@
-/*
- * Copyright (c) 2017 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-extern "C" {
-#include <Bom/Bom.h>
-#include <Metabom/MBTypes.h>
-#include <Metabom/MBEntry.h>
-#include <Metabom/MBMetabom.h>
-#include <Metabom/MBIterator.h>
-};
-
-#include <algorithm>
-#include <CommonCrypto/CommonDigest.h>
-#include <CommonCrypto/CommonDigestSPI.h>
-#include <Foundation/Foundation.h>
-
-#include "MachOFileAbstraction.hpp"
-#include "FileAbstraction.hpp"
-#include "Trie.hpp"
-#include "FileUtils.h"
-#include "StringUtils.h"
-#include "MachOFile.h"
-#include "MachOAnalyzer.h"
-
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-
-#include <array>
-#include <vector>
-
-#include "Manifest.h"
-#include "ClosureFileSystemPhysical.h"
-
-namespace {
-//FIXME this should be in a class
-static inline NSString* cppToObjStr(const std::string& str) { return [NSString stringWithUTF8String:str.c_str()]; }
-
-template <class Set1, class Set2>
-inline bool is_disjoint(const Set1& set1, const Set2& set2)
-{
-    if (set1.empty() || set2.empty())
-        return true;
-
-    typename Set1::const_iterator it1 = set1.begin(), it1End = set1.end();
-    typename Set2::const_iterator it2 = set2.begin(), it2End = set2.end();
-
-    if (*it1 > *set2.rbegin() || *it2 > *set1.rbegin())
-        return true;
-
-    while (it1 != it1End && it2 != it2End) {
-        if (*it1 == *it2)
-            return false;
-        if (*it1 < *it2) {
-            it1++;
-        } else {
-            it2++;
-        }
-    }
-
-    return true;
-}
-
-} /* Anonymous namespace */
-
-namespace dyld3 {
-void Manifest::Results::exclude(const dyld3::MachOAnalyzer* mh, const std::string& reason)
-{
-    UUID dylibUUID(mh);
-    dylibs[dylibUUID].uuid          = dylibUUID;
-    dylibs[dylibUUID].installname   = mh->installName();
-    dylibs[dylibUUID].included      = false;
-    dylibs[dylibUUID].exclusionInfo = reason;
-}
-
-void Manifest::Results::exclude(Manifest& manifest, const UUID& uuid, const std::string& reason)
-{
-    const MachOAnalyzer* mh = manifest.machOForUUID(uuid);
-    dylibs[uuid].uuid          = uuid;
-    dylibs[uuid].installname   = mh->installName();
-    dylibs[uuid].included      = false;
-    dylibs[uuid].exclusionInfo = reason;
-}
-
-Manifest::CacheImageInfo& Manifest::Results::dylibForInstallname(const std::string& installname)
-{
-    auto i = find_if(dylibs.begin(), dylibs.end(), [&installname](std::pair<UUID, CacheImageInfo> d) { return d.second.installname == installname; });
-    assert(i != dylibs.end());
-    return i->second;
-}
-
-bool Manifest::Architecture::operator==(const Architecture& O) const
-{
-    for (auto& dylib : results.dylibs) {
-        if (dylib.second.included) {
-            auto Odylib = O.results.dylibs.find(dylib.first);
-            if (Odylib == O.results.dylibs.end()
-                || Odylib->second.included == false
-                || Odylib->second.uuid != dylib.second.uuid)
-                return false;
-        }
-    }
-
-    for (const auto& Odylib : O.results.dylibs) {
-        if (Odylib.second.included) {
-            auto dylib = results.dylibs.find(Odylib.first);
-            if (dylib == results.dylibs.end()
-                || dylib->second.included == false
-                || dylib->second.uuid != Odylib.second.uuid)
-                return false;
-        }
-    }
-
-    for (auto& bundle : results.bundles) {
-        if (bundle.second.included) {
-            auto Obundle = O.results.bundles.find(bundle.first);
-            if (Obundle == O.results.bundles.end()
-                || Obundle->second.included == false
-                || Obundle->second.uuid != bundle.second.uuid)
-                return false;
-        }
-    }
-
-    for (const auto& Obundle : O.results.bundles) {
-        if (Obundle.second.included) {
-            auto bundle = results.bundles.find(Obundle.first);
-            if (bundle == results.bundles.end()
-                || bundle->second.included == false
-                || bundle->second.uuid != Obundle.second.uuid)
-                return false;
-        }
-    }
-
-    for (auto& executable : results.executables) {
-        if (executable.second.included) {
-            auto Oexecutable = O.results.executables.find(executable.first);
-            if (Oexecutable == O.results.executables.end()
-                || Oexecutable->second.included == false
-                || Oexecutable->second.uuid != executable.second.uuid)
-                return false;
-        }
-    }
-
-    for (const auto& Oexecutable : O.results.executables) {
-        if (Oexecutable.second.included) {
-            auto executable = results.executables.find(Oexecutable.first);
-            if (executable == results.executables.end()
-                || executable->second.included == false
-                || executable->second.uuid != Oexecutable.second.uuid)
-                return false;
-        }
-    }
-
-    return true;
-}
-
-bool Manifest::Configuration::operator==(const Configuration& O) const
-{
-    return architectures == O.architectures;
-}
-
-bool Manifest::Configuration::operator!=(const Configuration& other) const { return !(*this == other); }
-
-const Manifest::Architecture& Manifest::Configuration::architecture(const std::string& architecture) const
-{
-    assert(architectures.find(architecture) != architectures.end());
-    return architectures.find(architecture)->second;
-}
-
-void Manifest::Configuration::forEachArchitecture(std::function<void(const std::string& archName)> lambda) const
-{
-    for (const auto& architecutre : architectures) {
-        lambda(architecutre.first);
-    }
-}
-
-bool Manifest::Architecture::operator!=(const Architecture& other) const { return !(*this == other); }
-
-const std::map<std::string, Manifest::Project>& Manifest::projects()
-{
-    return _projects;
-}
-
-const Manifest::Configuration& Manifest::configuration(const std::string& configuration) const
-{
-    assert(_configurations.find(configuration) != _configurations.end());
-    return _configurations.find(configuration)->second;
-}
-
-void Manifest::forEachConfiguration(std::function<void(const std::string& configName)> lambda) const
-{
-    for (const auto& configuration : _configurations) {
-        lambda(configuration.first);
-    }
-}
-
-void Manifest::addProjectSource(const std::string& project, const std::string& source, bool first)
-{
-    auto& sources = _projects[project].sources;
-    if (std::find(sources.begin(), sources.end(), source) == sources.end()) {
-        if (first) {
-            sources.insert(sources.begin(), source);
-        } else {
-            sources.push_back(source);
-        }
-    }
-}
-
-const std::string Manifest::projectPath(const std::string& projectName)
-{
-    auto project = _projects.find(projectName);
-    if (project == _projects.end())
-        return "";
-    if (project->second.sources.size() == 0)
-        return "";
-    return project->second.sources[0];
-}
-
-const bool Manifest::empty(void)
-{
-    for (const auto& configuration : _configurations) {
-        if (configuration.second.architectures.size() != 0)
-            return false;
-    }
-    return true;
-}
-
-const std::string Manifest::dylibOrderFile() const { return _dylibOrderFile; };
-void Manifest::setDylibOrderFile(const std::string& dylibOrderFile) { _dylibOrderFile = dylibOrderFile; };
-
-const std::string Manifest::dirtyDataOrderFile() const { return _dirtyDataOrderFile; };
-void Manifest::setDirtyDataOrderFile(const std::string& dirtyDataOrderFile) { _dirtyDataOrderFile = dirtyDataOrderFile; };
-
-const std::string Manifest::metabomFile() const { return _metabomFile; };
-void Manifest::setMetabomFile(const std::string& metabomFile) { _metabomFile = metabomFile; };
-
-const Platform Manifest::platform() const { return _platform; };
-void Manifest::setPlatform(const Platform platform) { _platform = platform; };
-
-const std::string& Manifest::build() const { return _build; };
-void Manifest::setBuild(const std::string& build) { _build = build; };
-const uint32_t                             Manifest::version() const { return _manifestVersion; };
-void Manifest::setVersion(const uint32_t manifestVersion) { _manifestVersion = manifestVersion; };
-
-BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs,
-                                         const std::string& prefix, bool isLocallyBuiltCache, bool skipWrites, bool verbose)
-{
-    dyld3::BuildQueueEntry retval;
-
-    DyldSharedCache::CreateOptions options;
-    options.outputFilePath    = skipWrites ? "" : outputPath;
-    options.outputMapFilePath = skipWrites ? "" : outputPath + ".map";
-    options.archs = &dyld3::GradedArchs::forName(arch.c_str());
-    options.platform = platform();
-    options.excludeLocalSymbols = true;
-    options.optimizeStubs = optimizeStubs;
-    options.optimizeObjC = true;
-    options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ?
-                                    DyldSharedCache::Agile : DyldSharedCache::SHA256only;
-    options.dylibsRemovedDuringMastering = true;
-    options.inodesAreSameAsRuntime = false;
-    options.cacheSupportsASLR = true;
-    options.forSimulator = false;
-    options.isLocallyBuiltCache = isLocallyBuiltCache;
-    options.verbose = verbose;
-    options.evictLeafDylibsOnOverflow = true;
-    options.loggingPrefix = prefix;
-    options.dylibOrdering = parseOrderFile(loadOrderFile(_dylibOrderFile));
-    options.dirtyDataSegmentOrdering = parseOrderFile(loadOrderFile(_dirtyDataOrderFile));
-
-    char rootsDir[PATH_MAX];
-    realpath("./Root/", rootsDir);
-    
-    dyld3::BuildQueueEntry queueEntry;
-    retval.configNames = configs;
-    retval.options = options;
-    retval.fileSystem = dyld3::closure::FileSystemPhysical(strdup(rootsDir));
-    retval.outputPath = outputPath;
-    retval.dylibsForCache = dylibsForCache(*configs.begin(), arch);
-    retval.otherDylibsAndBundles = otherDylibsAndBundles(*configs.begin(), arch);
-    retval.mainExecutables = mainExecutables(*configs.begin(), arch);
-
-    return retval;
-}
-
-bool Manifest::loadParser(const void* p, size_t sliceLength, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set<std::string>& architectures)
-{
-    assert(!_diags.hasError());
-
-    const MachOFile* mf = reinterpret_cast<const MachOFile*>(p);
-    const std::string archName = mf->archName();
-    if ( archName == "unknown" ) {
-        // Clear the error and punt
-        _diags.verbose("Dylib located at '%s' has unknown architecture\n", runtimePath.c_str());
-        return false;
-    }
-    if ( architectures.count(archName) == 0 )
-        return false;
-
-    const MachOAnalyzer* ma = reinterpret_cast<const MachOAnalyzer*>(p);
-    if ( !ma->validMachOForArchAndPlatform(_diags, sliceLength, runtimePath.c_str(), dyld3::GradedArchs::forName(archName.c_str()), _platform) ) {
-        // Clear the error and punt
-        _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
-        _diags.clearError();
-        return false;
-    }
-
-    // if this file uses zero-fill expansion, then mapping whole file in one blob will not work
-    // remapIfZeroFill() will remap the file
-    closure::FileSystemPhysical fileSystem;
-    closure::LoadedFileInfo info;
-    info.fileContent                = p;
-    info.fileContentLen             = sliceLength;
-    info.sliceOffset                = 0;
-    info.sliceLen                   = sliceLength;
-    info.inode                      = 0;
-    info.mtime                      = 0;
-    info.unload                     = nullptr;
-    ma = ma->remapIfZeroFill(_diags, fileSystem, info);
-
-    if (ma == nullptr) {
-        _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
-        _diags.clearError();
-        return false;
-    }
-
-    uuid_t uuid;
-    ma->getUuid(uuid);
-
-    if ( ma->isDylib() ) {
-        std::string installName = ma->installName();
-        auto index = std::make_pair(installName, archName);
-        auto i = _installNameMap.find(index);
-
-        if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib"
-            || installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" ) {
-            // HACK to deal with device specific dylibs. These must not be inseted into the installNameMap
-            _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
-        } else if (i == _installNameMap.end()) {
-            _installNameMap.insert(std::make_pair(index, uuid));
-            _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
-            if (installName[0] != '@' && installName != runtimePath) {
-                _diags.warning("Dylib located at '%s' has  installname '%s'", runtimePath.c_str(), installName.c_str());
-            }
-        } else {
-            auto info = infoForUUID(i->second);
-            _diags.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), runtimePath.c_str(), info.runtimePath.c_str());
-
-            // This is the "Good" one, overwrite
-            if (runtimePath == installName) {
-                _uuidMap.erase(uuid);
-                _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
-            }
-        }
-    } else {
-        _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, "")));
-    }
-    return true;
-}
-
-//FIXME: assert we have not errored first
-bool Manifest::loadParsers(const std::string& buildPath, const std::string& runtimePath, const std::set<std::string>& architectures)
-{
-    __block bool retval = false;
-    const void*  p = (uint8_t*)(-1);
-    struct stat  stat_buf;
-
-    std::tie(p, stat_buf) = fileCache.cacheLoad(_diags, buildPath);
-
-    if (p == (uint8_t*)(-1)) {
-        return false;
-    }
-
-    if ( const FatFile* fh = FatFile::isFatFile(p) ) {
-        fh->forEachSlice(_diags, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
-            if (loadParser(sliceStart, sliceSize, (uintptr_t)sliceStart-(uintptr_t)p, runtimePath, buildPath, architectures))
-                retval = true;
-        });
-    } else {
-        return loadParser(p, stat_buf.st_size, 0, runtimePath, buildPath, architectures);
-    }
-    return retval;
-}
-
-
-const Manifest::UUIDInfo& Manifest::infoForUUID(const UUID& uuid) const {
-    auto i = _uuidMap.find(uuid);
-    assert(i != _uuidMap.end());
-    return i->second;
-}
-
-const Manifest::UUIDInfo Manifest::infoForInstallNameAndarch(const std::string& installName, const std::string arch) const  {
-    UUIDInfo retval;
-    auto uuidI = _installNameMap.find(std::make_pair(installName, arch));
-    if (uuidI == _installNameMap.end())
-        return UUIDInfo();
-
-    auto i = _uuidMap.find(uuidI->second);
-    if (i == _uuidMap.end())
-    return UUIDInfo();
-    return i->second;
-}
-
-const MachOAnalyzer* Manifest::machOForUUID(const UUID& uuid) const {
-    return infoForUUID(uuid).mh;
-}
-
-const std::string Manifest::buildPathForUUID(const UUID& uuid) {
-    return infoForUUID(uuid).buildPath;
-}
-
-const std::string Manifest::runtimePathForUUID(const UUID& uuid) {
-    return infoForUUID(uuid).runtimePath;
-}
-
-const std::string& Manifest::installNameForUUID(const UUID& uuid) {
-    return infoForUUID(uuid).installName;
-}
-
-Manifest::Manifest(Diagnostics& D, const std::string& path, bool populateIt) :
-    _diags(D)
-{
-    _manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
-    if (!_manifestDict)
-        return;
-    NSString*            platStr = _manifestDict[@"platform"];
-
-    if (platStr == nullptr)
-        platStr = @"ios";
-    std::string platformString = [platStr UTF8String];
-    setMetabomFile([_manifestDict[@"metabomFile"] UTF8String]);
-
-    if (platformString == "ios") {
-        setPlatform(dyld3::Platform::iOS);
-    } else if ( (platformString == "tvos") || (platformString == "atv") ) {
-        setPlatform(dyld3::Platform::tvOS);
-    } else if ( (platformString == "watchos") || (platformString == "watch") ) {
-        setPlatform(dyld3::Platform::watchOS);
-    } else if ( (platformString == "bridgeos") || (platformString == "bridge") ) {
-        setPlatform(dyld3::Platform::bridgeOS);
-    } else if ( (platformString == "macos") || (platformString == "osx") ) {
-        setPlatform(dyld3::Platform::macOS);
-    } else {
-        //Fixme should we error?
-        setPlatform(dyld3::Platform::iOS);
-    }
-
-    for (NSString* project in _manifestDict[@"projects"]) {
-        for (NSString* source in _manifestDict[@"projects"][project]) {
-            addProjectSource([project UTF8String], [source UTF8String]);
-        }
-    }
-
-    for (NSString* configuration in _manifestDict[@"configurations"]) {
-        std::string configStr = [configuration UTF8String];
-        std::string configTag = [_manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
-
-        if (_manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
-            for (NSString* excludeTag in _manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
-                _metabomExcludeTagMap[configStr].insert([excludeTag UTF8String]);
-                _configurations[configStr].metabomExcludeTags.insert([excludeTag UTF8String]);
-            }
-        }
-
-        if (_manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
-            for (NSString* restrictTag in _manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
-                _metabomRestrictedTagMap[configStr].insert([restrictTag UTF8String]);
-                _configurations[configStr].metabomRestrictTags.insert([restrictTag UTF8String]);
-            }
-        }
-
-        _configurations[configStr].metabomTag = configTag;
-        _configurations[configStr].metabomTags.insert(configTag);
-        _configurations[configStr].platformName =
-            [_manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
-
-        if (endsWith(configStr, "InternalOS")) {
-            _configurations[configStr].disposition = "internal";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("InternalOS"));
-        } else if (endsWith(configStr, "VendorOS")) {
-            _configurations[configStr].disposition = "internal";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorOS"));
-        } else if (endsWith(configStr, "VendorUIOS")) {
-            _configurations[configStr].disposition = "internal";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorUIOS"));
-        } else if (endsWith(configStr, "CarrierOS")) {
-            _configurations[configStr].disposition = "internal";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("CarrierOS"));
-        } else if (endsWith(configStr, "FactoryOS")) {
-            _configurations[configStr].disposition = "internal";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("FactoryOS"));
-        } else if (endsWith(configStr, "DesenseOS")) {
-            _configurations[configStr].disposition = "internal";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DesenseOS"));
-        } else if (endsWith(configStr, "MinosOS")) {
-            _configurations[configStr].disposition = "minos";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
-        } else if (endsWith(configStr, "DemoOS")) {
-            _configurations[configStr].disposition = "demo";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DemoOS"));
-        } else if (endsWith(configStr, "MinosOS")) {
-            _configurations[configStr].disposition = "minos";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
-        } else if (endsWith(configStr, "DeveloperOS")) {
-            _configurations[configStr].disposition = "user";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DeveloperOS"));
-        } else if (endsWith(configStr, "OS")) {
-            _configurations[configStr].disposition = "user";
-            _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("OS"));
-        }
-
-        for (NSString* architecture in _manifestDict[@"configurations"][configuration][@"architectures"]) {
-            //HACK until B&I stops mastering armv7s
-            if ([architecture isEqual:@"armv7s"]) break;
-            _configurations[configStr].architectures[[architecture UTF8String]] = Architecture();
-        }
-    }
-
-    setVersion([_manifestDict[@"manifest-version"] unsignedIntValue]);
-    setBuild([_manifestDict[@"build"] UTF8String]);
-    if (_manifestDict[@"dylibOrderFile"]) {
-        setDylibOrderFile([_manifestDict[@"dylibOrderFile"] UTF8String]);
-    }
-    if (_manifestDict[@"dirtyDataOrderFile"]) {
-        setDirtyDataOrderFile([_manifestDict[@"dirtyDataOrderFile"] UTF8String]);
-    }
-
-    if (populateIt)
-        populate(std::set<std::string>());
-}
-
-// Perform the initialization that populateIt=false omitted.
-void Manifest::populate(const std::set<std::string>& overlays)
-{
-    auto    metabom = MBMetabomOpen(metabomFile().c_str(), false);
-    auto    metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
-    MBEntry entry;
-
-    // Collect every architecture from the configurations.
-    std::set<std::string> architectures;
-    for (auto& configuration : _configurations) {
-        for (auto& arch : configuration.second.architectures) {
-            architectures.insert(arch.first);
-        }
-    }
-
-    auto filterPath = [](std::string& entryPath) {
-        // Skip artifacts that happen to be in the build chain
-        if ( startsWith(entryPath, "/Applications/Xcode.app") ) {
-            return true;
-        }
-
-        // Skip variants we can't deal with
-        if ( endsWith(entryPath, "_profile.dylib") || endsWith(entryPath, "_debug.dylib") || endsWith(entryPath, "_profile") || endsWith(entryPath, "_debug") || endsWith(entryPath, "/CoreADI") ) {
-            return true;
-        }
-
-        // Skip images that are only used in InternalOS variants
-        if ( startsWith(entryPath, "/AppleInternal/") || startsWith(entryPath, "/usr/local/") || startsWith(entryPath, "/Developer/")) {
-            return true;
-        }
-
-        // Skip genCache generated dylibs
-        if ( endsWith(entryPath, "/System/Library/Caches/com.apple.xpc/sdk.dylib") || endsWith(entryPath, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib")) {
-            return true;
-        }
-
-        return false;
-    };
-
-    __block std::set<std::string> seenPaths;
-
-    // FIXME error handling (NULL metabom)
-
-    //First we iterate through the bom and build our objects
-    while ((entry = MBIteratorNext(metabomEnumerator))) {
-        BOMFSObject  fsObject = MBEntryGetFSObject(entry);
-        BOMFSObjType entryType = BOMFSObjectType(fsObject);
-        std::string  entryPath = BOMFSObjectPathName(fsObject);
-        if (entryPath[0] == '.') {
-            entryPath.erase(0, 1);
-        }
-
-        if (filterPath(entryPath))
-            continue;
-
-        MBTag tag;
-        auto  tagCount = MBEntryGetNumberOfProjectTags(entry);
-        bool isObjectFile = (entryType == BOMFileType) && BOMFSObjectIsBinaryObject(fsObject);
-        bool isSymlinkFile = entryType == BOMSymlinkType;
-        if ( (isObjectFile || isSymlinkFile) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) {
-            if (tagCount == 1) {
-                MBEntryGetProjectTags(entry, &tag);
-            } else {
-                MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
-                MBEntryGetProjectTags(entry, tags);
-
-                //Sigh, we can have duplicate entries for the same tag, so build a set to work with
-                std::set<std::string> tagStrs;
-                std::map<std::string, MBTag> tagStrMap;
-                for (auto i = 0; i < tagCount; ++i) {
-                    tagStrs.insert(MBMetabomGetProjectForTag(metabom, tags[i]));
-                    tagStrMap.insert(std::make_pair(MBMetabomGetProjectForTag(metabom, tags[i]), tags[i]));
-                }
-
-                if (tagStrs.size() > 1) {
-                    std::string projects;
-                    for (const auto& tagStr : tagStrs) {
-                        if (!projects.empty())
-                            projects += ", ";
-
-                        projects += "'" + tagStr + "'";
-                    }
-                    _diags.warning("Bom entry '%s' is claimed by multiple projects: %s, taking first entry", entryPath.c_str(), projects.c_str());
-                }
-                tag = tagStrMap[*tagStrs.begin()];
-                free(tags);
-            }
-
-            std::string projectName = MBMetabomGetProjectForTag(metabom, tag);
-            tagCount = MBEntryGetNumberOfPackageTags(entry);
-            MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
-            MBEntryGetPackageTags(entry, tags);
-            std::set<std::string> tagStrs;
-
-            for (auto i = 0; i < tagCount; ++i) {
-                tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i]));
-            }
-
-            if (isObjectFile) {
-                _metabomTagMap.insert(std::make_pair(entryPath, tagStrs));
-
-                bool foundParser = false;
-                for (const auto& overlay : overlays) {
-                    if (loadParsers(overlay + "/" + entryPath, entryPath, architectures)) {
-                        foundParser = true;
-                        _diags.verbose("Taking '%s' from overlay instead of dylib cache\n", entryPath.c_str());
-                        break;
-                    }
-                    if (_diags.hasError()) {
-                        _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
-                        _diags.clearError();
-                    }
-                }
-
-                if (!foundParser) {
-                    foundParser = loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures);
-                    if (_diags.hasError()) {
-                        _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
-                        _diags.clearError();
-                    }
-                }
-
-                if (foundParser)
-                    seenPaths.insert(entryPath);
-            } else if (isSymlinkFile) {
-                const char* target = BOMFSObjectSymlinkTarget(fsObject);
-                _symlinks.push_back({ entryPath, target });
-                _metabomSymlinkTagMap.insert(std::make_pair(entryPath, tagStrs));
-            }
-        }
-    }
-
-    MBIteratorFree(metabomEnumerator);
-    MBMetabomFree(metabom);
-
-    // Add files from the overlays
-    for (const auto& overlay : overlays) {
-        iterateDirectoryTree("", overlay, ^bool(const std::string &dirPath) {
-            return false;
-        }, ^(const std::string &path, const struct stat &statBuf) {
-            std::string relativePath = path;
-            if (relativePath.size() < overlay.size())
-                return;
-            relativePath = relativePath.substr(overlay.size());
-            if (relativePath.front() != '/')
-                return;
-            if (filterPath(relativePath))
-                return;
-            // Filter out dylibs we've seen already
-            if (seenPaths.count(relativePath))
-                return;
-            if (loadParsers(path, relativePath, architectures)) {
-                seenPaths.insert(relativePath);
-                // Get the tags from libSystem, assuming that will work.
-                std::set<std::string> tagStrs;
-                auto it = _metabomTagMap.find("/usr/lib/libSystem.B.dylib");
-                if (it != _metabomTagMap.end())
-                    tagStrs = it->second;
-                _metabomTagMap.insert(std::make_pair(relativePath, tagStrs));
-                _diags.verbose("Taking '%s' from overlay instead of dylib cache\n", relativePath.c_str());
-                return;
-            }
-            if (_diags.hasError()) {
-                _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
-                _diags.clearError();
-            }
-        },
-        true, true);
-    }
-
-}
-
-void Manifest::insert(std::vector<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo) {
-    auto info = infoForUUID(imageInfo.uuid);
-    auto runtimePath = info.runtimePath;
-    mappedMachOs.emplace_back(runtimePath, info.mh, info.size, false, false, info.sliceFileOffset, 0, 0);
-}
-
-std::vector<DyldSharedCache::MappedMachO> Manifest::dylibsForCache(const std::string& configuration, const std::string& architecture)
-{
-    std::vector<DyldSharedCache::MappedMachO> retval;
-    const auto&                               dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
-    for (const auto& dylib : dylibs) {
-        if (dylib.second.included) {
-            insert(retval, dylib.second);
-        }
-    }
-    return retval;
-}
-
-std::vector<DyldSharedCache::MappedMachO> Manifest::otherDylibsAndBundles(const std::string& configuration, const std::string& architecture)
-{
-    std::vector<DyldSharedCache::MappedMachO> retval;
-    const auto&                               dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
-    for (const auto& dylib : dylibs) {
-        if (!dylib.second.included) {
-            const UUIDInfo& info = infoForUUID(dylib.second.uuid);
-            if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
-                insert(retval, dylib.second);
-        }
-    }
-
-    const auto& bundles = _configurations[configuration].architectures[architecture].results.bundles;
-    for (const auto& bundle : bundles) {
-        const UUIDInfo& info = infoForUUID(bundle.second.uuid);
-        if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
-            insert(retval, bundle.second);
-    }
-
-    return retval;
-}
-
-std::vector<DyldSharedCache::MappedMachO> Manifest::mainExecutables(const std::string& configuration, const std::string& architecture)
-{
-    std::vector<DyldSharedCache::MappedMachO> retval;
-    const auto&                               executables = _configurations[configuration].architectures[architecture].results.executables;
-    for (const auto& executable : executables) {
-        insert(retval, executable.second);
-    }
-
-    return retval;
-}
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wrange-loop-analysis"
-bool Manifest::filterForConfig(const std::string& configName)
-{
-    for (const auto configuration : _configurations) {
-        if (configName == configuration.first) {
-            std::map<std::string, Configuration> filteredConfigs;
-            filteredConfigs[configName] = configuration.second;
-
-            _configurations = filteredConfigs;
-
-            for (auto& arch : configuration.second.architectures) {
-                arch.second.results = Manifest::Results();
-            }
-            return true;
-        }
-    }
-    return false;
-}
-#pragma clang diagnostic pop
-
-std::set<std::string> Manifest::resultsForConfiguration(const std::string& configName) {
-    std::set<std::string> results;
-    NSDictionary* configurationResults = _manifestDict[@"results"][[NSString stringWithUTF8String:configName.c_str()]];
-    for (NSString* arch in configurationResults) {
-        NSDictionary* dylibs = configurationResults[arch][@"dylibs"];
-        for (NSString* dylib in dylibs) {
-            NSDictionary* dylibDict = dylibs[dylib];
-            if ([dylibDict[@"included"] boolValue])
-                results.insert([dylib UTF8String]);
-        }
-    }
-    return results;
-}
-
-void Manifest::dedupeDispositions(void) {
-    // Since this is all hacky and inference based for now only do it for iOS until XBS
-    // is reved to give us real info. All the other platforms are way smaller anyway.
-    if (_platform != Platform::iOS)
-        return;
-
-    std::map<std::pair<std::string, std::string>, std::set<std::string>> dispositionSets;
-
-    for (const auto& configuration : _configurations) {
-        dispositionSets[std::make_pair(configuration.second.device, configuration.second.disposition)].insert(configuration.first);
-    }
-
-    for (const auto& dSet : dispositionSets) {
-        for (const auto &c1 : dSet.second) {
-            for (const auto &c2 : dSet.second) {
-                _configurations[c1].metabomTags.insert(_configurations[c2].metabomTag);
-            }
-        }
-    }
-}
-
-void Manifest::calculateClosure()
-{
-    auto closureSemaphore = dispatch_semaphore_create(32);
-    auto closureGroup = dispatch_group_create();
-    auto closureQueue = dispatch_queue_create("com.apple.dyld.cache.closure", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
-
-    dedupeDispositions();
-    for (auto& config : _configurations) {
-        for (auto& arch : config.second.architectures) {
-            dispatch_semaphore_wait(closureSemaphore, DISPATCH_TIME_FOREVER);
-            dispatch_group_async(closureGroup, closureQueue, [&] {
-                calculateClosure(config.first, arch.first);
-                dispatch_semaphore_signal(closureSemaphore);
-            });
-        }
-    }
-
-    dispatch_group_wait(closureGroup, DISPATCH_TIME_FOREVER);
-}
-
-void Manifest::remove(const std::string& config, const std::string& arch)
-{
-    if (_configurations.count(config))
-        _configurations[config].architectures.erase(arch);
-}
-
-
-void Manifest::calculateClosure(const std::string& configuration, const std::string& architecture)
-{
-    __block auto&   configManifest = _configurations[configuration];
-    __block auto&   archManifest = _configurations[configuration].architectures[architecture];
-    __block std::set<UUID> newUuids;
-    std::set<UUID>         processedUuids;
-    std::set<UUID>         cachedUUIDs;
-
-    // Seed anchors
-    for (auto& uuidInfo : _uuidMap) {
-        auto info = uuidInfo.second;
-        if (info.arch != architecture) {
-            continue;
-        }
-        
-        auto i = _metabomTagMap.find(info.runtimePath);
-        assert(i != _metabomTagMap.end());
-        auto tags = i->second;
-        if (!is_disjoint(tags, configManifest.metabomTags)) {
-            newUuids.insert(info.uuid);
-        }
-    }
-
-    __block std::set<UUID>         removedUUIDs;
-
-    // Pull in all dependencies
-    while (!newUuids.empty()) {
-        std::set<UUID> uuidsToProcess = newUuids;
-        newUuids.clear();
-
-        for (const auto& uuid : uuidsToProcess) {
-            if (processedUuids.count(uuid) > 0) {
-                continue;
-            }
-            processedUuids.insert(uuid);
-
-            const MachOAnalyzer* mh = machOForUUID(uuid);
-            auto runtimePath = runtimePathForUUID(uuid);
-            assert(mh != nullptr);
-
-            mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
-                auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
-                if (i != _installNameMap.end())
-                newUuids.insert(i->second);
-            });
-
-            if (mh->isDylib()) {
-                // Add the dylib to the results
-                if (archManifest.results.dylibs.count(uuid) == 0 ) {
-                    archManifest.results.dylibs[uuid].uuid = uuid;
-                    archManifest.results.dylibs[uuid].installname = mh->installName();
-                }
-
-                // HACK to insert device specific dylib closures into all caches
-                if ( (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0)
-                    || (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0) ) {
-                    archManifest.results.exclude(mh, "Device specific dylib");
-                    continue;
-                }
-
-                __block std::set<std::string> reasons;
-                if (mh->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* reason) { reasons.insert(reason); })) {
-                    auto i = _metabomTagMap.find(runtimePath);
-                    assert(i != _metabomTagMap.end());
-                    auto restrictions = _metabomRestrictedTagMap.find(configuration);
-                    if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
-                        archManifest.results.exclude(mh, "Dylib '" + runtimePath + "' removed due to explict restriction");
-                    }
-
-                    // It can be placed in the cache, grab its dependents and queue them for inclusion
-                    cachedUUIDs.insert(uuid);
-                } else {
-                    // It can't be placed in the cache, print out the reasons why
-                    std::string reasonString = "Rejected from cached dylibs: " + runtimePath + " " + architecture + " (\"";
-                    for (auto i = reasons.begin(); i != reasons.end(); ++i) {
-                        reasonString += *i;
-                        if (i != --reasons.end()) {
-                            reasonString += "\", \"";
-                        }
-                    }
-                    reasonString += "\")";
-                    archManifest.results.exclude(mh, reasonString);
-                    removedUUIDs.insert(uuid);
-                }
-            } else if (mh->isBundle()) {
-                if (archManifest.results.bundles.count(uuid) == 0) {
-                    archManifest.results.bundles[uuid].uuid = uuid;
-                }
-            } else if (mh->isMainExecutable()) {
-                //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends
-                if (runtimePath == "/sbin/launchd"
-                    || runtimePath == "/usr/local/sbin/launchd.debug"
-                    || runtimePath == "/usr/local/sbin/launchd.development"
-                    || runtimePath == "/usr/libexec/installd") {
-                    continue;
-                }
-                if (archManifest.results.executables.count(uuid) == 0) {
-                    archManifest.results.executables[uuid].uuid = uuid;
-                }
-            }
-        }
-    }
-
-    __block bool                   doAgain = true;
-
-    //Trim out dylibs that are missing dependencies
-    while ( doAgain ) {
-        doAgain = false;
-        for (const auto& uuid : cachedUUIDs) {
-            __block std::set<std::string> badDependencies;
-            const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
-            mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
-                if (isWeak)
-                    return;
-
-                auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
-                if (i == _installNameMap.end() || removedUUIDs.count(i->second)) {
-                    removedUUIDs.insert(uuid);
-                    badDependencies.insert(loadPath);
-                    doAgain = true;
-                }
-
-                if (badDependencies.size()) {
-                    std::string reasonString = "Rejected from cached dylibs: " + std::string(mh->installName()) + " " + architecture + " (\"";
-                    for (auto i = badDependencies.begin(); i != badDependencies.end(); ++i) {
-                        reasonString += *i;
-                        if (i != --badDependencies.end()) {
-                            reasonString += "\", \"";
-                        }
-                    }
-                    reasonString += "\")";
-                    archManifest.results.exclude(mh, reasonString);
-                }
-            });
-        }
-
-        for (const auto& removedUUID : removedUUIDs) {
-            cachedUUIDs.erase(removedUUID);
-        }
-    }
-
-    //Trim out excluded leaf dylibs
-    __block std::set<std::string> linkedDylibs;
-
-    for(const auto& uuid : cachedUUIDs) {
-        const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
-        mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
-            linkedDylibs.insert(loadPath);
-        });
-    }
-
-    for(const auto& uuid : cachedUUIDs) {
-        auto info = infoForUUID(uuid);
-        auto i = _metabomTagMap.find(info.runtimePath);
-        assert(i != _metabomTagMap.end());
-        auto exclusions = _metabomExcludeTagMap.find(configuration);
-        if (exclusions == _metabomExcludeTagMap.end() || is_disjoint(exclusions->second, i->second))
-            continue;
-
-        if (linkedDylibs.count(info.installName) != 0)
-            continue;
-
-        archManifest.results.exclude(*this, info.uuid, "Dylib '" + info.runtimePath + "' excluded leaf node");
-    }
-}
-
-void Manifest::writeJSON(const std::string& path) {
-    NSMutableDictionary* jsonDict = [[NSMutableDictionary alloc] init];
-    for (auto& configuration : _configurations) {
-        jsonDict[cppToObjStr(configuration.first)] = [[NSMutableDictionary alloc] init];
-
-        for (auto& arch : configuration.second.architectures) {
-            NSMutableOrderedSet* includedDylibsSet = [[NSMutableOrderedSet alloc] init];
-            NSMutableOrderedSet* executablesSet = [[NSMutableOrderedSet alloc] init];
-            NSMutableOrderedSet* otherSet = [[NSMutableOrderedSet alloc] init];
-            for (auto& dylib : arch.second.results.dylibs) {
-                NSString *runtimePath = cppToObjStr(runtimePathForUUID(dylib.second.uuid));
-                if (dylib.second.included) {
-                    [includedDylibsSet addObject:runtimePath];
-                } else {
-                    [otherSet addObject:runtimePath];
-                }
-            }
-
-            for (auto& executable : arch.second.results.executables) {
-                NSString *runtimePath = cppToObjStr(runtimePathForUUID(executable.second.uuid));
-                [executablesSet addObject:runtimePath];
-            }
-
-            for (auto& bundle : arch.second.results.bundles) {
-                NSString *runtimePath = cppToObjStr(runtimePathForUUID(bundle.second.uuid));
-                [otherSet addObject:runtimePath];
-            }
-
-            [includedDylibsSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
-                return [obj1 compare:obj2];
-            }];
-
-            [executablesSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
-                return [obj1 compare:obj2];
-            }];
-
-            [otherSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
-                return [obj1 compare:obj2];
-            }];
-
-            jsonDict[cppToObjStr(configuration.first)][cppToObjStr(arch.first)] = @{ @"cachedDylibs" : [includedDylibsSet array], @"mainExecutables" : [executablesSet array], @"other" : [otherSet array]};;
-        }
-    }
-
-    NSError* error = nil;
-    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:0x0 error:&error];
-    (void)[jsonData writeToFile:cppToObjStr(path) atomically:YES];
-}
-
-void Manifest::write(const std::string& path)
-{
-    if (path.empty())
-        return;
-
-    NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
-    NSMutableDictionary* projectDict = [[NSMutableDictionary alloc] init];
-    NSMutableDictionary* configurationsDict = [[NSMutableDictionary alloc] init];
-    NSMutableDictionary* resultsDict = [[NSMutableDictionary alloc] init];
-
-    cacheDict[@"manifest-version"] = @(version());
-    cacheDict[@"build"] = cppToObjStr(build());
-    cacheDict[@"dylibOrderFile"] = cppToObjStr(dylibOrderFile());
-    cacheDict[@"dirtyDataOrderFile"] = cppToObjStr(dirtyDataOrderFile());
-    cacheDict[@"metabomFile"] = cppToObjStr(metabomFile());
-
-    cacheDict[@"projects"] = projectDict;
-    cacheDict[@"results"] = resultsDict;
-    cacheDict[@"configurations"] = configurationsDict;
-
-    for (const auto& project : projects()) {
-        NSMutableArray* sources = [[NSMutableArray alloc] init];
-
-        for (const auto& source : project.second.sources) {
-            [sources addObject:cppToObjStr(source)];
-        }
-
-        projectDict[cppToObjStr(project.first)] = sources;
-    }
-
-    for (auto& configuration : _configurations) {
-        NSMutableArray* archArray = [[NSMutableArray alloc] init];
-        for (auto& arch : configuration.second.architectures) {
-            [archArray addObject:cppToObjStr(arch.first)];
-        }
-
-        NSMutableArray* excludeTags = [[NSMutableArray alloc] init];
-        for (const auto& excludeTag : configuration.second.metabomExcludeTags) {
-            [excludeTags addObject:cppToObjStr(excludeTag)];
-        }
-
-        configurationsDict[cppToObjStr(configuration.first)] = @{
-            @"platformName" : cppToObjStr(configuration.second.platformName),
-            @"metabomTag" : cppToObjStr(configuration.second.metabomTag),
-            @"metabomExcludeTags" : excludeTags,
-            @"architectures" : archArray
-        };
-    }
-
-    for (auto& configuration : _configurations) {
-        NSMutableDictionary* archResultsDict = [[NSMutableDictionary alloc] init];
-        for (auto& arch : configuration.second.architectures) {
-            NSMutableDictionary* dylibsDict = [[NSMutableDictionary alloc] init];
-            NSMutableArray* warningsArray = [[NSMutableArray alloc] init];
-            NSMutableDictionary* devRegionsDict = [[NSMutableDictionary alloc] init];
-            NSMutableDictionary* prodRegionsDict = [[NSMutableDictionary alloc] init];
-            NSString* prodCDHash = cppToObjStr(arch.second.results.productionCache.cdHash);
-            NSString* devCDHash = cppToObjStr(arch.second.results.developmentCache.cdHash);
-
-            for (auto& dylib : arch.second.results.dylibs) {
-                NSMutableDictionary* dylibDict = [[NSMutableDictionary alloc] init];
-                if (dylib.second.included) {
-                    dylibDict[@"included"] = @YES;
-                } else {
-                    dylibDict[@"included"] = @NO;
-                    dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo);
-                }
-                dylibsDict[cppToObjStr(dylib.second.installname)] = dylibDict;
-            }
-
-            for (auto& warning : arch.second.results.warnings) {
-                [warningsArray addObject:cppToObjStr(warning)];
-            }
-
-            BOOL built = arch.second.results.failure.empty();
-            archResultsDict[cppToObjStr(arch.first)] = @{
-                @"dylibs" : dylibsDict,
-                @"built" : @(built),
-                @"failure" : cppToObjStr(arch.second.results.failure),
-                @"productionCache" : @{ @"cdhash" : prodCDHash, @"regions" : prodRegionsDict },
-                @"developmentCache" : @{ @"cdhash" : devCDHash, @"regions" : devRegionsDict },
-                @"warnings" : warningsArray
-            };
-        }
-        resultsDict[cppToObjStr(configuration.first)] = archResultsDict;
-    }
-
-    switch (platform()) {
-    case Platform::iOS:
-        cacheDict[@"platform"] = @"ios";
-        break;
-    case Platform::tvOS:
-        cacheDict[@"platform"] = @"tvos";
-        break;
-    case Platform::watchOS:
-        cacheDict[@"platform"] = @"watchos";
-        break;
-    case Platform::bridgeOS:
-        cacheDict[@"platform"] = @"bridgeos";
-        break;
-    case Platform::macOS:
-        cacheDict[@"platform"] = @"macos";
-        break;
-    case Platform::unknown:
-    case Platform::iOSMac:
-    case Platform::iOS_simulator:
-    case Platform::tvOS_simulator:
-    case Platform::watchOS_simulator:
-    case Platform::driverKit:
-        cacheDict[@"platform"] = @"unknown";
-        break;
-    }
-
-    NSError* error = nil;
-    NSData*  outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
-                                                                 format:NSPropertyListBinaryFormat_v1_0
-                                                                options:0
-                                                                  error:&error];
-    (void)[outData writeToFile:cppToObjStr(path) atomically:YES];
-}
-
-
-void Manifest::forEachMachO(std::string configuration,
-                            std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda) {
-    for (auto& uuidInfo : _uuidMap) {
-        auto i = _metabomTagMap.find(uuidInfo.second.runtimePath);
-        assert(i != _metabomTagMap.end());
-        auto restrictions = _metabomRestrictedTagMap.find(configuration);
-        if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
-            continue;
-        }
-        auto& configManifest = _configurations[configuration];
-        auto exclusions = _metabomExcludeTagMap.find(configuration);
-        bool isExcluded = (exclusions != _metabomExcludeTagMap.end()) && !is_disjoint(exclusions->second, i->second);
-        bool isAnchor = !is_disjoint(i->second, configManifest.metabomTags);
-        bool shouldBeExcludedIfLeaf = isExcluded || !isAnchor;
-        lambda(uuidInfo.second.buildPath, uuidInfo.second.runtimePath, uuidInfo.second.arch, shouldBeExcludedIfLeaf);
-    }
-}
-
-
-void Manifest::forEachSymlink(std::string configuration,
-                            std::function<void(const std::string &fromPath, const std::string &toPath)> lambda) {
-    for (const auto& symlink : _symlinks) {
-        auto i = _metabomSymlinkTagMap.find(symlink.first);
-        assert(i != _metabomSymlinkTagMap.end());
-        auto restrictions = _metabomRestrictedTagMap.find(configuration);
-        if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
-            continue;
-        }
-        lambda(symlink.first, symlink.second);
-    }
-}
-
-} //namespace dyld3
index 085797cba5119942dc69cdc4958c8e793c9751e7..ef2cd13b56321d6ab87b177aaea3bd080e322ba8 100644 (file)
@@ -491,6 +491,7 @@ public:
         if (instanceProperties)      aslrTracker.add(&instanceProperties);
         if (extendedMethodTypes)     aslrTracker.add(&extendedMethodTypes);
         if (demangledName)           aslrTracker.add(&demangledName);
+        if (classProperties)         aslrTracker.add(&classProperties);
     }
 };
 
index 67c31c29c92f1c1a7d3e972ca39d4abf3fdc8027..b47a80ac520c9095467c0050ab201d176dce7d37 100644 (file)
@@ -42,7 +42,7 @@
 #include "MachOAnalyzer.h"
 #include "Diagnostics.h"
 #include "DyldSharedCache.h"
-#include "CacheBuilder.h"
+#include "SharedCacheBuilder.h"
 
 static const bool verbose = false;
 
@@ -120,7 +120,6 @@ private:
     macho_header<P>*                        _mh;
     int64_t                                 _cacheSlide          = 0;
     uint64_t                                _cacheUnslideAddr    = 0;
-    bool                                    _chainedFixups       = false;
     uint32_t                                _linkeditSize        = 0;
     uint64_t                                _linkeditAddr        = 0;
     const uint8_t*                          _linkeditBias        = nullptr;
@@ -149,11 +148,6 @@ StubOptimizer<P>::StubOptimizer(const DyldSharedCache* cache, macho_header<P>* m
 {
     _cacheSlide = (long)cache - cache->unslidLoadAddress();
     _cacheUnslideAddr = cache->unslidLoadAddress();
-#if SUPPORT_ARCH_arm64e
-    _chainedFixups = (strcmp(cache->archName(), "arm64e") == 0);
-#else
-    _chainedFixups = false;
-#endif
     const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
     const uint32_t cmd_count = mh->ncmds();
     macho_segment_command<P>* segCmd;
@@ -415,20 +409,6 @@ void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& never
                                 break;
                             default:
                                 lpValue = (pint_t)P::getP(lpContent[j]);
-
-                                // Fixup threaded rebase/bind
-                                if ( _chainedFixups ) {
-                                    dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
-                                    ptr.raw64 = lpValue;
-                                    assert(ptr.arm64e.authRebase.bind == 0);
-                                    if ( ptr.arm64e.authRebase.auth ) {
-                                        lpValue = (pint_t)(_cacheUnslideAddr + ptr.arm64e.authRebase.target);
-                                    }
-                                    else {
-                                        lpValue = (pint_t)ptr.arm64e.unpackTarget();
-                                    }
-                                }
-
                                 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",
@@ -954,7 +934,7 @@ void bypassStubs(DyldSharedCache* cache, const std::string& archName, std::unord
         delete op;
 }
 
-void CacheBuilder::optimizeAwayStubs()
+void SharedCacheBuilder::optimizeAwayStubs()
 {
     std::unordered_map<uint64_t, uint64_t> targetAddrToOptStubAddr;
 
index 048522b3a6785da5d13b775e2d699ef5b7fcee67..967f8cbdf90584990a6a76deda4aaa7b27936a73 100644 (file)
@@ -186,341 +186,6 @@ private:
 
 
 
-template <typename P>
-class AcceleratorTables {
-public:
-                AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers);
-
-    uint32_t    totalSize() const;
-    void        copyTo(uint8_t* buffer);
-
-private:
-    typedef typename P::E  E;
-
-    struct NodeChain;
-
-    struct DepNode {
-        std::vector<DepNode*>   _dependents;
-        unsigned                _depth;
-        const char*             _installName;
-
-                                DepNode() : _depth(0), _installName(nullptr) { }
-        void                    computeDepth();
-        static void             verifyUnreachable(DepNode* target, NodeChain& chain, Diagnostics& diag, std::unordered_set<DepNode*>& visitedNodes, const std::vector<DepNode*>& from);
-    };
-
-    struct NodeChain {
-        NodeChain*   prev;
-        DepNode*     node;
-    };
-
-    std::unordered_map<macho_header<P>*, DepNode>                   _depDAG;
-    std::vector<dyld_cache_image_info_extra>                        _extraInfo;
-    std::vector<uint8_t>                                            _trieBytes;
-    std::vector<uint16_t>                                           _reExportArray;
-    std::vector<uint16_t>                                           _dependencyArray;
-    std::vector<uint16_t>                                           _bottomUpArray;
-    std::vector<dyld_cache_accelerator_initializer>                 _initializers;
-    std::vector<dyld_cache_accelerator_dof>                         _dofSections;
-    std::vector<dyld_cache_range_entry>                             _rangeTable;
-    std::unordered_map<macho_header<P>*, uint32_t>                  _machHeaderToImageIndex;
-    std::unordered_map<std::string, macho_header<P>*>               _dylibPathToMachHeader;
-    std::unordered_map<macho_header<P>*, LinkeditOptimizer<P>*>     _machHeaderToOptimizer;
-    dyld_cache_accelerator_info                                     _acceleratorInfoHeader;
-};
-
-
-template <typename P>
-void AcceleratorTables<P>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables<P>::DepNode* target, struct AcceleratorTables<P>::NodeChain& chain, Diagnostics& diag,
-                                                                        std::unordered_set<DepNode*>& visitedNodes, const std::vector<AcceleratorTables<P>::DepNode*>& from) {
-    for (DepNode* node : from) {
-        bool foundCycle = (node == target);
-        for (NodeChain* c = &chain; c->prev != nullptr; c = c->prev) {
-            if ( c->node == target ) {
-                foundCycle = true;
-                break;
-            }
-        }
-        if ( foundCycle ) {
-            NodeChain* chp = &chain;
-            std::string msg = std::string("found cycle for ") + target->_installName;
-            while (chp != nullptr) {
-                msg = msg + "\n  " + chp->node->_installName;
-                chp = chp->prev;
-            }
-            diag.warning("%s", msg.c_str());
-            return;
-        }
-
-        if ( visitedNodes.count(node) )
-            continue;
-        visitedNodes.insert(node);
-        NodeChain nextChain;
-        nextChain.prev = &chain;
-        nextChain.node = node;
-        verifyUnreachable(target, nextChain, diag, visitedNodes, node->_dependents);
-    }
-}
-
-const uint16_t kBranchIslandDylibIndex = 0x7FFF;
-
-template <typename P>
-AcceleratorTables<P>::AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers)
-{
-    // build table mapping tables to map between mach_header, index, and optimizer
-    for ( LinkeditOptimizer<P>* op : optimizers ) {
-        _machHeaderToOptimizer[op->machHeader()] = op;
-    }
-    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)cache + cache->header.mappingOffset);
-    uint64_t cacheStartAddress = mappings[0].address;
-    const dyld_cache_image_info* images = (dyld_cache_image_info*)((uint8_t*)cache + cache->header.imagesOffset);
-    for (unsigned i=0; i < cache->header.imagesCount; ++i) {
-        uint64_t segCacheFileOffset = images[i].address - cacheStartAddress;
-        macho_header<P>* mhMapped = (macho_header<P>*)((uint8_t*)cache+segCacheFileOffset);
-        const char* path = (char*)cache + images[i].pathFileOffset;
-        _dylibPathToMachHeader[path] = mhMapped;
-        // don't add alias entries (path offset in pool near start of cache) to header->index map
-        if ( images[i].pathFileOffset > segCacheFileOffset )
-            _machHeaderToImageIndex[mhMapped] = i;
-    }
-
-
-    // build DAG of image dependencies
-    for (LinkeditOptimizer<P>* op : optimizers) {
-        _depDAG[op->machHeader()]._installName = op->installName();
-    }
-    for (LinkeditOptimizer<P>* op : optimizers) {
-        DepNode& node = _depDAG[op->machHeader()];
-        for (const char* depPath : op->getDownwardDependents()) {
-            macho_header<P>* depMH = _dylibPathToMachHeader[depPath];
-            if ( depMH != nullptr ) {
-                DepNode* depNode = &_depDAG[depMH];
-                node._dependents.push_back(depNode);
-            }
-        }
-    }
-
-    // check for cycles in DAG
-    for (auto& entry : _depDAG) {
-        DepNode* node = &entry.second;
-        NodeChain chain;
-        chain.prev = nullptr;
-        chain.node = node;
-        std::unordered_set<DepNode*> visitedNodes;
-        DepNode::verifyUnreachable(node, chain, diag, visitedNodes, node->_dependents);
-    }
-
-    // compute depth for each DAG node
-    for (auto& entry : _depDAG) {
-        entry.second.computeDepth();
-    }
-
-    // build sorted (bottom up) list of images
-    std::vector<macho_header<P>*> sortedMachHeaders;
-    sortedMachHeaders.reserve(optimizers.size());
-    for (LinkeditOptimizer<P>* op : optimizers) {
-        if ( strcmp(op->installName(), "dyld_shared_cache_branch_islands") != 0 )
-            sortedMachHeaders.push_back(op->machHeader());
-        else
-            _machHeaderToImageIndex[op->machHeader()] = kBranchIslandDylibIndex;
-    }
-    std::sort(sortedMachHeaders.begin(), sortedMachHeaders.end(),
-              [&](macho_header<P>* lmh, macho_header<P>* rmh) -> bool {
-                if ( _depDAG[lmh]._depth != _depDAG[rmh]._depth )
-                    return (_depDAG[lmh]._depth < _depDAG[rmh]._depth);
-                else
-                    return (lmh < rmh);
-              });
-
-    // build zeroed array of extra infos
-    dyld_cache_image_info_extra emptyExtra;
-    emptyExtra.exportsTrieAddr              = 0;
-    emptyExtra.weakBindingsAddr             = 0;
-    emptyExtra.exportsTrieSize              = 0;
-    emptyExtra.weakBindingsSize             = 0;
-    emptyExtra.dependentsStartArrayIndex    = 0;
-    emptyExtra.reExportsStartArrayIndex     = 0;
-    _extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra);
-
-    //for ( macho_header<P>* mh : sortedMachHeaders ) {
-    //    fprintf(stderr, "depth: %3d mh: %p  path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName());
-    //}
-
-    // build dependency table
-    _dependencyArray.push_back(0xFFFF); // reserve 0 slot to be "no-dependencies"
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned index = _machHeaderToImageIndex[mh];
-        auto depPaths = op->getAllDependents();
-        if ( depPaths.empty() ) {
-            _extraInfo[index].dependentsStartArrayIndex = 0;
-        }
-        else {
-            _extraInfo[index].dependentsStartArrayIndex = (uint32_t)_dependencyArray.size();
-            auto downPaths = op->getDownwardDependents();
-            for (const char* depPath : depPaths) {
-                macho_header<P>* depMH = _dylibPathToMachHeader[depPath];
-                uint16_t depIndex = _machHeaderToImageIndex[depMH];
-                if ( std::find(downPaths.begin(), downPaths.end(), depPath) == downPaths.end())
-                    depIndex |= 0x8000;
-                _dependencyArray.push_back(depIndex);
-            }
-            _dependencyArray.push_back(0xFFFF); // mark end of list
-       }
-    }
-
-    // build re-exports table
-    _reExportArray.push_back(0xFFFF); // reserve 0 slot to be "no-re-exports"
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned index = _machHeaderToImageIndex[mh];
-        auto reExPaths = op->getReExportPaths();
-        if ( reExPaths.empty() ) {
-            _extraInfo[index].reExportsStartArrayIndex = 0;
-        }
-        else {
-            _extraInfo[index].reExportsStartArrayIndex = (uint32_t)_reExportArray.size();
-            for (const char* reExPath : reExPaths) {
-                macho_header<P>* reExMH = _dylibPathToMachHeader[reExPath];
-                uint32_t reExIndex = _machHeaderToImageIndex[reExMH];
-                _reExportArray.push_back(reExIndex);
-            }
-            _reExportArray.push_back(0xFFFF); // mark end of list
-       }
-    }
-
-    // build ordered list of initializers
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned index = _machHeaderToImageIndex[mh];
-        _bottomUpArray.push_back(index);
-        for (uint64_t initializer : op->initializerAddresses()) {
-            //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
-            dyld_cache_accelerator_initializer entry;
-            entry.functionOffset = (uint32_t)(initializer-cacheStartAddress);
-            entry.imageIndex = _machHeaderToImageIndex[mh];
-            _initializers.push_back(entry);
-        }
-    }
-
-    // build ordered list of DOF sections
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        assert(op != NULL);
-        unsigned imageIndex = _machHeaderToImageIndex[mh];
-        for (auto& sect : op->dofSections()) {
-            //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
-            dyld_cache_accelerator_dof entry;
-            entry.sectionAddress = sect->addr();
-            entry.sectionSize    = (uint32_t)sect->size();
-            entry.imageIndex     = imageIndex;
-            _dofSections.push_back(entry);
-        }
-    }
-
-
-    // register exports trie and weak binding info in each dylib with image extra info
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned index = _machHeaderToImageIndex[mh];
-        _extraInfo[index].exportsTrieAddr  = op->exportsTrieLinkEditOffset() + linkeditStartAddr;
-        _extraInfo[index].exportsTrieSize  = op->exportsTrieLinkEditSize();
-        _extraInfo[index].weakBindingsAddr = op->weakBindingLinkEditOffset() + linkeditStartAddr;
-        _extraInfo[index].weakBindingsSize = op->weakBindingLinkEditSize();
-    }
-
-    // record location of __DATA/__dyld section in libdyld.dylib
-    macho_header<P>* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"];
-    LinkeditOptimizer<P>* libdyldOp = _machHeaderToOptimizer[libdyldMH];
-    uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress();
-
-    // build range table for fast address->image lookups
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned imageIndex = _machHeaderToImageIndex[mh];
-        for (const macho_segment_command<P>* segCmd : op->segCmds()) {
-            if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
-                continue;
-            dyld_cache_range_entry entry;
-            entry.startAddress = segCmd->vmaddr();
-            entry.size         = (uint32_t)segCmd->vmsize();
-            entry.imageIndex   = imageIndex;
-            _rangeTable.push_back(entry);
-        }
-    }
-    std::sort(_rangeTable.begin(), _rangeTable.end(),
-              [&](const dyld_cache_range_entry& lRange, const dyld_cache_range_entry& rRange) -> bool {
-                return (lRange.startAddress < rRange.startAddress);
-              });
-
-    // build trie that maps install names to image index
-    std::vector<DylibIndexTrie::Entry> dylibEntrys;
-    for (auto &x : _dylibPathToMachHeader) {
-        const std::string& path = x.first;
-        unsigned index = _machHeaderToImageIndex[x.second];
-        dylibEntrys.push_back(DylibIndexTrie::Entry(path, DylibIndex(index)));
-    }
-    DylibIndexTrie dylibsTrie(dylibEntrys);
-    dylibsTrie.emit(_trieBytes);
-    while ( (_trieBytes.size() % 4) != 0 )
-        _trieBytes.push_back(0);
-
-    // fill out header
-    _acceleratorInfoHeader.version              = 1;
-    _acceleratorInfoHeader.imageExtrasCount     = (uint32_t)_extraInfo.size();
-    _acceleratorInfoHeader.imagesExtrasOffset   = ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra);
-    _acceleratorInfoHeader.bottomUpListOffset   = _acceleratorInfoHeader.imagesExtrasOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(dyld_cache_image_info_extra);
-    _acceleratorInfoHeader.dylibTrieOffset      = _acceleratorInfoHeader.bottomUpListOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(uint16_t);
-    _acceleratorInfoHeader.dylibTrieSize        = (uint32_t)_trieBytes.size();
-    _acceleratorInfoHeader.initializersOffset   = ALIGN_AS_TYPE(_acceleratorInfoHeader.dylibTrieOffset + _acceleratorInfoHeader.dylibTrieSize, dyld_cache_accelerator_initializer);
-    _acceleratorInfoHeader.initializersCount    = (uint32_t)_initializers.size();
-    _acceleratorInfoHeader.dofSectionsOffset    = ALIGN_AS_TYPE(_acceleratorInfoHeader.initializersOffset + _acceleratorInfoHeader.initializersCount*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer);
-    _acceleratorInfoHeader.dofSectionsCount     = (uint32_t)_dofSections.size();
-    _acceleratorInfoHeader.reExportListOffset   = ALIGN_AS_TYPE(_acceleratorInfoHeader.dofSectionsOffset + _acceleratorInfoHeader.dofSectionsCount*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof);
-    _acceleratorInfoHeader.reExportCount        = (uint32_t)_reExportArray.size();
-    _acceleratorInfoHeader.depListOffset        = ALIGN_AS_TYPE(_acceleratorInfoHeader.reExportListOffset + _acceleratorInfoHeader.reExportCount*sizeof(uint16_t), uint16_t);
-    _acceleratorInfoHeader.depListCount         = (uint32_t)_dependencyArray.size();
-    _acceleratorInfoHeader.rangeTableOffset     = ALIGN_AS_TYPE(_acceleratorInfoHeader.depListOffset + _acceleratorInfoHeader.depListCount*sizeof(uint16_t), dyld_cache_range_entry);
-    _acceleratorInfoHeader.rangeTableCount      = (uint32_t)_rangeTable.size();
-    _acceleratorInfoHeader.dyldSectionAddr      = dyldSectionAddr;
-}
-
-
-template <typename P>
-void AcceleratorTables<P>::DepNode::computeDepth()
-{
-    if ( _depth != 0 )
-        return;
-    _depth = 1;
-    for (DepNode* node : _dependents) {
-        node->computeDepth();
-        if ( node->_depth >= _depth )
-            _depth = node->_depth + 1;
-    }
-}
-
-template <typename P>
-uint32_t AcceleratorTables<P>::totalSize() const
-{
-    return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset + _acceleratorInfoHeader.rangeTableCount*sizeof(dyld_cache_range_entry), 14);
-}
-
-template <typename P>
-void AcceleratorTables<P>::copyTo(uint8_t* buffer)
-{
-    memcpy(buffer, &_acceleratorInfoHeader, sizeof(dyld_cache_accelerator_info));
-    memcpy(&buffer[_acceleratorInfoHeader.imagesExtrasOffset], &_extraInfo[0],       _extraInfo.size()*sizeof(dyld_cache_image_info_extra));
-    memcpy(&buffer[_acceleratorInfoHeader.bottomUpListOffset], &_bottomUpArray[0],   _bottomUpArray.size()*sizeof(uint16_t));
-    memcpy(&buffer[_acceleratorInfoHeader.initializersOffset], &_initializers[0],    _initializers.size()*sizeof(dyld_cache_accelerator_initializer));
-    memcpy(&buffer[_acceleratorInfoHeader.reExportListOffset], &_reExportArray[0],   _reExportArray.size()*sizeof(uint16_t));
-    memcpy(&buffer[_acceleratorInfoHeader.dofSectionsOffset],  &_dofSections[0],     _dofSections.size()*sizeof(dyld_cache_accelerator_dof));
-    memcpy(&buffer[_acceleratorInfoHeader.depListOffset],      &_dependencyArray[0], _dependencyArray.size()*sizeof(uint16_t));
-    memcpy(&buffer[_acceleratorInfoHeader.rangeTableOffset],   &_rangeTable[0],      _rangeTable.size()*sizeof(dyld_cache_range_entry));
-    memcpy(&buffer[_acceleratorInfoHeader.dylibTrieOffset],    &_trieBytes[0],       _trieBytes.size());
-}
-
-
-
 template <typename P>
 LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diag)
 : _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diag)
@@ -621,6 +286,7 @@ LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh,
                     }
                 }
                 break;
+            case LC_DYLD_CHAINED_FIXUPS:
             case LC_SEGMENT_SPLIT_INFO:
                 remove = true;
                 break;
@@ -1089,25 +755,6 @@ void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, std::vector<Lin
     ::free(newLinkEdit);
     builder._readOnlyRegion.sizeInUse = builder._nonLinkEditReadOnlySize + newLinkeditAlignedSize;
 
-    // If making cache for customers, add extra accelerator tables for dyld
-    DyldSharedCache* cacheHeader = (DyldSharedCache*)builder._readExecuteRegion.buffer;
-    if ( builder._options.optimizeStubs ) {
-        uint64_t addrWhereAccTablesWillBe     = builder._readOnlyRegion.unslidLoadAddress+builder._readOnlyRegion.sizeInUse;
-        uint64_t addrWhereMergedLinkWillStart = builder._readOnlyRegion.unslidLoadAddress+builder._nonLinkEditReadOnlySize;
-        AcceleratorTables<P> tables(cacheHeader, addrWhereMergedLinkWillStart, builder._diagnostics, optimizers);
-        uint32_t tablesSize = tables.totalSize();
-        if ( tablesSize < (builder._readOnlyRegion.bufferSize - builder._readOnlyRegion.sizeInUse) ) {
-            tables.copyTo(builder._readOnlyRegion.buffer+builder._readOnlyRegion.sizeInUse);
-            cacheHeader->header.accelerateInfoAddr = addrWhereAccTablesWillBe;
-            cacheHeader->header.accelerateInfoSize = tablesSize;
-            builder._readOnlyRegion.sizeInUse += align(tablesSize, 14);
-            builder._diagnostics.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize/(1024*1024));
-       }
-        else {
-            builder._diagnostics.warning("not enough room to add dyld accelerator tables");
-        }
-    }
-
     // overwrite end of un-opt linkedits to create a new unmapped region for local symbols
     if ( builder._options.excludeLocalSymbols ) {
         const uint32_t entriesOffset = sizeof(dyld_cache_local_symbols_info);
@@ -1141,6 +788,7 @@ void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, std::vector<Lin
             // 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;
@@ -1189,7 +837,7 @@ void LinkeditOptimizer<P>::optimizeLinkedit(CacheBuilder& builder)
 
 void CacheBuilder::optimizeLinkedit()
 {
-    if ( _archLayout->is64 ) {
+    if ( _is64 ) {
         return LinkeditOptimizer<Pointer64<LittleEndian>>::optimizeLinkedit(*this);
     }
     else {
index faf23308873db81c8e6158faf096b2f1b043b76f..1e81c271f282245aa19e9304d81ee4167fdf589f 100644 (file)
@@ -32,7 +32,7 @@
 
 #include "DyldSharedCache.h"
 #include "Diagnostics.h"
-#include "CacheBuilder.h"
+#include "SharedCacheBuilder.h"
 #include "FileAbstraction.hpp"
 #include "MachOFileAbstraction.hpp"
 #include "MachOLoaded.h"
@@ -117,27 +117,11 @@ public:
         _cacheStart         = (uint8_t*)cache;
         _cacheUnslideAddr   = cache->unslidLoadAddress();
         _slide              = (uint64_t)cache - _cacheUnslideAddr;
-#if SUPPORT_ARCH_arm64e
-        _chainedFixups      = (strcmp(cache->archName(), "arm64e") == 0);
-#else
-        _chainedFixups      = false;
-#endif
      }
 
     // Converts from an on disk vmAddr to the real vmAddr
     // That is, for a chained fixup, decodes the chain, for a non-chained fixup, does nothing.
     uint64_t vmAddrForOnDiskVMAddr(uint64_t vmaddr) {
-        if ( _chainedFixups ) {
-            dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
-            ptr.raw64 = vmaddr;
-            assert(ptr.arm64e.authRebase.bind == 0);
-            if ( ptr.arm64e.authRebase.auth ) {
-                vmaddr = _cacheUnslideAddr + ptr.arm64e.authRebase.target;
-            }
-            else {
-                vmaddr = ptr.arm64e.unpackTarget();
-            }
-        }
         return vmaddr;
     }
 
@@ -164,7 +148,6 @@ private:
     uint64_t                        _slide;
     uint64_t                        _cacheUnslideAddr;
     uint8_t*                        _cacheStart;
-    bool                            _chainedFixups;
 };
 
 
@@ -1102,7 +1085,7 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
 
 } // anon namespace
 
-void CacheBuilder::optimizeObjC()
+void SharedCacheBuilder::optimizeObjC()
 {
     uint32_t objcRwFileOffset = (uint32_t)((_objcReadWriteBuffer - _readWriteRegion.buffer) + _readWriteRegion.cacheFileOffset);
     if ( _archLayout->is64 )
@@ -1127,13 +1110,13 @@ static uint32_t hashTableSize(uint32_t maxElements, uint32_t perElementData)
 
 // The goal here is to allocate space in the dyld shared cache (while it is being laid out) that will contain
 // the objc structures that previously were in the __objc_opt_ro section.
-uint32_t CacheBuilder::computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount)
+uint32_t SharedCacheBuilder::computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount)
 {
     return 0xA000 + hashTableSize(selRefCount, 5) + hashTableSize(classDefCount, 12) + hashTableSize(protocolDefCount, 8);
 }
 
 // Space to replace the __objc_opt_rw section.
-uint32_t CacheBuilder::computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount)
+uint32_t SharedCacheBuilder::computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount)
 {
     return 8*imageCount + protocolDefCount*12*(_archLayout->is64 ? 8 : 4);
 }
diff --git a/dyld3/shared-cache/SharedCacheBuilder.cpp b/dyld3/shared-cache/SharedCacheBuilder.cpp
new file mode 100644 (file)
index 0000000..b1523d1
--- /dev/null
@@ -0,0 +1,3535 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <mach/mach_time.h>
+#include <mach/shared_region.h>
+#include <apfs/apfs_fsctl.h>
+
+#include <CommonCrypto/CommonHMAC.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+
+#include "ClosureBuilder.h"
+#include "Closure.h"
+#include "ClosureFileSystemNull.h"
+#include "CodeSigningTypes.h"
+#include "MachOFileAbstraction.hpp"
+#include "SharedCacheBuilder.h"
+
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "Trie.hpp"
+
+#if __has_include("dyld_cache_config.h")
+    #include "dyld_cache_config.h"
+#else
+    #define ARM_SHARED_REGION_START      0x1A000000ULL
+    #define ARM_SHARED_REGION_SIZE       0x26000000ULL
+    #define ARM64_SHARED_REGION_START   0x180000000ULL
+    #define ARM64_SHARED_REGION_SIZE     0x100000000ULL
+#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
+#else
+  #define  ARMV7K_CHAIN_BITS    0xE0000000
+  #define  ARMV7K_MAX           0x20000000
+#endif
+
+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 },
+#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 },
+#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  },
+#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
+};
+
+// These are functions that are interposed by Instruments.app or ASan
+const char* const SharedCacheBuilder::_s_neverStubEliminateSymbols[] = {
+    "___bzero",
+    "___cxa_atexit",
+    "___cxa_throw",
+    "__longjmp",
+    "__objc_autoreleasePoolPop",
+    "_accept",
+    "_access",
+    "_asctime",
+    "_asctime_r",
+    "_asprintf",
+    "_atoi",
+    "_atol",
+    "_atoll",
+    "_calloc",
+    "_chmod",
+    "_chown",
+    "_close",
+    "_confstr",
+    "_ctime",
+    "_ctime_r",
+    "_dispatch_after",
+    "_dispatch_after_f",
+    "_dispatch_async",
+    "_dispatch_async_f",
+    "_dispatch_barrier_async_f",
+    "_dispatch_group_async",
+    "_dispatch_group_async_f",
+    "_dispatch_source_set_cancel_handler",
+    "_dispatch_source_set_event_handler",
+    "_dispatch_sync_f",
+    "_dlclose",
+    "_dlopen",
+    "_dup",
+    "_dup2",
+    "_endgrent",
+    "_endpwent",
+    "_ether_aton",
+    "_ether_hostton",
+    "_ether_line",
+    "_ether_ntoa",
+    "_ether_ntohost",
+    "_fchmod",
+    "_fchown",
+    "_fclose",
+    "_fdopen",
+    "_fflush",
+    "_fopen",
+    "_fork",
+    "_fprintf",
+    "_free",
+    "_freopen",
+    "_frexp",
+    "_frexpf",
+    "_frexpl",
+    "_fscanf",
+    "_fstat",
+    "_fstatfs",
+    "_fstatfs64",
+    "_fsync",
+    "_ftime",
+    "_getaddrinfo",
+    "_getattrlist",
+    "_getcwd",
+    "_getgrent",
+    "_getgrgid",
+    "_getgrgid_r",
+    "_getgrnam",
+    "_getgrnam_r",
+    "_getgroups",
+    "_gethostbyaddr",
+    "_gethostbyname",
+    "_gethostbyname2",
+    "_gethostent",
+    "_getifaddrs",
+    "_getitimer",
+    "_getnameinfo",
+    "_getpass",
+    "_getpeername",
+    "_getpwent",
+    "_getpwnam",
+    "_getpwnam_r",
+    "_getpwuid",
+    "_getpwuid_r",
+    "_getsockname",
+    "_getsockopt",
+    "_gmtime",
+    "_gmtime_r",
+    "_if_indextoname",
+    "_if_nametoindex",
+    "_index",
+    "_inet_aton",
+    "_inet_ntop",
+    "_inet_pton",
+    "_initgroups",
+    "_ioctl",
+    "_lchown",
+    "_lgamma",
+    "_lgammaf",
+    "_lgammal",
+    "_link",
+    "_listxattr",
+    "_localtime",
+    "_localtime_r",
+    "_longjmp",
+    "_lseek",
+    "_lstat",
+    "_malloc",
+    "_malloc_create_zone",
+    "_malloc_default_purgeable_zone",
+    "_malloc_default_zone",
+    "_malloc_good_size",
+    "_malloc_make_nonpurgeable",
+    "_malloc_make_purgeable",
+    "_malloc_set_zone_name",
+    "_mbsnrtowcs",
+    "_mbsrtowcs",
+    "_mbstowcs",
+    "_memchr",
+    "_memcmp",
+    "_memcpy",
+    "_memmove",
+    "_memset",
+    "_mktime",
+    "_mlock",
+    "_mlockall",
+    "_modf",
+    "_modff",
+    "_modfl",
+    "_munlock",
+    "_munlockall",
+    "_objc_autoreleasePoolPop",
+    "_objc_setProperty",
+    "_objc_setProperty_atomic",
+    "_objc_setProperty_atomic_copy",
+    "_objc_setProperty_nonatomic",
+    "_objc_setProperty_nonatomic_copy",
+    "_objc_storeStrong",
+    "_open",
+    "_opendir",
+    "_poll",
+    "_posix_memalign",
+    "_pread",
+    "_printf",
+    "_pthread_attr_getdetachstate",
+    "_pthread_attr_getguardsize",
+    "_pthread_attr_getinheritsched",
+    "_pthread_attr_getschedparam",
+    "_pthread_attr_getschedpolicy",
+    "_pthread_attr_getscope",
+    "_pthread_attr_getstack",
+    "_pthread_attr_getstacksize",
+    "_pthread_condattr_getpshared",
+    "_pthread_create",
+    "_pthread_getschedparam",
+    "_pthread_join",
+    "_pthread_mutex_lock",
+    "_pthread_mutex_unlock",
+    "_pthread_mutexattr_getprioceiling",
+    "_pthread_mutexattr_getprotocol",
+    "_pthread_mutexattr_getpshared",
+    "_pthread_mutexattr_gettype",
+    "_pthread_rwlockattr_getpshared",
+    "_pwrite",
+    "_rand_r",
+    "_read",
+    "_readdir",
+    "_readdir_r",
+    "_readv",
+    "_readv$UNIX2003",
+    "_realloc",
+    "_realpath",
+    "_recv",
+    "_recvfrom",
+    "_recvmsg",
+    "_remquo",
+    "_remquof",
+    "_remquol",
+    "_scanf",
+    "_send",
+    "_sendmsg",
+    "_sendto",
+    "_setattrlist",
+    "_setgrent",
+    "_setitimer",
+    "_setlocale",
+    "_setpwent",
+    "_shm_open",
+    "_shm_unlink",
+    "_sigaction",
+    "_sigemptyset",
+    "_sigfillset",
+    "_siglongjmp",
+    "_signal",
+    "_sigpending",
+    "_sigprocmask",
+    "_sigwait",
+    "_snprintf",
+    "_sprintf",
+    "_sscanf",
+    "_stat",
+    "_statfs",
+    "_statfs64",
+    "_strcasecmp",
+    "_strcat",
+    "_strchr",
+    "_strcmp",
+    "_strcpy",
+    "_strdup",
+    "_strerror",
+    "_strerror_r",
+    "_strlen",
+    "_strncasecmp",
+    "_strncat",
+    "_strncmp",
+    "_strncpy",
+    "_strptime",
+    "_strtoimax",
+    "_strtol",
+    "_strtoll",
+    "_strtoumax",
+    "_tempnam",
+    "_time",
+    "_times",
+    "_tmpnam",
+    "_tsearch",
+    "_unlink",
+    "_valloc",
+    "_vasprintf",
+    "_vfprintf",
+    "_vfscanf",
+    "_vprintf",
+    "_vscanf",
+    "_vsnprintf",
+    "_vsprintf",
+    "_vsscanf",
+    "_wait",
+    "_wait$UNIX2003",
+    "_wait3",
+    "_wait4",
+    "_waitid",
+    "_waitid$UNIX2003",
+    "_waitpid",
+    "_waitpid$UNIX2003",
+    "_wcslen",
+    "_wcsnrtombs",
+    "_wcsrtombs",
+    "_wcstombs",
+    "_wordexp",
+    "_write",
+    "_writev",
+    "_writev$UNIX2003",
+    // <rdar://problem/22050956> always use stubs for C++ symbols that can be overridden
+    "__ZdaPv",
+    "__ZdlPv",
+    "__Znam",
+    "__Znwm",
+
+    nullptr
+};
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+    return (uint32_t)(abstime/1000/1000);
+}
+
+// Handles building a list of input files to the SharedCacheBuilder itself.
+class CacheInputBuilder {
+public:
+    CacheInputBuilder(const dyld3::closure::FileSystem& fileSystem,
+                      const dyld3::GradedArchs& archs, dyld3::Platform reqPlatform)
+    : fileSystem(fileSystem), reqArchs(archs), reqPlatform(reqPlatform) { }
+
+    // Loads and maps any MachOs in the given list of files.
+    void loadMachOs(std::vector<CacheBuilder::InputFile>& inputFiles,
+                    std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
+                    std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
+                    std::vector<CacheBuilder::LoadedMachO>& executables,
+                    std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles) {
+
+        std::map<std::string, uint64_t> dylibInstallNameMap;
+        for (CacheBuilder::InputFile& inputFile : inputFiles) {
+            char realerPath[MAXPATHLEN];
+            dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchs, reqPlatform, realerPath);
+            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+            if (ma == nullptr) {
+                couldNotLoadFiles.emplace_back((CacheBuilder::LoadedMachO){ DyldSharedCache::MappedMachO(), loadedFileInfo, &inputFile });
+                continue;
+            }
+
+            DyldSharedCache::MappedMachO mappedFile(inputFile.path, ma, loadedFileInfo.sliceLen, false, false,
+                                                    loadedFileInfo.sliceOffset, loadedFileInfo.mtime, loadedFileInfo.inode);
+
+            // The file can be loaded with the given slice, but we may still want to exlude it from the cache.
+            if (ma->isDylib()) {
+                std::string installName = ma->installName();
+
+                // Let the platform exclude the file before we do anything else.
+                if (platformExcludesInstallName(installName)) {
+                    inputFile.diag.verbose("Platform excluded file\n");
+                    fileSystem.unloadFile(loadedFileInfo);
+                    continue;
+                }
+
+                if (!ma->canBePlacedInDyldCache(inputFile.path, ^(const char* msg) {
+                    inputFile.diag.warning("Dylib located at '%s' cannot be placed in cache because: %s", inputFile.path, msg);
+                })) {
+                    // TODO: Add exclusion lists here?
+                    // Probably not as we already applied the dylib exclusion list.
+                    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);
+                    }) ) {
+                        fileSystem.unloadFile(loadedFileInfo);
+                        continue;
+                    }
+                    otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+                    continue;
+                }
+
+                // Otherwise see if we have another file with this install name
+                auto iteratorAndInserted = dylibInstallNameMap.insert(std::make_pair(installName, dylibsToCache.size()));
+                if (iteratorAndInserted.second) {
+                    // We inserted the dylib so we haven't seen another with this name.
+                    if (installName[0] != '@' && installName != inputFile.path) {
+                        inputFile.diag.warning("Dylib located at '%s' has installname '%s'", inputFile.path, installName.c_str());
+                    }
+
+                    dylibsToCache.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+                } else {
+                    // We didn't insert this one so we've seen it before.
+                    CacheBuilder::LoadedMachO& previousLoadedMachO = dylibsToCache[iteratorAndInserted.first->second];
+                    inputFile.diag.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), inputFile.path, previousLoadedMachO.mappedFile.runtimePath.c_str());
+
+                    // This is the "Good" one, overwrite
+                    if (inputFile.path == installName) {
+                        // Unload the old one
+                        fileSystem.unloadFile(previousLoadedMachO.loadedFileInfo);
+
+                        // And replace with this one.
+                        previousLoadedMachO.mappedFile = mappedFile;
+                        previousLoadedMachO.loadedFileInfo = loadedFileInfo;
+                    }
+                }
+            } else if (ma->isBundle()) {
+                // TODO: Add exclusion lists here?
+                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);
+                }) ) {
+                    fileSystem.unloadFile(loadedFileInfo);
+                    continue;
+                }
+                otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+            } else if (ma->isDynamicExecutable()) {
+                if (platformExcludesExecutablePath_macOS(inputFile.path)) {
+                    inputFile.diag.verbose("Platform excluded file\n");
+                    fileSystem.unloadFile(loadedFileInfo);
+                    continue;
+                }
+                executables.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+            } else {
+                inputFile.diag.verbose("Unsupported mach file type\n");
+                fileSystem.unloadFile(loadedFileInfo);
+            }
+        }
+    }
+
+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") {
+            return true;
+        }
+        return false;
+    }
+
+    static bool platformExcludesExecutablePath_tvOS(const std::string& path) {
+        return platformExcludesExecutablePath_iOS(path);
+    }
+
+    static bool platformExcludesExecutablePath_watchOS(const std::string& path) {
+        return platformExcludesExecutablePath_iOS(path);
+    }
+
+    static bool platformExcludesExecutablePath_bridgeOS(const std::string& path) {
+        return platformExcludesExecutablePath_iOS(path);
+    }
+
+    // Returns true if the current platform requires that this path be excluded from the shared cache
+    // Note that this overrides any exclusion from anywhere else.
+    bool platformExcludesExecutablePath(const std::string& path) {
+        switch (reqPlatform) {
+            case dyld3::Platform::unknown:
+                return false;
+            case dyld3::Platform::macOS:
+                return platformExcludesExecutablePath_macOS(path);
+            case dyld3::Platform::iOS:
+                return platformExcludesExecutablePath_iOS(path);
+            case dyld3::Platform::tvOS:
+                return platformExcludesExecutablePath_tvOS(path);
+            case dyld3::Platform::watchOS:
+                return platformExcludesExecutablePath_watchOS(path);
+            case dyld3::Platform::bridgeOS:
+                return platformExcludesExecutablePath_bridgeOS(path);
+            case dyld3::Platform::iOSMac:
+                return false;
+            case dyld3::Platform::iOS_simulator:
+                return false;
+            case dyld3::Platform::tvOS_simulator:
+                return false;
+            case dyld3::Platform::watchOS_simulator:
+                return false;
+            case dyld3::Platform::driverKit:
+                return false;
+        }
+    }
+
+    const dyld3::closure::FileSystem&                   fileSystem;
+    const dyld3::GradedArchs&                           reqArchs;
+    dyld3::Platform                                     reqPlatform;
+};
+
+SharedCacheBuilder::SharedCacheBuilder(const DyldSharedCache::CreateOptions& options,
+                                       const dyld3::closure::FileSystem& fileSystem)
+    : CacheBuilder(options, fileSystem) {
+
+    std::string targetArch = options.archs->name();
+    if ( options.forSimulator && (options.archs == &dyld3::GradedArchs::i386) )
+        targetArch = "sim-x86";
+
+    for (const ArchLayout& layout : _s_archLayout) {
+        if ( layout.archName == targetArch ) {
+            _archLayout = &layout;
+            _is64 = _archLayout->is64;
+            break;
+        }
+    }
+
+    if (!_archLayout) {
+        _diagnostics.error("Tool was built without support for: '%s'", targetArch.c_str());
+    }
+}
+
+static void verifySelfContained(std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
+                                std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
+                                std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles)
+{
+    // build map of dylibs
+    __block std::map<std::string, const CacheBuilder::LoadedMachO*> knownDylibs;
+    __block std::map<std::string, const CacheBuilder::LoadedMachO*> allDylibs;
+    for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+        knownDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+        allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+        if (const char* installName = dylib.mappedFile.mh->installName()) {
+            knownDylibs.insert({ installName, &dylib });
+            allDylibs.insert({ installName, &dylib });
+        }
+    }
+
+    for (const CacheBuilder::LoadedMachO& dylib : otherDylibs) {
+        allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+        if (const char* installName = dylib.mappedFile.mh->installName())
+            allDylibs.insert({ installName, &dylib });
+    }
+
+    for (const CacheBuilder::LoadedMachO& dylib : couldNotLoadFiles) {
+        allDylibs.insert({ dylib.inputFile->path, &dylib });
+    }
+
+    // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
+    __block std::map<std::string, std::set<std::string>> badDylibs;
+    __block bool doAgain = true;
+    while ( doAgain ) {
+        doAgain = false;
+        // scan dylib list making sure all dependents are in dylib list
+        for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+            if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+                continue;
+            dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                if (isWeak)
+                    return;
+                if ( knownDylibs.count(loadPath) == 0 ) {
+                    badDylibs[dylib.mappedFile.runtimePath].insert(std::string("Could not find dependency '") + loadPath + "'");
+                    knownDylibs.erase(dylib.mappedFile.runtimePath);
+                    knownDylibs.erase(dylib.mappedFile.mh->installName());
+                    doAgain = true;
+                }
+            });
+        }
+    }
+
+    // Now walk the dylibs which depend on missing dylibs and see if any of them are required binaries.
+    for (auto badDylibsIterator : badDylibs) {
+        const std::string& dylibRuntimePath = badDylibsIterator.first;
+        auto requiredDylibIterator = allDylibs.find(dylibRuntimePath);
+        if (requiredDylibIterator == allDylibs.end())
+            continue;
+        if (!requiredDylibIterator->second->inputFile->mustBeIncluded())
+            continue;
+        // This dylib is required so mark all dependencies as requried too
+        __block std::vector<const CacheBuilder::LoadedMachO*> worklist;
+        worklist.push_back(requiredDylibIterator->second);
+        while (!worklist.empty()) {
+            const CacheBuilder::LoadedMachO* dylib = worklist.back();
+            worklist.pop_back();
+            if (!dylib->mappedFile.mh)
+                continue;
+            dylib->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                if (isWeak)
+                    return;
+                auto dylibIterator = allDylibs.find(loadPath);
+                if (dylibIterator != allDylibs.end()) {
+                    if (dylibIterator->second->inputFile->state == CacheBuilder::InputFile::Unset) {
+                        dylibIterator->second->inputFile->state = CacheBuilder::InputFile::MustBeIncludedForDependent;
+                        worklist.push_back(dylibIterator->second);
+                    }
+                }
+            });
+        }
+    }
+
+    // FIXME: Make this an option we can pass in
+    const bool evictLeafDylibs = true;
+    if (evictLeafDylibs) {
+        doAgain = true;
+        while ( doAgain ) {
+            doAgain = false;
+
+            // build count of how many references there are to each dylib
+            __block std::set<std::string> referencedDylibs;
+            for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+                if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+                    continue;
+                dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+                    referencedDylibs.insert(loadPath);
+                });
+            }
+
+            // find all dylibs not referenced
+            for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+                if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+                    continue;
+                const char* installName = dylib.mappedFile.mh->installName();
+                if ( (referencedDylibs.count(installName) == 0) && (dylib.inputFile->state == CacheBuilder::InputFile::MustBeExcludedIfUnused) ) {
+                    badDylibs[dylib.mappedFile.runtimePath].insert(std::string("It has been explicitly excluded as it is unused"));
+                    doAgain = true;
+                }
+            }
+        }
+    }
+
+    // Move bad dylibs from dylibs to cache to other dylibs.
+    for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+        auto i = badDylibs.find(dylib.mappedFile.runtimePath);
+        if ( i != badDylibs.end()) {
+            otherDylibs.push_back(dylib);
+            for (const std::string& reason : i->second )
+                otherDylibs.back().inputFile->diag.warning("Dylib located at '%s' not placed in shared cache because: %s", dylib.mappedFile.runtimePath.c_str(), reason.c_str());
+        }
+    }
+
+    const auto& badDylibsLambdaRef = badDylibs;
+    dylibsToCache.erase(std::remove_if(dylibsToCache.begin(), dylibsToCache.end(), [&](const CacheBuilder::LoadedMachO& dylib) {
+        if (badDylibsLambdaRef.find(dylib.mappedFile.runtimePath) != badDylibsLambdaRef.end())
+            return true;
+        return false;
+    }), dylibsToCache.end());
+}
+
+// This is the new build API which takes the raw files (which could be FAT) and tries to build a cache from them.
+// We should remove the other build() method, or make it private so that this can wrap it.
+void SharedCacheBuilder::build(std::vector<CacheBuilder::InputFile>& inputFiles,
+                               std::vector<DyldSharedCache::FileAlias>& aliases) {
+    // First filter down to files which are actually MachO's
+    CacheInputBuilder cacheInputBuilder(_fileSystem, *_options.archs, _options.platform);
+
+    std::vector<LoadedMachO> dylibsToCache;
+    std::vector<LoadedMachO> otherDylibs;
+    std::vector<LoadedMachO> executables;
+    std::vector<LoadedMachO> couldNotLoadFiles;
+    cacheInputBuilder.loadMachOs(inputFiles, dylibsToCache, otherDylibs, executables, couldNotLoadFiles);
+
+    verifySelfContained(dylibsToCache, otherDylibs, couldNotLoadFiles);
+
+    // Check for required binaries before we try to build the cache
+    if (!_diagnostics.hasError()) {
+        // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
+        std::string errorString;
+        for (const LoadedMachO& dylib : otherDylibs) {
+            if (dylib.inputFile->mustBeIncluded()) {
+                // An error loading a required file must be propagated up to the top level diagnostic handler.
+                bool gotWarning = false;
+                for (const std::string& warning : dylib.inputFile->diag.warnings()) {
+                    gotWarning = true;
+                    std::string message = warning;
+                    if (message.back() == '\n')
+                        message.pop_back();
+                    if (!errorString.empty())
+                        errorString += "ERROR: ";
+                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + message + "\n";
+                }
+                if (!gotWarning) {
+                    if (!errorString.empty())
+                        errorString += "ERROR: ";
+                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error.  Please report to dyld'\n";
+                }
+            }
+        }
+        for (const LoadedMachO& dylib : couldNotLoadFiles) {
+            if (dylib.inputFile->mustBeIncluded()) {
+                if (dylib.inputFile->diag.hasError()) {
+                    if (!errorString.empty())
+                        errorString += "ERROR: ";
+                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + dylib.inputFile->diag.errorMessage() + "\n";
+                } else {
+                    if (!errorString.empty())
+                        errorString += "ERROR: ";
+                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error.  Please report to dyld'\n";
+
+                }
+            }
+        }
+        if (!errorString.empty()) {
+            _diagnostics.error("%s", errorString.c_str());
+        }
+    }
+
+    if (!_diagnostics.hasError())
+        build(dylibsToCache, otherDylibs, executables, aliases);
+
+    if (!_diagnostics.hasError()) {
+        // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
+        std::string errorString;
+        for (CacheBuilder::InputFile& inputFile : inputFiles) {
+            if (inputFile.mustBeIncluded() && inputFile.diag.hasError()) {
+                // An error loading a required file must be propagated up to the top level diagnostic handler.
+                std::string message = inputFile.diag.errorMessage();
+                if (message.back() == '\n')
+                    message.pop_back();
+                errorString += "Required binary was not included in the shared cache '" + std::string(inputFile.path) + "' because: " + message + "\n";
+            }
+        }
+        if (!errorString.empty()) {
+            _diagnostics.error("%s", errorString.c_str());
+        }
+    }
+
+    // Add all the warnings from the input files to the top level warnings on the main diagnostics object.
+    for (CacheBuilder::InputFile& inputFile : inputFiles) {
+        for (const std::string& warning : inputFile.diag.warnings())
+            _diagnostics.warning("%s", warning.c_str());
+    }
+
+    // Clean up the loaded files
+    for (LoadedMachO& loadedMachO : dylibsToCache)
+        _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+    for (LoadedMachO& loadedMachO : otherDylibs)
+        _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+    for (LoadedMachO& loadedMachO : executables)
+        _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+}
+
+void SharedCacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs,
+                               const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibsInput,
+                               const std::vector<DyldSharedCache::MappedMachO>& osExecutables,
+                               std::vector<DyldSharedCache::FileAlias>& aliases) {
+
+    std::vector<LoadedMachO> dylibsToCache;
+    std::vector<LoadedMachO> otherDylibs;
+    std::vector<LoadedMachO> executables;
+
+    for (const DyldSharedCache::MappedMachO& mappedMachO : dylibs) {
+        dyld3::closure::LoadedFileInfo loadedFileInfo;
+        loadedFileInfo.fileContent      = mappedMachO.mh;
+        loadedFileInfo.fileContentLen   = mappedMachO.length;
+        loadedFileInfo.sliceOffset      = mappedMachO.sliceFileOffset;
+        loadedFileInfo.sliceLen         = mappedMachO.length;
+        loadedFileInfo.inode            = mappedMachO.inode;
+        loadedFileInfo.mtime            = mappedMachO.modTime;
+        loadedFileInfo.path             = mappedMachO.runtimePath.c_str();
+        dylibsToCache.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+    }
+
+    for (const DyldSharedCache::MappedMachO& mappedMachO : otherOsDylibsInput) {
+        dyld3::closure::LoadedFileInfo loadedFileInfo;
+        loadedFileInfo.fileContent      = mappedMachO.mh;
+        loadedFileInfo.fileContentLen   = mappedMachO.length;
+        loadedFileInfo.sliceOffset      = mappedMachO.sliceFileOffset;
+        loadedFileInfo.sliceLen         = mappedMachO.length;
+        loadedFileInfo.inode            = mappedMachO.inode;
+        loadedFileInfo.mtime            = mappedMachO.modTime;
+        loadedFileInfo.path             = mappedMachO.runtimePath.c_str();
+        otherDylibs.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+    }
+
+    for (const DyldSharedCache::MappedMachO& mappedMachO : osExecutables) {
+        dyld3::closure::LoadedFileInfo loadedFileInfo;
+        loadedFileInfo.fileContent      = mappedMachO.mh;
+        loadedFileInfo.fileContentLen   = mappedMachO.length;
+        loadedFileInfo.sliceOffset      = mappedMachO.sliceFileOffset;
+        loadedFileInfo.sliceLen         = mappedMachO.length;
+        loadedFileInfo.inode            = mappedMachO.inode;
+        loadedFileInfo.mtime            = mappedMachO.modTime;
+        loadedFileInfo.path             = mappedMachO.runtimePath.c_str();
+        executables.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+    }
+
+    build(dylibsToCache, otherDylibs, executables, aliases);
+}
+
+void SharedCacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
+                               const std::vector<LoadedMachO>& otherOsDylibsInput,
+                               const std::vector<LoadedMachO>& osExecutables,
+                               std::vector<DyldSharedCache::FileAlias>& aliases)
+{
+    // <rdar://problem/21317611> error out instead of crash if cache has no dylibs
+    // FIXME: plist should specify required vs optional dylibs
+    if ( dylibs.size() < 30 ) {
+        _diagnostics.error("missing required minimum set of dylibs");
+        return;
+    }
+    uint64_t t1 = mach_absolute_time();
+
+    // make copy of dylib list and sort
+    makeSortedDylibs(dylibs, _options.dylibOrdering);
+
+    // allocate space used by largest possible cache plus room for LINKEDITS before optimization
+    _allocatedBufferSize = _archLayout->sharedMemorySize * 1.50;
+    if ( vm_allocate(mach_task_self(), &_fullAllocatedBuffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) {
+        _diagnostics.error("could not allocate buffer");
+        return;
+    }
+
+    // assign addresses for each segment of each dylib in new cache
+    parseCoalescableSegments();
+    processSelectorStrings(osExecutables);
+    assignSegmentAddresses();
+    std::vector<const LoadedMachO*> overflowDylibs;
+    while ( cacheOverflowAmount() != 0 ) {
+        if ( !_options.evictLeafDylibsOnOverflow ) {
+            _diagnostics.error("cache overflow by %lluMB", cacheOverflowAmount() / 1024 / 1024);
+            return;
+        }
+        size_t evictionCount = evictLeafDylibs(cacheOverflowAmount(), overflowDylibs);
+        // re-layout cache
+        for (DylibInfo& dylib : _sortedDylibs)
+            dylib.cacheLocation.clear();
+        _coalescedText.clear();
+        parseCoalescableSegments();
+        processSelectorStrings(osExecutables);
+        assignSegmentAddresses();
+
+        _diagnostics.verbose("cache overflow, evicted %lu leaf dylibs\n", evictionCount);
+    }
+    markPaddingInaccessible();
+
+     // copy all segments into cache
+    uint64_t t2 = mach_absolute_time();
+    writeCacheHeader();
+    copyRawSegments();
+
+    // rebase all dylibs for new location in cache
+    uint64_t t3 = mach_absolute_time();
+    _aslrTracker.setDataRegion(_readWriteRegion.buffer, _readWriteRegion.sizeInUse);
+    if ( !_options.cacheSupportsASLR )
+        _aslrTracker.disable();
+    adjustAllImagesForNewSegmentLocations();
+    if ( _diagnostics.hasError() )
+        return;
+
+    // build ImageArray for dyld3, which has side effect of binding all cached dylibs
+    uint64_t t4 = mach_absolute_time();
+    buildImageArray(aliases);
+    if ( _diagnostics.hasError() )
+        return;
+
+    // optimize ObjC
+    uint64_t t5 = mach_absolute_time();
+    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    if ( _options.optimizeObjC )
+        optimizeObjC();
+    if ( _diagnostics.hasError() )
+        return;
+
+
+    // optimize away stubs
+    uint64_t t6 = mach_absolute_time();
+    if ( _options.optimizeStubs )
+         optimizeAwayStubs();
+
+
+    // 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();
+    optimizeLinkedit();
+
+    // 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) ) {
+        // compute and add dlopen closures for all other dylibs
+        addOtherImageArray(otherOsDylibsInput, overflowDylibs);
+        if ( _diagnostics.hasError() )
+            return;
+
+        // compute and add launch closures to end of read-only region
+        addClosures(osExecutables);
+        if ( _diagnostics.hasError() )
+            return;
+    }
+
+    // update final readOnly region size
+    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCache->header.mappingOffset);
+    mappings[2].size = _readOnlyRegion.sizeInUse;
+    if ( _options.excludeLocalSymbols )
+        dyldCache->header.localSymbolsOffset = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse;
+
+    // record 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;
+        dyldCache->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2);
+    }
+    else {
+        // <rdar://problem/49852839> branch predictor on arm64 currently only looks at low 32-bits, so don't slide cache more than 2GB
+        if ( (_archLayout->sharedMemorySize == 0x100000000) && (_readExecuteRegion.sizeInUse < 0x80000000) )
+            dyldCache->header.maxSlide = 0x80000000 - _readExecuteRegion.sizeInUse;
+        else
+            dyldCache->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (_readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
+    }
+
+    // mark if any input dylibs were built with chained fixups
+    dyldCache->header.builtFromChainedFixups = _someDylibsUsedChainedFixups;
+
+    uint64_t t9 = mach_absolute_time();
+
+    // fill in slide info at start of region[2]
+    // do this last because it modifies pointers in DATA segments
+    if ( _options.cacheSupportsASLR ) {
+#if SUPPORT_ARCH_arm64e
+        if ( strcmp(_archLayout->archName, "arm64e") == 0 )
+            writeSlideInfoV3(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+        else
+#endif
+        if ( _archLayout->is64 )
+            writeSlideInfoV2<Pointer64<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
+        else if ( _archLayout->pointerDeltaMask == 0xC0000000 )
+            writeSlideInfoV4<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+#endif
+        else
+            writeSlideInfoV2<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+    }
+
+    uint64_t t10 = mach_absolute_time();
+
+    // last sanity check on size
+    if ( cacheOverflowAmount() != 0 ) {
+        _diagnostics.error("cache overflow after optimizations 0x%llX -> 0x%llX", _readExecuteRegion.unslidLoadAddress, _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
+        return;
+    }
+
+    // codesignature is part of file, but is not mapped
+    codeSign();
+    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 build %lu closures: %ums\n", osExecutables.size(), absolutetime_to_milliseconds(t9-t8));
+        fprintf(stderr, "time to compute slide info: %ums\n", absolutetime_to_milliseconds(t10-t9));
+        fprintf(stderr, "time to compute UUID and codesign cache file: %ums\n", absolutetime_to_milliseconds(t11-t10));
+    }
+
+    return;
+}
+
+const std::set<std::string> SharedCacheBuilder::warnings()
+{
+    return _diagnostics.warnings();
+}
+
+const std::set<const dyld3::MachOAnalyzer*> SharedCacheBuilder::evictions()
+{
+    return _evictions;
+}
+
+void SharedCacheBuilder::deleteBuffer()
+{
+    // Cache buffer
+    vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _archLayout->sharedMemorySize);
+    _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;
+}
+
+
+void SharedCacheBuilder::makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder)
+{
+    for (const LoadedMachO& dylib : dylibs) {
+        _sortedDylibs.push_back({ &dylib, dylib.mappedFile.runtimePath, {} });
+    }
+
+    std::sort(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& a, const DylibInfo& b) {
+        const auto& orderA = sortOrder.find(a.input->mappedFile.runtimePath);
+        const auto& orderB = sortOrder.find(b.input->mappedFile.runtimePath);
+        bool foundA = (orderA != sortOrder.end());
+        bool foundB = (orderB != sortOrder.end());
+
+        // 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;
+
+        // Sort mac before iOSMac
+        bool isIOSMacA = strncmp(a.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0;
+        bool isIOSMacB = strncmp(b.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0;
+        if (isIOSMacA != isIOSMacB)
+            return !isIOSMacA;
+
+        // Finally sort by path
+        return a.input->mappedFile.runtimePath < b.input->mappedFile.runtimePath;
+    });
+}
+
+struct DylibAndSize
+{
+    const CacheBuilder::LoadedMachO*    input;
+    const char*                         installName;
+    uint64_t                            size;
+};
+
+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 ( _readWriteRegion.sizeInUse > 0x40000000 )
+            return (_readWriteRegion.sizeInUse - 0x40000000);
+
+        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;
+    }
+    else {
+        bool alreadyOptimized = (_readOnlyRegion.sizeInUse != _readOnlyRegion.bufferSize);
+        uint64_t vmSize = _readOnlyRegion.unslidLoadAddress - _readExecuteRegion.unslidLoadAddress;
+        if ( alreadyOptimized )
+            vmSize += _readOnlyRegion.sizeInUse;
+        else if ( _options.excludeLocalSymbols )
+            vmSize += (_readOnlyRegion.sizeInUse * 37/100); // assume locals removal and LINKEDIT optimzation reduces LINKEDITs %37 of original size
+        else
+            vmSize += (_readOnlyRegion.sizeInUse * 80/100); // assume LINKEDIT optimzation reduces LINKEDITs to %80 of original size
+        if ( vmSize > _archLayout->sharedMemorySize )
+            return vmSize - _archLayout->sharedMemorySize;
+    }
+    // fits in shared region
+    return 0;
+}
+
+size_t SharedCacheBuilder::evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs)
+{
+    // build a reverse map of all dylib dependencies
+    __block std::map<std::string, std::set<std::string>> references;
+    std::map<std::string, std::set<std::string>>* referencesPtr = &references;
+    for (const DylibInfo& dylib : _sortedDylibs) {
+        // Esnure we have an entry (even if it is empty)
+        if (references.count(dylib.input->mappedFile.mh->installName()) == 0) {
+            references[dylib.input->mappedFile.mh->installName()] = std::set<std::string>();
+        };
+        dylib.input->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+            references[loadPath].insert(dylib.input->mappedFile.mh->installName());
+        });
+    }
+
+    // Find the sizes of all the dylibs
+    std::vector<DylibAndSize> dylibsToSort;
+    std::vector<DylibAndSize> sortedDylibs;
+    for (const DylibInfo& dylib : _sortedDylibs) {
+        const char* installName = dylib.input->mappedFile.mh->installName();
+        __block uint64_t segsSize = 0;
+        dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
+            if ( strcmp(info.segName, "__LINKEDIT") != 0 )
+                segsSize += info.vmSize;
+        });
+        dylibsToSort.push_back({ dylib.input, installName, segsSize });
+    }
+
+    // Build an ordered list of what to remove. At each step we do following
+    // 1) Find all dylibs that nothing else depends on
+    // 2a) If any of those dylibs are not in the order select the largest one of them
+    // 2b) If all the leaf dylibs are in the order file select the last dylib that appears last in the order file
+    // 3) Remove all entries to the removed file from the reverse dependency map
+    // 4) Go back to one and repeat until there are no more evictable dylibs
+    // This results in us always choosing the locally optimal selection, and then taking into account how that impacts
+    // the dependency graph for subsequent selections
+
+    bool candidateFound = true;
+    while (candidateFound) {
+        candidateFound = false;
+        DylibAndSize candidate;
+        uint64_t candidateOrder = 0;
+        for(const auto& dylib : dylibsToSort) {
+            const auto &i = referencesPtr->find(dylib.installName);
+            assert(i != referencesPtr->end());
+            if (!i->second.empty()) {
+                continue;
+            }
+            const auto& j = _options.dylibOrdering.find(dylib.input->mappedFile.runtimePath);
+            uint64_t order = 0;
+            if (j != _options.dylibOrdering.end()) {
+                order = j->second;
+            } else {
+                // Not in the order file, set order sot it goes to the front of the list
+                order = UINT64_MAX;
+            }
+            if (order > candidateOrder ||
+                (order == UINT64_MAX && candidate.size < dylib.size)) {
+                    // The new file is either a lower priority in the order file
+                    // or the same priority as the candidate but larger
+                    candidate = dylib;
+                    candidateOrder = order;
+                    candidateFound = true;
+            }
+        }
+        if (candidateFound) {
+            sortedDylibs.push_back(candidate);
+            referencesPtr->erase(candidate.installName);
+            for (auto& dependent : references) {
+                (void)dependent.second.erase(candidate.installName);
+            }
+            auto j = std::find_if(dylibsToSort.begin(), dylibsToSort.end(), [&candidate](const DylibAndSize& dylib) {
+                return (strcmp(candidate.installName, dylib.installName) == 0);
+            });
+            if (j != dylibsToSort.end()) {
+                dylibsToSort.erase(j);
+            }
+        }
+    }
+
+     // build set of dylibs that if removed will allow cache to build
+    for (DylibAndSize& dylib : sortedDylibs) {
+        if ( _options.verbose )
+            _diagnostics.warning("to prevent cache overflow, not caching %s", dylib.installName);
+        _evictions.insert(dylib.input->mappedFile.mh);
+        // Track the evicted dylibs so we can try build "other" dlopen closures for them.
+        overflowDylibs.push_back(dylib.input);
+        if ( dylib.size > reductionTarget )
+            break;
+        reductionTarget -= dylib.size;
+    }
+
+    // prune _sortedDylibs
+    _sortedDylibs.erase(std::remove_if(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& dylib) {
+        return (_evictions.count(dylib.input->mappedFile.mh) != 0);
+    }),_sortedDylibs.end());
+
+    return _evictions.size();
+}
+
+
+void SharedCacheBuilder::writeCacheHeader()
+{
+    // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
+    std::string magic = "dyld_v1";
+    magic.append(15 - magic.length() - strlen(_options.archs->name()), ' ');
+    magic.append(_options.archs->name());
+    assert(magic.length() == 15);
+
+    // 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->imagesCount          = (uint32_t)_sortedDylibs.size() + _aliasCount;
+    dyldCacheHeader->dyldBaseAddress      = 0;
+    dyldCacheHeader->codeSignatureOffset  = 0;
+    dyldCacheHeader->codeSignatureSize    = 0;
+    dyldCacheHeader->slideInfoOffset      = _slideInfoFileOffset;
+    dyldCacheHeader->slideInfoSize        = _slideInfoBufferSizeAllocated;
+    dyldCacheHeader->localSymbolsOffset   = 0;
+    dyldCacheHeader->localSymbolsSize     = 0;
+    dyldCacheHeader->cacheType            = _options.optimizeStubs ? kDyldSharedCacheTypeProduction : kDyldSharedCacheTypeDevelopment;
+    dyldCacheHeader->accelerateInfoAddr   = 0;
+    dyldCacheHeader->accelerateInfoSize   = 0;
+    bzero(dyldCacheHeader->uuid, 16);// overwritten later by recomputeCacheUUID()
+    dyldCacheHeader->branchPoolsOffset    = 0;
+    dyldCacheHeader->branchPoolsCount     = 0;
+    dyldCacheHeader->imagesTextOffset     = dyldCacheHeader->imagesOffset + sizeof(dyld_cache_image_info)*dyldCacheHeader->imagesCount;
+    dyldCacheHeader->imagesTextCount      = _sortedDylibs.size();
+    dyldCacheHeader->patchInfoAddr        = 0;
+    dyldCacheHeader->patchInfoSize        = 0;
+    dyldCacheHeader->otherImageGroupAddrUnused  = 0;
+    dyldCacheHeader->otherImageGroupSizeUnused  = 0;
+    dyldCacheHeader->progClosuresAddr     = 0;
+    dyldCacheHeader->progClosuresSize     = 0;
+    dyldCacheHeader->progClosuresTrieAddr = 0;
+    dyldCacheHeader->progClosuresTrieSize = 0;
+    dyldCacheHeader->platform             = (uint8_t)_options.platform;
+    dyldCacheHeader->formatVersion        = dyld3::closure::kFormatVersion;
+    dyldCacheHeader->dylibsExpectedOnDisk = !_options.dylibsRemovedDuringMastering;
+    dyldCacheHeader->simulator            = _options.forSimulator;
+    dyldCacheHeader->locallyBuiltCache    = _options.isLocallyBuiltCache;
+    dyldCacheHeader->builtFromChainedFixups= false;
+    dyldCacheHeader->formatVersion        = dyld3::closure::kFormatVersion;
+    dyldCacheHeader->sharedRegionStart    = _archLayout->sharedMemoryStart;
+    dyldCacheHeader->sharedRegionSize     = _archLayout->sharedMemorySize;
+
+   // fill in mappings
+    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCacheHeader->mappingOffset);
+    mappings[0].address    = _readExecuteRegion.unslidLoadAddress;
+    mappings[0].fileOffset = 0;
+    mappings[0].size       = _readExecuteRegion.sizeInUse;
+    mappings[0].maxProt    = VM_PROT_READ | VM_PROT_EXECUTE;
+    mappings[0].initProt   = VM_PROT_READ | VM_PROT_EXECUTE;
+    mappings[1].address    = _readWriteRegion.unslidLoadAddress;
+    mappings[1].fileOffset = _readExecuteRegion.sizeInUse;
+    mappings[1].size       = _readWriteRegion.sizeInUse;
+    mappings[1].maxProt    = VM_PROT_READ | VM_PROT_WRITE;
+    mappings[1].initProt   = VM_PROT_READ | VM_PROT_WRITE;
+    mappings[2].address    = _readOnlyRegion.unslidLoadAddress;
+    mappings[2].fileOffset = _readExecuteRegion.sizeInUse + _readWriteRegion.sizeInUse;
+    mappings[2].size       = _readOnlyRegion.sizeInUse;
+    mappings[2].maxProt    = VM_PROT_READ;
+    mappings[2].initProt   = VM_PROT_READ;
+
+    // fill in image table
+    dyld_cache_image_info* images = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
+    for (const DylibInfo& dylib : _sortedDylibs) {
+        const char* installName = dylib.input->mappedFile.mh->installName();
+        images->address = dylib.cacheLocation[0].dstCacheUnslidAddress;
+        if ( _options.dylibsRemovedDuringMastering ) {
+            images->modTime = 0;
+            images->inode   = pathHash(installName);
+        }
+        else {
+            images->modTime = dylib.input->mappedFile.modTime;
+            images->inode   = dylib.input->mappedFile.inode;
+        }
+        uint32_t installNameOffsetInTEXT =  (uint32_t)(installName - (char*)dylib.input->mappedFile.mh);
+        images->pathFileOffset = (uint32_t)dylib.cacheLocation[0].dstCacheFileOffset + installNameOffsetInTEXT;
+        ++images;
+    }
+    // append aliases image records and strings
+/*
+    for (auto &dylib : _dylibs) {
+        if (!dylib->installNameAliases.empty()) {
+            for (const std::string& alias : dylib->installNameAliases) {
+                images->set_address(_segmentMap[dylib][0].address);
+                if (_manifest.platform() == "osx") {
+                    images->modTime = dylib->lastModTime;
+                    images->inode = dylib->inode;
+                }
+                else {
+                    images->modTime = 0;
+                    images->inode = pathHash(alias.c_str());
+                }
+                images->pathFileOffset = offset;
+                //fprintf(stderr, "adding alias %s for %s\n", alias.c_str(), dylib->installName.c_str());
+                ::strcpy((char*)&_buffer[offset], alias.c_str());
+                offset += alias.size() + 1;
+                ++images;
+            }
+        }
+    }
+*/
+    // calculate start of text image array and trailing string pool
+    dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesTextOffset);
+    uint32_t stringOffset = (uint32_t)(dyldCacheHeader->imagesTextOffset + sizeof(dyld_cache_image_text_info) * _sortedDylibs.size());
+
+    // write text image array and image names pool at same time
+    for (const DylibInfo& dylib : _sortedDylibs) {
+        dylib.input->mappedFile.mh->getUuid(textImages->uuid);
+        textImages->loadAddress     = dylib.cacheLocation[0].dstCacheUnslidAddress;
+        textImages->textSegmentSize = (uint32_t)dylib.cacheLocation[0].dstCacheSegmentSize;
+        textImages->pathOffset      = stringOffset;
+        const char* installName = dylib.input->mappedFile.mh->installName();
+        ::strcpy((char*)_readExecuteRegion.buffer + stringOffset, installName);
+        stringOffset += (uint32_t)strlen(installName)+1;
+        ++textImages;
+    }
+
+    // make sure header did not overflow into first mapped image
+    const dyld_cache_image_info* firstImage = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
+    assert(stringOffset <= (firstImage->address - mappings[0].address));
+}
+
+void SharedCacheBuilder::processSelectorStrings(const std::vector<LoadedMachO>& executables) {
+    const bool log = false;
+
+    _selectorStringsFromExecutables = 0;
+
+    // Don't do this optimisation on watchOS where the shared cache is too small
+    if (_options.platform == dyld3::Platform::watchOS)
+        return;
+
+    // Get the method name coalesced section as that is where we need to put these strings
+    CacheBuilder::CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData("__objc_methname");
+    for (const LoadedMachO& executable : executables) {
+        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)executable.loadedFileInfo.fileContent;
+
+        uint64_t sizeBeforeProcessing = cacheStringSection.bufferSize;
+
+        ma->forEachObjCMethodName(^(const char *methodName) {
+            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 (log) printf("Selector: %s -> %s\n", ma->installName(), methodName);
+                ++_selectorStringsFromExecutables;
+            }
+        });
+
+        uint64_t sizeAfterProcessing = cacheStringSection.bufferSize;
+        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);
+}
+
+void SharedCacheBuilder::parseCoalescableSegments() {
+    const bool log = false;
+
+    for (DylibInfo& dylib : _sortedDylibs)
+        _coalescedText.parseCoalescableText(dylib.input->mappedFile.mh, dylib.textCoalescer);
+
+    if (log) {
+        for (const char* section : CacheCoalescedText::SupportedSections) {
+            CacheCoalescedText::StringSection& sectionData = _coalescedText.getSectionData(section);
+            printf("Coalesced %s from % 10lld -> % 10d, saving % 10lld bytes\n", section,
+                   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);
+    }
+    //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);
+    }
+#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)
+                    return;
+                if ( dylib.textCoalescer.sectionWasCoalesced(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             = _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) {
+            __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 - _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)(_readExecuteRegion.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;
+            });
+        }
+    }
+
+    // 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);
+    _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 = _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;
+        });
+    }
+
+    // layout all __DATA segments (and other r/w non-dirty, non-const) segments
+    for (DylibInfo& dylib : _sortedDylibs) {
+        __block uint64_t textSegVmAddr = 0;
+       dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                textSegVmAddr = segInfo.vmAddr;
+            if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
+                return;
+            if ( _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;
+        });
+    }
+
+    // layout all __DATA_DIRTY segments, sorted (FIXME)
+    const size_t dylibCount = _sortedDylibs.size();
+    uint32_t dirtyDataSortIndexes[dylibCount];
+    for (size_t i=0; i < dylibCount; ++i)
+        dirtyDataSortIndexes[i] = (uint32_t)i;
+    std::sort(&dirtyDataSortIndexes[0], &dirtyDataSortIndexes[dylibCount], [&](const uint32_t& a, const uint32_t& b) {
+        const auto& orderA = _options.dirtyDataSegmentOrdering.find(_sortedDylibs[a].input->mappedFile.runtimePath);
+        const auto& orderB = _options.dirtyDataSegmentOrdering.find(_sortedDylibs[b].input->mappedFile.runtimePath);
+        bool foundA = (orderA != _options.dirtyDataSegmentOrdering.end());
+        bool foundB = (orderB != _options.dirtyDataSegmentOrdering.end());
+
+        // Order all __DATA_DIRTY segments specified in the order file first, in the order specified in the file,
+        // followed by any other __DATA_DIRTY segments in lexicographic order.
+        if ( foundA && foundB )
+            return orderA->second < orderB->second;
+        else if ( foundA )
+            return true;
+        else if ( foundB )
+             return false;
+        else
+             return _sortedDylibs[a].input->mappedFile.runtimePath < _sortedDylibs[b].input->mappedFile.runtimePath;
+    });
+    addr = align(addr, 12);
+    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)
+                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 __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;
+            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;
+        });
+    }
+
+    // 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;
+
+    // align DATA region end
+    uint64_t endDataAddress = align(addr, _archLayout->sharedRegionAlignP2);
+    _readWriteRegion.bufferSize   = endDataAddress - _readWriteRegion.unslidLoadAddress;
+    _readWriteRegion.sizeInUse    = _readWriteRegion.bufferSize;
+
+    // start read-only region
+    if ( _archLayout->sharedRegionsAreDiscontiguous )
+        addr = _archLayout->sharedMemoryStart + 0xA0000000;
+    else
+        addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2);
+    _readOnlyRegion.buffer               = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart;
+    _readOnlyRegion.bufferSize           = 0;
+    _readOnlyRegion.sizeInUse            = 0;
+    _readOnlyRegion.unslidLoadAddress    = addr;
+    _readOnlyRegion.cacheFileOffset      = _readWriteRegion.cacheFileOffset + _readWriteRegion.sizeInUse;
+
+    // reserve space for kernel ASLR slide info at start of r/o region
+    if ( _options.cacheSupportsASLR ) {
+        size_t slideInfoSize = sizeof(dyld_cache_slide_info);
+        slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info2));
+        slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info3));
+        slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info4));
+        _slideInfoBufferSizeAllocated = align(slideInfoSize + (_readWriteRegion.sizeInUse/4096) * _archLayout->slideInfoBytesPerPage + 0x4000, _archLayout->sharedRegionAlignP2);
+        _slideInfoFileOffset = _readOnlyRegion.cacheFileOffset;
+        addr += _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;
+
+                // 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;
+    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));
+            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+            uint64_t offsetInRegion = addr - _readOnlyRegion.unslidLoadAddress;
+            SegmentMappingInfo loc;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
+            loc.dstSegment             = _readOnlyRegion.buffer + offsetInRegion;
+            loc.dstCacheUnslidAddress  = addr;
+            loc.dstCacheFileOffset     = (uint32_t)(_readOnlyRegion.cacheFileOffset + offsetInRegion);
+            loc.dstCacheSegmentSize    = (uint32_t)align(segInfo.sizeOfSections, 12);
+            loc.dstCacheFileSize       = (uint32_t)copySize;
+            loc.copySegmentSize        = (uint32_t)copySize;
+            loc.srcSegmentIndex        = segInfo.segIndex;
+            dylib.cacheLocation.push_back(loc);
+            addr += loc.dstCacheSegmentSize;
+        });
+    }
+
+    // align r/o region end
+    uint64_t endReadOnlyAddress = align(addr, _archLayout->sharedRegionAlignP2);
+    _readOnlyRegion.bufferSize  = endReadOnlyAddress - _readOnlyRegion.unslidLoadAddress;
+    _readOnlyRegion.sizeInUse   = _readOnlyRegion.bufferSize;
+
+    //fprintf(stderr, "RX region=%p -> %p, logical addr=0x%llX\n", _readExecuteRegion.buffer, _readExecuteRegion.buffer+_readExecuteRegion.bufferSize, _readExecuteRegion.unslidLoadAddress);
+    //fprintf(stderr, "RW region=%p -> %p, logical addr=0x%llX\n", _readWriteRegion.buffer,   _readWriteRegion.buffer+_readWriteRegion.bufferSize, _readWriteRegion.unslidLoadAddress);
+    //fprintf(stderr, "RO region=%p -> %p, logical addr=0x%llX\n", _readOnlyRegion.buffer,    _readOnlyRegion.buffer+_readOnlyRegion.bufferSize, _readOnlyRegion.unslidLoadAddress);
+
+    // sort SegmentMappingInfo for each image to be in the same order as original segments
+    for (DylibInfo& dylib : _sortedDylibs) {
+        std::sort(dylib.cacheLocation.begin(), dylib.cacheLocation.end(), [&](const SegmentMappingInfo& a, const SegmentMappingInfo& b) {
+            return a.srcSegmentIndex < b.srcSegmentIndex;
+        });
+    }
+}
+
+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;
+}
+
+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;
+    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;
+    return patch;
+}
+
+void SharedCacheBuilder::buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases)
+{
+    typedef dyld3::closure::ClosureBuilder::CachedDylibInfo         CachedDylibInfo;
+
+    // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray()
+    __block std::vector<CachedDylibInfo> dylibInfos;
+    __block std::unordered_map<dyld3::closure::ImageNum, const dyld3::MachOLoaded*> imageNumToML;
+    DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    cache->forEachImage(^(const mach_header* mh, const char* installName) {
+        uint64_t mtime;
+        uint64_t inode;
+        cache->getIndexedImageEntry((uint32_t)dylibInfos.size(), mtime, inode);
+        CachedDylibInfo entry;
+        entry.fileInfo.fileContent  = mh;
+        entry.fileInfo.path         = installName;
+        entry.fileInfo.sliceOffset  = 0;
+        entry.fileInfo.inode        = inode;
+        entry.fileInfo.mtime        = mtime;
+        dylibInfos.push_back(entry);
+        imageNumToML[(dyld3::closure::ImageNum)(dylibInfos.size())] = (dyld3::MachOLoaded*)mh;
+    });
+
+    // Convert symlinks from STL to simple char pointers.
+    std::vector<dyld3::closure::ClosureBuilder::CachedDylibAlias> dylibAliases;
+    dylibAliases.reserve(aliases.size());
+    for (const auto& alias : aliases)
+        dylibAliases.push_back({ alias.realPath.c_str(), alias.aliasPath.c_str() });
+
+    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");
+                        }
+                    }
+                    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 ) {
+                        uint32_t targetVmAddr;
+                        if ( _aslrTracker.hasRebaseTarget32(fixupLoc, &targetVmAddr) )
+                            fixupLoc->raw32 = 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;
+                    }
+                    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);
+                }
+                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);
+                }
+                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::Array<CachedDylibInfo> dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size());
+    const dyld3::Array<dyld3::closure::ClosureBuilder::CachedDylibAlias> aliasesArray(dylibAliases.data(), dylibAliases.size(), dylibAliases.size());
+    _imageArray = cb.makeDyldCacheImageArray(_options.optimizeStubs, dylibs, aliasesArray);
+    if ( cb.diagnostics().hasError() ) {
+        _diagnostics.error("%s", cb.diagnostics().errorMessage().c_str());
+        return;
+    }
+
+}
+
+static bool operator==(const dyld_cache_patchable_location& a, const dyld_cache_patchable_location& b) {
+    return a.cacheOffset == b.cacheOffset;
+}
+
+void SharedCacheBuilder::addImageArray()
+{
+    // build trie of dylib paths
+    __block std::vector<DylibIndexTrie::Entry> dylibEntrys;
+    _imageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+        dylibEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum()-1)));
+        image->forEachAlias(^(const char *aliasPath, bool &innerStop) {
+            dylibEntrys.push_back(DylibIndexTrie::Entry(aliasPath, DylibIndex(image->imageNum()-1)));
+        });
+    });
+    DylibIndexTrie dylibsTrie(dylibEntrys);
+    std::vector<uint8_t> trieBytes;
+    dylibsTrie.emit(trieBytes);
+    while ( (trieBytes.size() % 4) != 0 )
+        trieBytes.push_back(0);
+
+    // build set of functions to never stub-eliminate because tools may need to override them
+    std::unordered_set<std::string> alwaysGeneratePatch;
+    for (const char* const* p=_s_neverStubEliminateSymbols; *p != nullptr; ++p)
+        alwaysGeneratePatch.insert(*p);
+
+    // Add the patches for the image array.
+    __block uint64_t numPatchImages             = _imageArray->size();
+    __block uint64_t numPatchExports            = 0;
+    __block uint64_t numPatchLocations          = 0;
+    __block uint64_t numPatchExportNameBytes    = 0;
+
+    auto needsPatch = [&](bool dylibNeedsPatching, const dyld3::MachOLoaded* mh,
+                          CacheOffset offset) -> bool {
+        if (dylibNeedsPatching)
+            return true;
+        if (_dylibWeakExports.find({ mh, offset }) != _dylibWeakExports.end())
+            return true;
+        const std::string& exportName = _exportsToName[offset];
+        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) {
+        const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh;
+        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);
+
+        uint64_t numDylibExports = 0;
+        for (CacheOffset exportCacheOffset : dylibExports) {
+            if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset))
+                continue;
+            std::vector<dyld_cache_patchable_location>& uses = _exportsToUses[exportCacheOffset];
+            uses.erase(std::unique(uses.begin(), uses.end()), uses.end());
+            numPatchLocations += uses.size();
+
+            std::string exportName = _exportsToName[exportCacheOffset];
+            numPatchExportNameBytes += exportName.size() + 1;
+        }
+        numPatchExports += numDylibExports;
+    });
+
+    // Now reserve the space
+    __block std::vector<dyld_cache_image_patches>       patchImages;
+    __block std::vector<dyld_cache_patchable_export>    patchExports;
+    __block std::vector<dyld_cache_patchable_location>  patchLocations;
+    __block std::vector<char>                           patchExportNames;
+
+    patchImages.reserve(numPatchImages);
+    patchExports.reserve(numPatchExports);
+    patchLocations.reserve(numPatchLocations);
+    patchExportNames.reserve(numPatchExportNameBytes);
+
+    // And now fill it with the patch data
+    cache->forEachImage(^(const mach_header* mh, const char* installName) {
+        const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh;
+        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);
+
+        // Add the patch image which points in to the exports
+        dyld_cache_image_patches patchImage;
+        patchImage.patchExportsStartIndex   = (uint32_t)patchExports.size();
+        patchImage.patchExportsCount        = 0;
+
+        // Then add each export which points to a list of locations and a name
+        for (CacheOffset exportCacheOffset : dylibExports) {
+            if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset))
+                continue;
+            ++patchImage.patchExportsCount;
+            std::vector<dyld_cache_patchable_location>& uses = _exportsToUses[exportCacheOffset];
+
+            dyld_cache_patchable_export cacheExport;
+            cacheExport.cacheOffsetOfImpl           = (uint32_t)exportCacheOffset;
+            cacheExport.patchLocationsStartIndex    = (uint32_t)patchLocations.size();
+            cacheExport.patchLocationsCount         = (uint32_t)uses.size();
+            cacheExport.exportNameOffset            = (uint32_t)patchExportNames.size();
+            patchExports.push_back(cacheExport);
+
+            // Now add the list of locations.
+            patchLocations.insert(patchLocations.end(), uses.begin(), uses.end());
+
+            // And add the export name
+            const std::string& exportName = _exportsToName[exportCacheOffset];
+            patchExportNames.insert(patchExportNames.end(), &exportName[0], &exportName[0] + exportName.size() + 1);
+        }
+        patchImages.push_back(patchImage);
+    });
+
+    while ( (patchExportNames.size() % 4) != 0 )
+        patchExportNames.push_back('\0');
+
+    uint64_t patchInfoSize = sizeof(dyld_cache_patch_info);
+    patchInfoSize += sizeof(dyld_cache_image_patches) * patchImages.size();
+    patchInfoSize += sizeof(dyld_cache_patchable_export) * patchExports.size();
+    patchInfoSize += sizeof(dyld_cache_patchable_location) * patchLocations.size();
+    patchInfoSize += patchExportNames.size();
+
+    // check for fit
+    uint64_t imageArraySize = _imageArray->size();
+    size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+    if ( (imageArraySize+trieBytes.size()+patchInfoSize) > freeSpace ) {
+        _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, patch size=%lluKB, free space=%ldMB)",
+                            _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, patchInfoSize/1024, freeSpace/1024/1024);
+        return;
+    }
+
+    // copy into cache and update header
+    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    dyldCache->header.dylibsImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+    dyldCache->header.dylibsImageArraySize = imageArraySize;
+    dyldCache->header.dylibsTrieAddr       = dyldCache->header.dylibsImageArrayAddr + imageArraySize;
+    dyldCache->header.dylibsTrieSize       = trieBytes.size();
+    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, _imageArray, imageArraySize);
+    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
+
+    // Also write out the patch info
+    dyldCache->header.patchInfoAddr = dyldCache->header.dylibsTrieAddr + dyldCache->header.dylibsTrieSize;
+    dyldCache->header.patchInfoSize = patchInfoSize;
+    dyld_cache_patch_info patchInfo;
+    patchInfo.patchTableArrayAddr = dyldCache->header.patchInfoAddr + sizeof(dyld_cache_patch_info);
+    patchInfo.patchTableArrayCount = patchImages.size();
+    patchInfo.patchExportArrayAddr = patchInfo.patchTableArrayAddr + (patchInfo.patchTableArrayCount * sizeof(dyld_cache_image_patches));
+    patchInfo.patchExportArrayCount = patchExports.size();
+    patchInfo.patchLocationArrayAddr = patchInfo.patchExportArrayAddr + (patchInfo.patchExportArrayCount * sizeof(dyld_cache_patchable_export));
+    patchInfo.patchLocationArrayCount = patchLocations.size();
+    patchInfo.patchExportNamesAddr = patchInfo.patchLocationArrayAddr + (patchInfo.patchLocationArrayCount * sizeof(dyld_cache_patchable_location));
+    patchInfo.patchExportNamesSize = patchExportNames.size();
+    ::memcpy(_readOnlyRegion.buffer + dyldCache->header.patchInfoAddr - _readOnlyRegion.unslidLoadAddress,
+             &patchInfo, sizeof(dyld_cache_patch_info));
+    ::memcpy(_readOnlyRegion.buffer + patchInfo.patchTableArrayAddr - _readOnlyRegion.unslidLoadAddress,
+             &patchImages[0], sizeof(patchImages[0]) * patchImages.size());
+    ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportArrayAddr - _readOnlyRegion.unslidLoadAddress,
+             &patchExports[0], sizeof(patchExports[0]) * patchExports.size());
+    ::memcpy(_readOnlyRegion.buffer + patchInfo.patchLocationArrayAddr - _readOnlyRegion.unslidLoadAddress,
+             &patchLocations[0], sizeof(patchLocations[0]) * patchLocations.size());
+    ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportNamesAddr - _readOnlyRegion.unslidLoadAddress,
+             &patchExportNames[0], patchExportNames.size());
+
+    _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size()+patchInfoSize,14);
+
+    // Free the underlying image array buffer
+    _imageArray->deallocate();
+}
+
+void SharedCacheBuilder::addOtherImageArray(const std::vector<LoadedMachO>& otherDylibsAndBundles, std::vector<const LoadedMachO*>& overflowDylibs)
+{
+    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::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform);
+
+    // make ImageArray for other dylibs and bundles
+    STACK_ALLOC_ARRAY(dyld3::closure::LoadedFileInfo, others, otherDylibsAndBundles.size() + overflowDylibs.size());
+    for (const LoadedMachO& other : otherDylibsAndBundles) {
+        if ( !contains(other.loadedFileInfo.path, "staged_system_apps/") )
+            others.push_back(other.loadedFileInfo);
+    }
+
+    for (const LoadedMachO* dylib : overflowDylibs) {
+        if (dylib->mappedFile.mh->canHavePrecomputedDlopenClosure(dylib->mappedFile.runtimePath.c_str(), ^(const char*) {}) )
+            others.push_back(dylib->loadedFileInfo);
+    }
+
+    // Sort the others array by name so that it is deterministic
+    std::sort(others.begin(), others.end(),
+              [](const dyld3::closure::LoadedFileInfo& a, const dyld3::closure::LoadedFileInfo& b) {
+                  // Sort mac before iOSMac
+                  bool isIOSMacA = strncmp(a.path, "/System/iOSSupport/", 19) == 0;
+                  bool isIOSMacB = strncmp(b.path, "/System/iOSSupport/", 19) == 0;
+                  if (isIOSMacA != isIOSMacB)
+                      return !isIOSMacA;
+                  return strcmp(a.path, b.path) < 0;
+              });
+
+    const dyld3::closure::ImageArray* otherImageArray = cb.makeOtherDylibsImageArray(others, (uint32_t)_sortedDylibs.size());
+
+    // build trie of paths
+    __block std::vector<DylibIndexTrie::Entry> otherEntrys;
+    otherImageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+        if ( !image->isInvalid() )
+            otherEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum())));
+    });
+    DylibIndexTrie dylibsTrie(otherEntrys);
+    std::vector<uint8_t> trieBytes;
+    dylibsTrie.emit(trieBytes);
+    while ( (trieBytes.size() % 4) != 0 )
+        trieBytes.push_back(0);
+
+    // check for fit
+    uint64_t imageArraySize = otherImageArray->size();
+    size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+    if ( imageArraySize+trieBytes.size() > freeSpace ) {
+        _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, free space=%ldMB)",
+                           _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, freeSpace/1024/1024);
+        return;
+    }
+
+    // copy into cache and update header
+    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    dyldCache->header.otherImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+    dyldCache->header.otherImageArraySize = imageArraySize;
+    dyldCache->header.otherTrieAddr       = dyldCache->header.otherImageArrayAddr + imageArraySize;
+    dyldCache->header.otherTrieSize       = trieBytes.size();
+    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, otherImageArray, imageArraySize);
+    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
+    _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size(),14);
+
+    // Free the underlying buffer
+    otherImageArray->deallocate();
+}
+
+
+void SharedCacheBuilder::addClosures(const std::vector<LoadedMachO>& osExecutables)
+{
+    const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+
+    __block std::vector<Diagnostics> osExecutablesDiags;
+    __block std::vector<const dyld3::closure::LaunchClosure*> osExecutablesClosures;
+    osExecutablesDiags.resize(osExecutables.size());
+    osExecutablesClosures.resize(osExecutables.size());
+
+    dispatch_apply(osExecutables.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+        const LoadedMachO& loadedMachO = osExecutables[index];
+        // don't pre-build closures for staged apps into dyld cache, since they won't run from that location
+        if ( startsWith(loadedMachO.mappedFile.runtimePath, "/private/var/staged_system_apps/") ) {
+            return;
+        }
+        dyld3::closure::PathOverrides pathOverrides;
+        dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, _fileSystem, dyldCache, false, *_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) )
+            _fileSystem.fileExists(loadedMachO.loadedFileInfo.path, nullptr, nullptr, &issetuid, nullptr);
+        const dyld3::closure::LaunchClosure* mainClosure = builder.makeLaunchClosure(loadedMachO.loadedFileInfo, issetuid);
+        if ( builder.diagnostics().hasError() ) {
+           osExecutablesDiags[index].error("%s", builder.diagnostics().errorMessage().c_str());
+        }
+        else {
+            assert(mainClosure != nullptr);
+            osExecutablesClosures[index] = mainClosure;
+        }
+    });
+
+    std::map<std::string, const dyld3::closure::LaunchClosure*> closures;
+    for (uint64_t i = 0, e = osExecutables.size(); i != e; ++i) {
+        const LoadedMachO& loadedMachO = osExecutables[i];
+        const Diagnostics& diag = osExecutablesDiags[i];
+        if (diag.hasError()) {
+            if ( _options.verbose ) {
+                _diagnostics.warning("building closure for '%s': %s", loadedMachO.mappedFile.runtimePath.c_str(), diag.errorMessage().c_str());
+                for (const std::string& warn : diag.warnings() )
+                    _diagnostics.warning("%s", warn.c_str());
+            }
+            if ( loadedMachO.inputFile && (loadedMachO.inputFile->mustBeIncluded()) ) {
+                loadedMachO.inputFile->diag.error("%s", diag.errorMessage().c_str());
+            }
+        } else {
+            // Note, a closure could be null here if it has a path we skip.
+            if (osExecutablesClosures[i] != nullptr)
+                closures[loadedMachO.mappedFile.runtimePath] = osExecutablesClosures[i];
+        }
+    }
+
+    osExecutablesDiags.clear();
+    osExecutablesClosures.clear();
+
+    // preflight space needed
+    size_t closuresSpace = 0;
+    for (const auto& entry : closures) {
+        closuresSpace += entry.second->size();
+    }
+    size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+    if ( closuresSpace > freeSpace ) {
+        _diagnostics.error("cache buffer too small to hold all closures (buffer size=%lldMB, closures size=%ldMB, free space=%ldMB)",
+                            _allocatedBufferSize/1024/1024, closuresSpace/1024/1024, freeSpace/1024/1024);
+        return;
+    }
+    DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    cache->header.progClosuresAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+    uint8_t* closuresBase = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse;
+    std::vector<DylibIndexTrie::Entry> closureEntrys;
+    uint32_t currentClosureOffset = 0;
+    for (const auto& entry : closures) {
+        const dyld3::closure::LaunchClosure* closure = entry.second;
+        closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset)));
+        size_t size = closure->size();
+        assert((size % 4) == 0);
+        memcpy(closuresBase+currentClosureOffset, closure, size);
+        currentClosureOffset += size;
+        freeSpace -= size;
+        closure->deallocate();
+    }
+    cache->header.progClosuresSize = currentClosureOffset;
+    _readOnlyRegion.sizeInUse += currentClosureOffset;
+    freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+    // build trie of indexes into closures list
+    DylibIndexTrie closureTrie(closureEntrys);
+    std::vector<uint8_t> trieBytes;
+    closureTrie.emit(trieBytes);
+    while ( (trieBytes.size() % 8) != 0 )
+        trieBytes.push_back(0);
+    if ( trieBytes.size() > freeSpace ) {
+        _diagnostics.error("cache buffer too small to hold all closures trie (buffer size=%lldMB, trie size=%ldMB, free space=%ldMB)",
+                            _allocatedBufferSize/1024/1024, trieBytes.size()/1024/1024, freeSpace/1024/1024);
+        return;
+    }
+    memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, &trieBytes[0], trieBytes.size());
+    cache->header.progClosuresTrieAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+    cache->header.progClosuresTrieSize = trieBytes.size();
+    _readOnlyRegion.sizeInUse += trieBytes.size();
+    _readOnlyRegion.sizeInUse = align(_readOnlyRegion.sizeInUse, 14);
+}
+
+
+bool 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);
+    assert(_readExecuteRegion.sizeInUse       == mappings[0].size);
+    assert(_readWriteRegion.sizeInUse         == mappings[1].size);
+    assert(_readOnlyRegion.sizeInUse          == mappings[2].size);
+    assert(_readExecuteRegion.cacheFileOffset == mappings[0].fileOffset);
+    assert(_readWriteRegion.cacheFileOffset   == mappings[1].fileOffset);
+    assert(_readOnlyRegion.cacheFileOffset    == mappings[2].fileOffset);
+    assert(_codeSignatureRegion.sizeInUse     == cacheHeader->codeSignatureSize);
+    assert(cacheHeader->codeSignatureOffset   == mappings[2].fileOffset+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse);
+    cacheSizeCallback(_readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse+_codeSignatureRegion.sizeInUse);
+    bool fullyWritten = copyCallback(_readExecuteRegion.buffer, _readExecuteRegion.sizeInUse, mappings[0].fileOffset);
+    fullyWritten &= copyCallback(_readWriteRegion.buffer, _readWriteRegion.sizeInUse, mappings[1].fileOffset);
+    fullyWritten &= copyCallback(_readOnlyRegion.buffer, _readOnlyRegion.sizeInUse, mappings[2].fileOffset);
+    if ( _localSymbolsRegion.sizeInUse != 0 ) {
+        assert(cacheHeader->localSymbolsOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse);
+        fullyWritten &= copyCallback(_localSymbolsRegion.buffer, _localSymbolsRegion.sizeInUse, cacheHeader->localSymbolsOffset);
+    }
+    fullyWritten &= copyCallback(_codeSignatureRegion.buffer, _codeSignatureRegion.sizeInUse, cacheHeader->codeSignatureOffset);
+    return fullyWritten;
+}
+
+
+void SharedCacheBuilder::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 ) {
+        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);
+        };
+        auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
+            uint64_t writtenSize = pwrite(fd, src, size, dstOffset);
+            return writtenSize == size;
+        };
+        // <rdar://problem/55370916> TOCTOU: verify path is still a realpath (not changed)
+        char tempPath[MAXPATHLEN];
+        if ( ::fcntl(fd, F_GETPATH, tempPath) == 0 ) {
+            size_t tempPathLen = strlen(tempPath);
+            if ( tempPathLen > 7 )
+                tempPath[tempPathLen-7] = '\0'; // remove trailing -xxxxxx
+            if ( path != tempPath ) {
+                _diagnostics.error("output file path changed from: '%s' to: '%s'", path.c_str(), tempPath);
+                ::close(fd);
+                return;
+            }
+        }
+        else {
+            _diagnostics.error("unable to fcntl(fd, F_GETPATH) on output file");
+            ::close(fd);
+            return;
+        }
+        bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
+        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;
+            }
+            if ( ::rename(pathTemplateSpace, path.c_str()) == 0) {
+                ::close(fd);
+                return; // success
+            }
+        }
+        else {
+            _diagnostics.error("could not write file %s", pathTemplateSpace);
+        }
+        ::close(fd);
+        ::unlink(pathTemplateSpace);
+    }
+    else {
+        _diagnostics.error("could not open file %s", pathTemplateSpace);
+    }
+}
+
+void SharedCacheBuilder::writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) {
+    auto cacheSizeCallback = ^(uint64_t size) {
+        buffer = (uint8_t*)malloc(size);
+        bufferSize = size;
+    };
+    auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
+        memcpy(buffer + dstOffset, src, size);
+        return true;
+    };
+    bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
+    assert(fullyWritten);
+}
+
+void SharedCacheBuilder::writeMapFile(const std::string& path)
+{
+    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    std::string mapContent = cache->mapFile();
+    safeSave(mapContent.c_str(), mapContent.size(), path);
+}
+
+std::string SharedCacheBuilder::getMapFileBuffer(const std::string& cacheDisposition) const
+{
+    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    return cache->generateJSONMap(cacheDisposition.c_str());
+}
+
+void SharedCacheBuilder::markPaddingInaccessible()
+{
+    // region between RX and RW
+    uint8_t* startPad1 = _readExecuteRegion.buffer+_readExecuteRegion.sizeInUse;
+    uint8_t* endPad1   = _readWriteRegion.buffer;
+    ::vm_protect(mach_task_self(), (vm_address_t)startPad1, endPad1-startPad1, false, 0);
+
+    // region between RW and RO
+    uint8_t* startPad2 = _readWriteRegion.buffer+_readWriteRegion.sizeInUse;
+    uint8_t* endPad2   = _readOnlyRegion.buffer;
+    ::vm_protect(mach_task_self(), (vm_address_t)startPad2, endPad2-startPad2, false, 0);
+}
+
+
+void SharedCacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) {
+    for (const DylibInfo& dylibInfo : _sortedDylibs)
+        callback(dylibInfo.runtimePath);
+}
+
+
+uint64_t SharedCacheBuilder::pathHash(const char* path)
+{
+    uint64_t sum = 0;
+    for (const char* s=path; *s != '\0'; ++s)
+        sum += sum*4 + *s;
+    return sum;
+}
+
+
+void SharedCacheBuilder::findDylibAndSegment(const void* contentPtr, std::string& foundDylibName, std::string& foundSegName)
+{
+    foundDylibName = "???";
+    foundSegName   = "???";
+    uint64_t unslidVmAddr = ((uint8_t*)contentPtr - _readExecuteRegion.buffer) + _readExecuteRegion.unslidLoadAddress;
+    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    cache->forEachImage(^(const mach_header* mh, const char* installName) {
+        ((dyld3::MachOLoaded*)mh)->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool &stop) {
+            if ( (unslidVmAddr >= info.vmAddr) && (unslidVmAddr < (info.vmAddr+info.vmSize)) ) {
+                foundDylibName = installName;
+                foundSegName   = info.segName;
+                stop           = true;
+            }
+        });
+    });
+}
+
+
+void SharedCacheBuilder::fipsSign()
+{
+    // find libcorecrypto.dylib in cache being built
+    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    __block const dyld3::MachOLoaded* ml = nullptr;
+    dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+        if ( strcmp(installName, "/usr/lib/system/libcorecrypto.dylib") == 0 )
+            ml = (dyld3::MachOLoaded*)mh;
+    });
+    if ( ml == nullptr ) {
+        _diagnostics.warning("Could not find libcorecrypto.dylib, skipping FIPS sealing");
+        return;
+    }
+
+    // find location in libcorecrypto.dylib to store hash of __text section
+    uint64_t hashStoreSize;
+    const void* hashStoreLocation = ml->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize);
+    if ( hashStoreLocation == nullptr ) {
+        _diagnostics.warning("Could not find __TEXT/__fips_hmacs section in libcorecrypto.dylib, skipping FIPS sealing");
+        return;
+    }
+    if ( hashStoreSize != 32 ) {
+        _diagnostics.warning("__TEXT/__fips_hmacs section in libcorecrypto.dylib is not 32 bytes in size, skipping FIPS sealing");
+        return;
+    }
+
+    // compute hmac hash of __text section
+    uint64_t textSize;
+    const void* textLocation = ml->findSectionContent("__TEXT", "__text", textSize);
+    if ( textLocation == nullptr ) {
+        _diagnostics.warning("Could not find __TEXT/__text section in libcorecrypto.dylib, skipping FIPS sealing");
+        return;
+    }
+    unsigned char hmac_key = 0;
+    CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, textLocation, textSize, (void*)hashStoreLocation); // store hash directly into hashStoreLocation
+}
+
+void SharedCacheBuilder::codeSign()
+{
+    uint8_t  dscHashType;
+    uint8_t  dscHashSize;
+    uint32_t dscDigestFormat;
+    bool agile = false;
+
+    // select which codesigning hash
+    switch (_options.codeSigningDigestMode) {
+        case DyldSharedCache::Agile:
+            agile = true;
+            // Fall through to SHA1, because the main code directory remains SHA1 for compatibility.
+            [[clang::fallthrough]];
+        case DyldSharedCache::SHA1only:
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+            dscHashType     = CS_HASHTYPE_SHA1;
+            dscHashSize     = CS_HASH_SIZE_SHA1;
+            dscDigestFormat = kCCDigestSHA1;
+#pragma clang diagnostic pop
+            break;
+        case DyldSharedCache::SHA256only:
+            dscHashType     = CS_HASHTYPE_SHA256;
+            dscHashSize     = CS_HASH_SIZE_SHA256;
+            dscDigestFormat = kCCDigestSHA256;
+            break;
+        default:
+            _diagnostics.error("codeSigningDigestMode has unknown, unexpected value %d, bailing out.",
+                               _options.codeSigningDigestMode);
+            return;
+    }
+
+    std::string cacheIdentifier = "com.apple.dyld.cache.";
+    cacheIdentifier +=  _options.archs->name();
+    if ( _options.dylibsRemovedDuringMastering ) {
+        if ( _options.optimizeStubs  )
+            cacheIdentifier +=  ".release";
+        else
+            cacheIdentifier += ".development";
+    }
+    // get pointers into shared cache buffer
+    size_t          inBbufferSize = _readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse;
+
+    const uint16_t pageSize = _archLayout->csPageSize;
+
+    // layout code signature contents
+    uint32_t blobCount     = agile ? 4 : 3;
+    size_t   idSize        = cacheIdentifier.size()+1; // +1 for terminating 0
+    uint32_t slotCount     = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize);
+    uint32_t xSlotCount    = CSSLOT_REQUIREMENTS;
+    size_t   idOffset      = offsetof(CS_CodeDirectory, end_withExecSeg);
+    size_t   hashOffset    = idOffset+idSize + dscHashSize*xSlotCount;
+    size_t   hash256Offset = idOffset+idSize + CS_HASH_SIZE_SHA256*xSlotCount;
+    size_t   cdSize        = hashOffset + (slotCount * dscHashSize);
+    size_t   cd256Size     = agile ? hash256Offset + (slotCount * CS_HASH_SIZE_SHA256) : 0;
+    size_t   reqsSize      = 12;
+    size_t   cmsSize       = sizeof(CS_Blob);
+    size_t   cdOffset      = sizeof(CS_SuperBlob) + blobCount*sizeof(CS_BlobIndex);
+    size_t   cd256Offset   = cdOffset + cdSize;
+    size_t   reqsOffset    = cd256Offset + cd256Size; // equals cdOffset + cdSize if not agile
+    size_t   cmsOffset     = reqsOffset + reqsSize;
+    size_t   sbSize        = cmsOffset + cmsSize;
+    size_t   sigSize       = align(sbSize, 14);       // keep whole cache 16KB aligned
+
+    // allocate space for blob
+    vm_address_t codeSigAlloc;
+    if ( vm_allocate(mach_task_self(), &codeSigAlloc, sigSize, VM_FLAGS_ANYWHERE) != 0 ) {
+        _diagnostics.error("could not allocate code signature buffer");
+        return;
+    }
+    _codeSignatureRegion.buffer     = (uint8_t*)codeSigAlloc;
+    _codeSignatureRegion.bufferSize = sigSize;
+    _codeSignatureRegion.sizeInUse  = sigSize;
+
+    // create overall code signature which is a superblob
+    CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(_codeSignatureRegion.buffer);
+    sb->magic           = htonl(CSMAGIC_EMBEDDED_SIGNATURE);
+    sb->length          = htonl(sbSize);
+    sb->count           = htonl(blobCount);
+    sb->index[0].type   = htonl(CSSLOT_CODEDIRECTORY);
+    sb->index[0].offset = htonl(cdOffset);
+    sb->index[1].type   = htonl(CSSLOT_REQUIREMENTS);
+    sb->index[1].offset = htonl(reqsOffset);
+    sb->index[2].type   = htonl(CSSLOT_CMS_SIGNATURE);
+    sb->index[2].offset = htonl(cmsOffset);
+    if ( agile ) {
+        sb->index[3].type = htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES + 0);
+        sb->index[3].offset = htonl(cd256Offset);
+    }
+
+    // fill in empty requirements
+    CS_RequirementsBlob* reqs = (CS_RequirementsBlob*)(((char*)sb)+reqsOffset);
+    reqs->magic  = htonl(CSMAGIC_REQUIREMENTS);
+    reqs->length = htonl(sizeof(CS_RequirementsBlob));
+    reqs->data   = 0;
+
+    // initialize fixed fields of Code Directory
+    CS_CodeDirectory* cd = (CS_CodeDirectory*)(((char*)sb)+cdOffset);
+    cd->magic           = htonl(CSMAGIC_CODEDIRECTORY);
+    cd->length          = htonl(cdSize);
+    cd->version         = htonl(0x20400);               // supports exec segment
+    cd->flags           = htonl(kSecCodeSignatureAdhoc);
+    cd->hashOffset      = htonl(hashOffset);
+    cd->identOffset     = htonl(idOffset);
+    cd->nSpecialSlots   = htonl(xSlotCount);
+    cd->nCodeSlots      = htonl(slotCount);
+    cd->codeLimit       = htonl(inBbufferSize);
+    cd->hashSize        = dscHashSize;
+    cd->hashType        = dscHashType;
+    cd->platform        = 0;                            // not platform binary
+    cd->pageSize        = __builtin_ctz(pageSize);      // log2(CS_PAGE_SIZE);
+    cd->spare2          = 0;                            // unused (must be zero)
+    cd->scatterOffset   = 0;                            // not supported anymore
+    cd->teamOffset      = 0;                            // no team ID
+    cd->spare3          = 0;                            // unused (must be zero)
+    cd->codeLimit64     = 0;                            // falls back to codeLimit
+
+    // executable segment info
+    cd->execSegBase     = htonll(_readExecuteRegion.cacheFileOffset); // base of TEXT segment
+    cd->execSegLimit    = htonll(_readExecuteRegion.sizeInUse);       // size of TEXT segment
+    cd->execSegFlags    = 0;                                          // not a main binary
+
+    // initialize dynamic fields of Code Directory
+    strcpy((char*)cd + idOffset, cacheIdentifier.c_str());
+
+    // add special slot hashes
+    uint8_t* hashSlot = (uint8_t*)cd + hashOffset;
+    uint8_t* reqsHashSlot = &hashSlot[-CSSLOT_REQUIREMENTS*dscHashSize];
+    CCDigest(dscDigestFormat, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHashSlot);
+
+    CS_CodeDirectory* cd256;
+    uint8_t* hash256Slot;
+    uint8_t* reqsHash256Slot;
+    if ( agile ) {
+        // Note that the assumption here is that the size up to the hashes is the same as for
+        // sha1 code directory, and that they come last, after everything else.
+
+        cd256 = (CS_CodeDirectory*)(((char*)sb)+cd256Offset);
+        cd256->magic           = htonl(CSMAGIC_CODEDIRECTORY);
+        cd256->length          = htonl(cd256Size);
+        cd256->version         = htonl(0x20400);               // supports exec segment
+        cd256->flags           = htonl(kSecCodeSignatureAdhoc);
+        cd256->hashOffset      = htonl(hash256Offset);
+        cd256->identOffset     = htonl(idOffset);
+        cd256->nSpecialSlots   = htonl(xSlotCount);
+        cd256->nCodeSlots      = htonl(slotCount);
+        cd256->codeLimit       = htonl(inBbufferSize);
+        cd256->hashSize        = CS_HASH_SIZE_SHA256;
+        cd256->hashType        = CS_HASHTYPE_SHA256;
+        cd256->platform        = 0;                            // not platform binary
+        cd256->pageSize        = __builtin_ctz(pageSize);      // log2(CS_PAGE_SIZE);
+        cd256->spare2          = 0;                            // unused (must be zero)
+        cd256->scatterOffset   = 0;                            // not supported anymore
+        cd256->teamOffset      = 0;                            // no team ID
+        cd256->spare3          = 0;                            // unused (must be zero)
+        cd256->codeLimit64     = 0;                            // falls back to codeLimit
+
+        // executable segment info
+        cd256->execSegBase     = cd->execSegBase;
+        cd256->execSegLimit    = cd->execSegLimit;
+        cd256->execSegFlags    = cd->execSegFlags;
+
+        // initialize dynamic fields of Code Directory
+        strcpy((char*)cd256 + idOffset, cacheIdentifier.c_str());
+
+        // add special slot hashes
+        hash256Slot = (uint8_t*)cd256 + hash256Offset;
+        reqsHash256Slot = &hash256Slot[-CSSLOT_REQUIREMENTS*CS_HASH_SIZE_SHA256];
+        CCDigest(kCCDigestSHA256, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHash256Slot);
+    }
+    else {
+        cd256 = NULL;
+        hash256Slot = NULL;
+        reqsHash256Slot = NULL;
+    }
+
+    // fill in empty CMS blob for ad-hoc signing
+    CS_Blob* cms = (CS_Blob*)(((char*)sb)+cmsOffset);
+    cms->magic  = htonl(CSMAGIC_BLOBWRAPPER);
+    cms->length = htonl(sizeof(CS_Blob));
+
+
+    // alter header of cache to record size and location of code signature
+    // do this *before* hashing each page
+    dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer;
+    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);
+    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);
+
+        CCDigest(dscDigestFormat, code, pageSize, hashSlot + (i * dscHashSize));
+
+        if ( agile ) {
+            CCDigest(kCCDigestSHA256, code, pageSize, hash256Slot + (i * CS_HASH_SIZE_SHA256));
+        }
+    };
+
+    // compute hashes
+    dispatch_apply(slotCount, DISPATCH_APPLY_AUTO, ^(size_t i) {
+        codeSignPage(i);
+    });
+
+    // Now that we have a code signature, compute a cache UUID by hashing the code signature blob
+    {
+        uint8_t* uuidLoc = cache->uuid;
+        assert(uuid_is_null(uuidLoc));
+        static_assert(offsetof(dyld_cache_header, uuid) / CS_PAGE_SIZE_4K == 0, "uuid is expected in the first page of the cache");
+        uint8_t fullDigest[CC_SHA256_DIGEST_LENGTH];
+        CC_SHA256((const void*)cd, (unsigned)cdSize, fullDigest);
+        memcpy(uuidLoc, fullDigest, 16);
+        // <rdar://problem/6723729> uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
+        uuidLoc[6] = ( uuidLoc[6] & 0x0F ) | ( 3 << 4 );
+        uuidLoc[8] = ( uuidLoc[8] & 0x3F ) | 0x80;
+
+        // Now codesign page 0 again, because we modified it by setting uuid in header
+        codeSignPage(0);
+    }
+
+    // hash of entire code directory (cdHash) uses same hash as each page
+    uint8_t fullCdHash[dscHashSize];
+    CCDigest(dscDigestFormat, (const uint8_t*)cd, cdSize, fullCdHash);
+    // Note: cdHash is defined as first 20 bytes of hash
+    memcpy(_cdHashFirst, fullCdHash, 20);
+    if ( agile ) {
+        uint8_t fullCdHash256[CS_HASH_SIZE_SHA256];
+        CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, cd256Size, fullCdHash256);
+        // Note: cdHash is defined as first 20 bytes of hash, even for sha256
+        memcpy(_cdHashSecond, fullCdHash256, 20);
+    }
+    else {
+        memset(_cdHashSecond, 0, 20);
+    }
+}
+
+const bool SharedCacheBuilder::agileSignature()
+{
+    return _options.codeSigningDigestMode == DyldSharedCache::Agile;
+}
+
+static const std::string cdHash(uint8_t hash[20])
+{
+    char buff[48];
+    for (int i = 0; i < 20; ++i)
+        sprintf(&buff[2*i], "%2.2x", hash[i]);
+    return buff;
+}
+
+const std::string SharedCacheBuilder::cdHashFirst()
+{
+    return cdHash(_cdHashFirst);
+}
+
+const std::string SharedCacheBuilder::cdHashSecond()
+{
+    return cdHash(_cdHashSecond);
+}
+
+const std::string SharedCacheBuilder::uuid() const
+{
+    dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer;
+    uuid_string_t uuidStr;
+    uuid_unparse(cache->uuid, uuidStr);
+    return uuidStr;
+}
+
+
+
+template <typename P>
+bool SharedCacheBuilder::makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info2* info)
+{
+    typedef typename P::uint_t     pint_t;
+
+    const pint_t   deltaMask    = (pint_t)(info->delta_mask);
+    const pint_t   valueMask    = ~deltaMask;
+    const pint_t   valueAdd     = (pint_t)(info->value_add);
+    const unsigned deltaShift   = __builtin_ctzll(deltaMask) - 2;
+    const uint32_t maxDelta     = (uint32_t)(deltaMask >> deltaShift);
+
+    pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0];
+    pint_t lastValue = (pint_t)P::getP(*lastLoc);
+    if ( (lastValue - valueAdd) & deltaMask ) {
+        std::string dylibName;
+        std::string segName;
+        findDylibAndSegment((void*)pageContent, dylibName, segName);
+        _diagnostics.error("rebase pointer (0x%0lX) does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
+                            (long)lastValue, lastLocationOffset, segName.c_str(), dylibName.c_str());
+        return false;
+    }
+    if ( offset <= (lastLocationOffset+maxDelta) ) {
+        // previous location in range, make link from it
+        // encode this location into last value
+        pint_t delta = offset - lastLocationOffset;
+        pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift);
+        //warning("  add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
+        //                    offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
+        uint8_t highByte;
+        if ( _aslrTracker.hasHigh8(lastLoc, &highByte) ) {
+            uint64_t tbi = (uint64_t)highByte << 56;
+            newLastValue |= tbi;
+        }
+        P::setP(*lastLoc, newLastValue);
+        return true;
+    }
+    //fprintf(stderr, "  too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
+
+    // distance between rebase locations is too far
+    // see if we can make a chain from non-rebase locations
+    uint16_t nonRebaseLocationOffsets[1024];
+    unsigned nrIndex = 0;
+    for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) {
+        nonRebaseLocationOffsets[nrIndex] = 0;
+        for (int j=maxDelta; j > 0; j -= 4) {
+            pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]);
+            if ( value == 0 ) {
+                // Steal values of 0 to be used in the rebase chain
+                nonRebaseLocationOffsets[nrIndex] = i+j;
+                break;
+            }
+        }
+        if ( nonRebaseLocationOffsets[nrIndex] == 0 ) {
+            lastValue = (pint_t)P::getP(*lastLoc);
+            pint_t newValue = ((lastValue - valueAdd) & valueMask);
+            //warning("   no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX", lastLocationOffset, (long)value, (long)newValue);
+            P::setP(*lastLoc, newValue);
+            return false;
+        }
+        i = nonRebaseLocationOffsets[nrIndex];
+        ++nrIndex;
+    }
+
+    // we can make chain. go back and add each non-rebase location to chain
+    uint16_t prevOffset = lastLocationOffset;
+    pint_t* prevLoc = (pint_t*)&pageContent[prevOffset];
+    for (unsigned n=0; n < nrIndex; ++n) {
+        uint16_t nOffset = nonRebaseLocationOffsets[n];
+        assert(nOffset != 0);
+        pint_t* nLoc = (pint_t*)&pageContent[nOffset];
+        uint32_t delta2 = nOffset - prevOffset;
+        pint_t value = (pint_t)P::getP(*prevLoc);
+        pint_t newValue;
+        if ( value == 0 )
+            newValue = (delta2 << deltaShift);
+        else
+            newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift);
+        //warning("    non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
+        P::setP(*prevLoc, newValue);
+        prevOffset = nOffset;
+        prevLoc = nLoc;
+    }
+    uint32_t delta3 = offset - prevOffset;
+    pint_t value = (pint_t)P::getP(*prevLoc);
+    pint_t newValue;
+    if ( value == 0 )
+        newValue = (delta3 << deltaShift);
+    else
+        newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift);
+    //warning("    non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
+    P::setP(*prevLoc, newValue);
+
+    return true;
+}
+
+
+template <typename P>
+void SharedCacheBuilder::addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info,
+                                         std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
+{
+    typedef typename P::uint_t     pint_t;
+
+    const pint_t   deltaMask    = (pint_t)(info->delta_mask);
+    const pint_t   valueMask    = ~deltaMask;
+    const uint32_t pageSize     = info->page_size;
+    const pint_t   valueAdd     = (pint_t)(info->value_add);
+
+    uint16_t startValue = DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE;
+    uint16_t lastLocationOffset = 0xFFFF;
+    for(uint32_t i=0; i < pageSize/4; ++i) {
+        unsigned offset = i*4;
+        if ( bitmap[i] ) {
+            if ( startValue == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
+                // found first rebase location in page
+                startValue = i;
+            }
+            else if ( !makeRebaseChainV2<P>(pageContent, lastLocationOffset, offset, info) ) {
+                // can't record all rebasings in one chain
+                if ( (startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 ) {
+                    // switch page_start to "extras" which is a list of chain starts
+                    unsigned indexInExtras = (unsigned)pageExtras.size();
+                    if ( indexInExtras > 0x3FFF ) {
+                        _diagnostics.error("rebase overflow in v2 page extras");
+                        return;
+                    }
+                    pageExtras.push_back(startValue);
+                    startValue = indexInExtras | DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA;
+                }
+                pageExtras.push_back(i);
+            }
+            lastLocationOffset = offset;
+        }
+    }
+    if ( lastLocationOffset != 0xFFFF ) {
+        // mark end of chain
+        pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset];
+        pint_t lastValue = (pint_t)P::getP(*lastLoc);
+        pint_t newValue = ((lastValue - valueAdd) & valueMask);
+        P::setP(*lastLoc, newValue);
+    }
+    if ( startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
+        // add end bit to extras
+        pageExtras.back() |= DYLD_CACHE_SLIDE_PAGE_ATTR_END;
+    }
+    pageStarts.push_back(startValue);
+}
+
+template <typename P>
+void SharedCacheBuilder::writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount)
+{
+    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;
+        }
+        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());
+}
+
+#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
+// fits in to int16_t
+static bool smallValue(uint64_t value)
+{
+    uint32_t high = (value & 0xFFFF8000);
+    return (high == 0) || (high == 0xFFFF8000);
+}
+
+template <typename P>
+bool SharedCacheBuilder::makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info4* info)
+{
+    typedef typename P::uint_t     pint_t;
+
+    const pint_t   deltaMask    = (pint_t)(info->delta_mask);
+    const pint_t   valueMask    = ~deltaMask;
+    const pint_t   valueAdd     = (pint_t)(info->value_add);
+    const unsigned deltaShift   = __builtin_ctzll(deltaMask) - 2;
+    const uint32_t maxDelta     = (uint32_t)(deltaMask >> deltaShift);
+
+    pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0];
+    pint_t lastValue = (pint_t)P::getP(*lastLoc);
+    if ( (lastValue - valueAdd) & deltaMask ) {
+        std::string dylibName;
+        std::string segName;
+        findDylibAndSegment((void*)pageContent, dylibName, segName);
+        _diagnostics.error("rebase pointer does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
+                            lastLocationOffset, segName.c_str(), dylibName.c_str());
+        return false;
+    }
+    if ( offset <= (lastLocationOffset+maxDelta) ) {
+        // previous location in range, make link from it
+        // encode this location into last value
+        pint_t delta = offset - lastLocationOffset;
+        pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift);
+        //warning("  add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
+        //                    offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
+        P::setP(*lastLoc, newLastValue);
+        return true;
+    }
+    //fprintf(stderr, "  too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
+
+    // distance between rebase locations is too far
+    // see if we can make a chain from non-rebase locations
+    uint16_t nonRebaseLocationOffsets[1024];
+    unsigned nrIndex = 0;
+    for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) {
+        nonRebaseLocationOffsets[nrIndex] = 0;
+        for (int j=maxDelta; j > 0; j -= 4) {
+            pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]);
+            if ( smallValue(value) ) {
+                // Steal values of 0 to be used in the rebase chain
+                nonRebaseLocationOffsets[nrIndex] = i+j;
+                break;
+            }
+        }
+        if ( nonRebaseLocationOffsets[nrIndex] == 0 ) {
+            lastValue = (pint_t)P::getP(*lastLoc);
+            pint_t newValue = ((lastValue - valueAdd) & valueMask);
+            //fprintf(stderr, "   no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX\n",
+            //                lastLocationOffset, (long)lastValue, (long)newValue);
+            P::setP(*lastLoc, newValue);
+            return false;
+        }
+        i = nonRebaseLocationOffsets[nrIndex];
+        ++nrIndex;
+    }
+
+    // we can make chain. go back and add each non-rebase location to chain
+    uint16_t prevOffset = lastLocationOffset;
+    pint_t* prevLoc = (pint_t*)&pageContent[prevOffset];
+    for (unsigned n=0; n < nrIndex; ++n) {
+        uint16_t nOffset = nonRebaseLocationOffsets[n];
+        assert(nOffset != 0);
+        pint_t* nLoc = (pint_t*)&pageContent[nOffset];
+        uint32_t delta2 = nOffset - prevOffset;
+        pint_t value = (pint_t)P::getP(*prevLoc);
+        pint_t newValue;
+        if ( smallValue(value) )
+            newValue = (value & valueMask) | (delta2 << deltaShift);
+        else
+            newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift);
+        //warning("    non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
+        P::setP(*prevLoc, newValue);
+        prevOffset = nOffset;
+        prevLoc = nLoc;
+    }
+    uint32_t delta3 = offset - prevOffset;
+    pint_t value = (pint_t)P::getP(*prevLoc);
+    pint_t newValue;
+    if ( smallValue(value) )
+        newValue = (value & valueMask) | (delta3 << deltaShift);
+    else
+        newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift);
+    //warning("    non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
+    P::setP(*prevLoc, newValue);
+
+    return true;
+}
+
+
+template <typename P>
+void SharedCacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info,
+                                         std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
+{
+    typedef typename P::uint_t     pint_t;
+
+    const pint_t   deltaMask    = (pint_t)(info->delta_mask);
+    const pint_t   valueMask    = ~deltaMask;
+    const uint32_t pageSize     = info->page_size;
+    const pint_t   valueAdd     = (pint_t)(info->value_add);
+
+    uint16_t startValue = DYLD_CACHE_SLIDE4_PAGE_NO_REBASE;
+    uint16_t lastLocationOffset = 0xFFFF;
+    for(uint32_t i=0; i < pageSize/4; ++i) {
+        unsigned offset = i*4;
+        if ( bitmap[i] ) {
+            if ( startValue == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
+                // found first rebase location in page
+                startValue = i;
+            }
+            else if ( !makeRebaseChainV4<P>(pageContent, lastLocationOffset, offset, info) ) {
+                // can't record all rebasings in one chain
+                if ( (startValue & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) == 0 ) {
+                    // switch page_start to "extras" which is a list of chain starts
+                    unsigned indexInExtras = (unsigned)pageExtras.size();
+                    if ( indexInExtras >= DYLD_CACHE_SLIDE4_PAGE_INDEX ) {
+                        _diagnostics.error("rebase overflow in v4 page extras");
+                        return;
+                    }
+                    pageExtras.push_back(startValue);
+                    startValue = indexInExtras | DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA;
+                }
+                pageExtras.push_back(i);
+            }
+            lastLocationOffset = offset;
+        }
+    }
+    if ( lastLocationOffset != 0xFFFF ) {
+        // mark end of chain
+        pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset];
+        pint_t lastValue = (pint_t)P::getP(*lastLoc);
+        pint_t newValue = ((lastValue - valueAdd) & valueMask);
+        P::setP(*lastLoc, newValue);
+    }
+    if ( startValue & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
+        // add end bit to extras
+        pageExtras.back() |= DYLD_CACHE_SLIDE4_PAGE_EXTRA_END;
+    }
+    pageStarts.push_back(startValue);
+}
+
+
+
+template <typename P>
+void SharedCacheBuilder::writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount)
+{
+    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;
+        }
+        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());
+}
+#endif
+
+/*
+void CacheBuilder::writeSlideInfoV1()
+{
+    // build one 128-byte bitmap per page (4096) of DATA
+    uint8_t* const dataStart = (uint8_t*)_buffer.get() + regions[1].fileOffset;
+    uint8_t* const dataEnd   = dataStart + regions[1].size;
+    const long bitmapSize = (dataEnd - dataStart)/(4*8);
+    uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1);
+    for (void* p : _pointersForASLR) {
+        if ( (p < dataStart) || ( p > dataEnd) )
+            terminate("DATA pointer for sliding, out of range\n");
+        long offset = (long)((uint8_t*)p - dataStart);
+        if ( (offset % 4) != 0 )
+            terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset);
+        long byteIndex = offset / (4*8);
+        long bitInByte =  (offset % 32) >> 2;
+        bitmap[byteIndex] |= (1 << bitInByte);
+    }
+
+    // allocate worst case size block of all slide info
+    const unsigned entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes.
+    const unsigned toc_count = (unsigned)bitmapSize/entry_size;
+    dyld_cache_slide_info* slideInfo = (dyld_cache_slide_info*)((uint8_t*)_buffer + _slideInfoFileOffset);
+    slideInfo->version          = 1;
+    slideInfo->toc_offset       = sizeof(dyld_cache_slide_info);
+    slideInfo->toc_count        = toc_count;
+    slideInfo->entries_offset   = (slideInfo->toc_offset+2*toc_count+127)&(-128);
+    slideInfo->entries_count    = 0;
+    slideInfo->entries_size     = entry_size;
+    // append each unique entry
+    const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap;
+    dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset());
+    int entry_count = 0;
+    for (int i=0; i < toc_count; ++i) {
+        const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i];
+        // see if it is same as one already added
+        bool found = false;
+        for (int j=0; j < entry_count; ++j) {
+            if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) {
+                slideInfo->set_toc(i, j);
+                found = true;
+                break;
+            }
+        }
+        if ( !found ) {
+            // append to end
+            memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size);
+            slideInfo->set_toc(i, entry_count++);
+        }
+    }
+    slideInfo->entries_count  = entry_count;
+    ::free((void*)bitmap);
+
+    _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2);
+}
+
+*/
+
+
+void SharedCacheBuilder::setPointerContentV3(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc, uint64_t targetVMAddr, size_t next)
+{
+    assert(targetVMAddr > _readExecuteRegion.unslidLoadAddress);
+    assert(targetVMAddr < _readOnlyRegion.unslidLoadAddress+_readOnlyRegion.sizeInUse);
+    dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp;
+    uint16_t diversity;
+    bool     hasAddrDiv;
+    uint8_t  key;
+    if ( _aslrTracker.hasAuthData(loc, &diversity, &hasAddrDiv, &key) ) {
+        // if base cache address cannot fit into target, then use offset
+        tmp.arm64e.authRebase.target = _readExecuteRegion.unslidLoadAddress;
+        if (  tmp.arm64e.authRebase.target != _readExecuteRegion.unslidLoadAddress )
+            targetVMAddr -= _readExecuteRegion.unslidLoadAddress;
+        loc->arm64e.authRebase.target    = targetVMAddr;
+        loc->arm64e.authRebase.diversity = diversity;
+        loc->arm64e.authRebase.addrDiv   = hasAddrDiv;
+        loc->arm64e.authRebase.key       = key;
+        loc->arm64e.authRebase.next      = next;
+        loc->arm64e.authRebase.bind      = 0;
+        loc->arm64e.authRebase.auth      = 1;
+        assert(loc->arm64e.authRebase.target == targetVMAddr && "target truncated");
+        assert(loc->arm64e.authRebase.next == next && "next location truncated");
+    }
+    else {
+        uint8_t highByte = 0;
+        _aslrTracker.hasHigh8(loc, &highByte);
+        // if base cache address cannot fit into target, then use offset
+        tmp.arm64e.rebase.target = _readExecuteRegion.unslidLoadAddress;
+        if ( tmp.arm64e.rebase.target != _readExecuteRegion.unslidLoadAddress )
+            targetVMAddr -= _readExecuteRegion.unslidLoadAddress;
+        loc->arm64e.rebase.target   = targetVMAddr;
+        loc->arm64e.rebase.high8    = highByte;
+        loc->arm64e.rebase.next     = next;
+        loc->arm64e.rebase.bind     = 0;
+        loc->arm64e.rebase.auth     = 0;
+        assert(loc->arm64e.rebase.target == targetVMAddr && "target truncated");
+        assert(loc->arm64e.rebase.next == next && "next location truncated");
+    }
+}
+
+uint16_t SharedCacheBuilder::pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[])
+{
+    const int maxPerPage = pageSize / 4;
+    uint16_t result = DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE;
+    dyld3::MachOLoaded::ChainedFixupPointerOnDisk* lastLoc = nullptr;
+    for (int i=0; i < maxPerPage; ++i) {
+        if ( bitmap[i] ) {
+            if ( result == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) {
+                // found first rebase location in page
+                result = i * 4;
+            }
+            dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)(pageContent + i*4);;
+            if ( lastLoc != nullptr ) {
+                // convert vmaddr based pointers to arm64e dyld cache chains
+                setPointerContentV3(lastLoc, lastLoc->raw64, loc - lastLoc);
+            }
+            lastLoc = loc;
+        }
+    }
+    if ( lastLoc != nullptr ) {
+        // convert vmaddr based pointers to arm64e dyld cache chain, and mark end of chain
+        setPointerContentV3(lastLoc, lastLoc->raw64, 0);
+    }
+    return result;
+}
+
+
+void SharedCacheBuilder::writeSlideInfoV3(const bool bitmap[], unsigned dataPageCount)
+{
+    const uint32_t pageSize = 4096;
+
+    // fill in fixed info
+    assert(_slideInfoFileOffset != 0);
+    dyld_cache_slide_info3* info = (dyld_cache_slide_info3*)_readOnlyRegion.buffer;
+    info->version           = 3;
+    info->page_size         = pageSize;
+    info->page_starts_count = dataPageCount;
+    info->auth_value_add    = _archLayout->sharedMemoryStart;
+
+    // fill in per-page starts
+    uint8_t* pageContent = _readWriteRegion.buffer;
+    const bool* bitmapForPage = bitmap;
+    for (unsigned i=0; i < dataPageCount; ++i) {
+        info->page_starts[i] = pageStartV3(pageContent, pageSize, bitmapForPage);
+        pageContent += pageSize;
+        bitmapForPage += (sizeof(bool)*(pageSize/4));
+    }
+
+    // update header with final size
+    dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+    dyldCacheHeader->slideInfoSize = align(__offsetof(dyld_cache_slide_info3, page_starts[dataPageCount]), _archLayout->sharedRegionAlignP2);
+    if ( dyldCacheHeader->slideInfoSize > _slideInfoBufferSizeAllocated ) {
+        _diagnostics.error("kernel slide info overflow buffer");
+    }
+}
diff --git a/dyld3/shared-cache/SharedCacheBuilder.h b/dyld3/shared-cache/SharedCacheBuilder.h
new file mode 100644 (file)
index 0000000..97520b1
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * 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 SharedCacheBuilder_h
+#define SharedCacheBuilder_h
+
+#include "CacheBuilder.h"
+#include "DyldSharedCache.h"
+#include "ClosureFileSystem.h"
+
+class SharedCacheBuilder : public CacheBuilder {
+public:
+    SharedCacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem);
+
+    void                                        build(std::vector<InputFile>& inputFiles,
+                                                      std::vector<DyldSharedCache::FileAlias>& aliases);
+    void                                        build(const std::vector<LoadedMachO>& dylibs,
+                                                      const std::vector<LoadedMachO>& otherOsDylibsInput,
+                                                      const std::vector<LoadedMachO>& osExecutables,
+                                                      std::vector<DyldSharedCache::FileAlias>& aliases);
+    void                                        build(const std::vector<DyldSharedCache::MappedMachO>&  dylibsToCache,
+                                                      const std::vector<DyldSharedCache::MappedMachO>&  otherOsDylibs,
+                                                      const std::vector<DyldSharedCache::MappedMachO>&  osExecutables,
+                                                      std::vector<DyldSharedCache::FileAlias>& aliases);
+
+    void                                        writeFile(const std::string& path);
+    void                                        writeBuffer(uint8_t*& buffer, uint64_t& size);
+    void                                        writeMapFile(const std::string& path);
+    std::string                                 getMapFileBuffer(const std::string& cacheDisposition) const;
+    void                                        deleteBuffer();
+    const std::set<std::string>                 warnings();
+    const std::set<const dyld3::MachOAnalyzer*> evictions();
+    const bool                                  agileSignature();
+    const std::string                           cdHashFirst();
+    const std::string                           cdHashSecond();
+    const std::string                           uuid() const;
+
+    void                                        forEachCacheDylib(void (^callback)(const std::string& path));
+
+private:
+
+    void        writeSlideInfoV1();
+
+    template <typename P> void writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount);
+    template <typename P> bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info);
+    template <typename P> void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info,
+                                             std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
+
+    void        writeSlideInfoV3(const bool bitmap[], unsigned dataPageCoun);
+    uint16_t    pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[]);
+    void        setPointerContentV3(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc, uint64_t targetVMAddr, size_t next);
+
+    template <typename P> void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount);
+    template <typename P> bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info);
+    template <typename P> void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info,
+                                             std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
+
+    struct ArchLayout
+    {
+        uint64_t    sharedMemoryStart;
+        uint64_t    sharedMemorySize;
+        uint64_t    textAndDataMaxSize;
+        uint64_t    sharedRegionPadding;
+        uint64_t    pointerDeltaMask;
+        const char* archName;
+        uint16_t    csPageSize;
+        uint8_t     sharedRegionAlignP2;
+        uint8_t     slideInfoBytesPerPage;
+        bool        sharedRegionsAreDiscontiguous;
+        bool        is64;
+        bool        useValueAdd;
+    };
+
+    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        assignSegmentAddresses();
+
+    uint64_t    cacheOverflowAmount();
+    size_t      evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs);
+
+    void        fipsSign();
+    void        codeSign();
+    uint64_t    pathHash(const char* path);
+    void        writeCacheHeader();
+    void        findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
+    void        addImageArray();
+    void        buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases);
+    void        addOtherImageArray(const std::vector<LoadedMachO>&, std::vector<const LoadedMachO*>& overflowDylibs);
+    void        addClosures(const std::vector<LoadedMachO>&);
+    void        markPaddingInaccessible();
+
+    bool        writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset));
+
+    // implemented in OptimizerObjC.cpp
+    void        optimizeObjC();
+    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();
+
+    typedef std::unordered_map<std::string, const dyld3::MachOAnalyzer*> InstallNameToMA;
+
+    typedef uint64_t                                                CacheOffset;
+
+    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;
+    uint8_t*                                    _objcReadWriteBuffer                    = nullptr;
+    uint64_t                                    _objcReadWriteBufferSizeAllocated       = 0;
+    uint64_t                                    _selectorStringsFromExecutables         = 0;
+    InstallNameToMA                             _installNameToCacheDylib;
+    std::unordered_map<std::string, uint32_t>   _dataDirtySegsOrder;
+    std::map<void*, std::string>                _missingWeakImports;
+    const dyld3::closure::ImageArray*           _imageArray                             = nullptr;
+    uint8_t                                     _cdHashFirst[20];
+    uint8_t                                     _cdHashSecond[20];
+    bool                                        _someDylibsUsedChainedFixups            = false;
+    std::unordered_map<const dyld3::MachOLoaded*, std::set<CacheOffset>>        _dylibToItsExports;
+    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;
+};
+
+
+
+#endif /* SharedCacheBuilder_h */
index 4841dc7ad9aa0395729a5697ddfe6048cdaeed37..0bbcc1e3a57fe24a1e9b773a584d3d462550a273 100644 (file)
 #include <spawn.h>
 
 #include <Bom/Bom.h>
+#include <Foundation/NSData.h>
+#include <Foundation/NSDictionary.h>
+#include <Foundation/NSPropertyList.h>
+#include <Foundation/NSString.h>
 
-#include "Manifest.h"
 #include "Diagnostics.h"
 #include "DyldSharedCache.h"
-#include "BuilderUtils.h"
 #include "FileUtils.h"
 #include "JSONReader.h"
 #include "JSONWriter.h"
@@ -149,13 +151,6 @@ void processRoots(Diagnostics& diags, std::set<std::string>& roots, const char *
             args[3] = (char*)"-C";
             args[4] = tempRootDir;
             args[5] = nullptr;
-        } else if (endsWith(root, ".xar")) {
-            args[0] = (char*)"/usr/bin/xar";
-            args[1] = (char*)"-xf";
-            args[2] = (char*)root.c_str();
-            args[3] = (char*)"-C";
-            args[4] = tempRootDir;
-            args[5] = nullptr;
         } else if (endsWith(root, ".zip")) {
             args[0] = (char*)"/usr/bin/ditto";
             args[1] = (char*)"-xk";
@@ -199,17 +194,6 @@ void writeRootList(const std::string& dstRoot, const std::set<std::string>& root
     ::fclose(froots);
 }
 
-BOMCopierCopyOperation filteredCopyExcludingPaths(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;
-    if (cachePaths->count(absolutePath)) {
-        return BOMCopierSkipFile;
-    }
-    return BOMCopierContinue;
-}
-
 BOMCopierCopyOperation filteredCopyIncludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
 {
     std::string absolutePath = &path[1];
@@ -225,19 +209,6 @@ BOMCopierCopyOperation filteredCopyIncludingPaths(BOMCopier copier, const char*
     return BOMCopierSkipFile;
 }
 
-static std::string dispositionToString(Disposition disposition) {
-    switch (disposition) {
-        case Unknown:
-            return "Unknown";
-        case InternalDevelopment:
-            return "InternalDevelopment";
-        case Customer:
-            return "Customer";
-        case InternalMinDevelopment:
-            return "InternalMinDevelopment";
-    }
-}
-
 static Disposition stringToDisposition(Diagnostics& diags, const std::string& str) {
     if (diags.hasError())
         return Unknown;
@@ -252,31 +223,6 @@ static Disposition stringToDisposition(Diagnostics& diags, const std::string& st
     return Unknown;
 }
 
-static std::string platformToString(Platform platform) {
-    switch (platform) {
-        case unknown:
-            return "unknown";
-        case macOS:
-            return "macOS";
-        case iOS:
-            return "iOS";
-        case tvOS:
-            return "tvOS";
-        case watchOS:
-            return "watchOS";
-        case bridgeOS:
-            return "bridgeOS";
-        case iOSMac:
-            return "UIKitForMac";
-        case iOS_simulator:
-            return "iOS_simulator";
-        case tvOS_simulator:
-            return "tvOS_simulator";
-        case watchOS_simulator:
-            return "watchOS_simulator";
-    }
-}
-
 static Platform stringToPlatform(Diagnostics& diags, const std::string& str) {
     if (diags.hasError())
         return unknown;
@@ -305,23 +251,6 @@ static Platform stringToPlatform(Diagnostics& diags, const std::string& str) {
     return unknown;
 }
 
-static std::string fileFlagsToString(FileFlags fileFlags) {
-    switch (fileFlags) {
-        case NoFlags:
-            return "NoFlags";
-        case MustBeInCache:
-            return "MustBeInCache";
-        case ShouldBeExcludedFromCacheIfUnusedLeaf:
-            return "ShouldBeExcludedFromCacheIfUnusedLeaf";
-        case RequiredClosure:
-            return "RequiredClosure";
-        case DylibOrderFile:
-            return "DylibOrderFile";
-        case DirtyDataOrderFile:
-            return "DirtyDataOrderFile";
-    }
-}
-
 static FileFlags stringToFileFlags(Diagnostics& diags, const std::string& str) {
     if (diags.hasError())
         return NoFlags;
@@ -340,21 +269,6 @@ static FileFlags stringToFileFlags(Diagnostics& diags, const std::string& str) {
     return NoFlags;
 }
 
-static dyld3::json::Node getBuildOptionsNode(BuildOptions_v1 buildOptions) {
-    dyld3::json::Node buildOptionsNode;
-    buildOptionsNode.map["version"].value             = dyld3::json::decimal(buildOptions.version);
-    buildOptionsNode.map["updateName"].value          = buildOptions.updateName;
-    buildOptionsNode.map["deviceName"].value          = buildOptions.deviceName;
-    buildOptionsNode.map["disposition"].value         = dispositionToString(buildOptions.disposition);
-    buildOptionsNode.map["platform"].value            = platformToString(buildOptions.platform);
-    for (unsigned i = 0; i != buildOptions.numArchs; ++i) {
-        dyld3::json::Node archNode;
-        archNode.value = buildOptions.archs[i];
-        buildOptionsNode.map["archs"].array.push_back(archNode);
-    }
-    return buildOptionsNode;
-}
-
 struct SharedCacheBuilderOptions {
     Diagnostics           diags;
     std::set<std::string> roots;
@@ -370,7 +284,6 @@ struct SharedCacheBuilderOptions {
     std::string           dstRoot;
     std::string           emitJSONPath;
     std::string           buildAllPath;
-    std::string           configuration;
     std::string           resultPath;
     std::string           baselineDifferenceResultPath;
     std::string           baselineCacheMapPath;
@@ -378,7 +291,7 @@ struct SharedCacheBuilderOptions {
 };
 
 static void loadMRMFiles(Diagnostics& diags,
-                         SharedCacheBuilder* sharedCacheBuilder,
+                         MRMSharedCacheBuilder* sharedCacheBuilder,
                          const std::vector<std::tuple<std::string, std::string, FileFlags>>& inputFiles,
                          std::vector<std::pair<const void*, size_t>>& mappedFiles,
                          const std::set<std::string>& baselineCacheFiles) {
@@ -430,11 +343,11 @@ static void unloadMRMFiles(std::vector<std::pair<const void*, size_t>>& mappedFi
         ::munmap((void*)mappedFile.first, mappedFile.second);
 }
 
-static void writeMRMResults(bool cacheBuildSuccess, SharedCacheBuilder* sharedCacheBuilder, const SharedCacheBuilderOptions& options) {
+static void writeMRMResults(bool cacheBuildSuccess, MRMSharedCacheBuilder* sharedCacheBuilder, const SharedCacheBuilderOptions& options) {
     if (!cacheBuildSuccess) {
         uint64_t errorCount = 0;
         if (const char* const* errors = getErrors(sharedCacheBuilder, &errorCount)) {
-            for (uint64 i = 0, e = errorCount; i != e; ++i) {
+            for (uint64_t i = 0, e = errorCount; i != e; ++i) {
                 const char* errorMessage = errors[i];
                 fprintf(stderr, "ERROR: %s\n", errorMessage);
             }
@@ -444,7 +357,7 @@ static void writeMRMResults(bool cacheBuildSuccess, SharedCacheBuilder* sharedCa
     // Now emit each cache we generated, or the errors for them.
     uint64_t cacheResultCount = 0;
     if (const CacheResult* const* cacheResults = getCacheResults(sharedCacheBuilder, &cacheResultCount)) {
-        for (uint64 i = 0, e = cacheResultCount; i != e; ++i) {
+        for (uint64_t i = 0, e = cacheResultCount; i != e; ++i) {
             const CacheResult& result = *(cacheResults[i]);
             // Always print the warnings if we have roots, even if there are errors
             if ( (result.numErrors == 0) || !options.roots.empty() ) {
@@ -470,7 +383,7 @@ static void writeMRMResults(bool cacheBuildSuccess, SharedCacheBuilder* sharedCa
     if (cacheBuildSuccess && !options.dstRoot.empty()) {
         uint64_t fileResultCount = 0;
         if (const FileResult* const* fileResults = getFileResults(sharedCacheBuilder, &fileResultCount)) {
-            for (uint64 i = 0, e = fileResultCount; i != e; ++i) {
+            for (uint64_t i = 0, e = fileResultCount; i != e; ++i) {
                 const FileResult& result = *(fileResults[i]);
 
                 switch (result.behavior) {
@@ -515,382 +428,6 @@ static void writeMRMResults(bool cacheBuildSuccess, SharedCacheBuilder* sharedCa
     }
 }
 
-static void reportUnknownConfiguration(const std::string& configuration, dyld3::Manifest& manifest) {
-    fprintf(stderr, "** Unknown config '%s' for build %s.\n",
-            configuration.c_str(), manifest.build().c_str());
-
-    // Look for available configurations that the user might have meant.
-    // Substring match: print configs that contain the user's string.
-    // Regex match: if user wants "N61OS" then match .*N.*6.*1.*O.*S.*
-
-    std::string patternString = ".*";
-    for (auto c : configuration) {
-        if (isalnum(c)) {  // filter regex special characters
-            patternString += c;
-            patternString += ".*";
-        }
-    }
-    std::regex pattern(patternString);
-
-    std::vector<std::string> likelyConfigs{};
-    std::vector<std::string> allConfigs{};
-    manifest.forEachConfiguration([&](const std::string& configName) {
-        allConfigs.push_back(configName);
-        if (!configuration.empty()) {
-            if (configName.find(configuration) != std::string::npos || std::regex_match(configName, pattern)) {
-                likelyConfigs.push_back(configName);
-            }
-        }
-    });
-
-    if (!likelyConfigs.empty()) {
-        fprintf(stderr, "\nDid you mean:\n");
-        for (auto configName: likelyConfigs) {
-            fprintf(stderr, "%s\n", configName.c_str());
-        }
-    }
-
-    fprintf(stderr, "\nAvailable configurations:\n");
-    for (auto configName : allConfigs) {
-        fprintf(stderr, "%s\n", configName.c_str());
-    }
-}
-
-static void buildCacheFromPListManifest(Diagnostics& diags, const SharedCacheBuilderOptions& options) {
-    // Get the list of configurations, without fetching all of the files.
-    auto manifest = dyld3::Manifest(diags, options.dylibCacheDir + "/Manifest.plist", false);
-
-    if (manifest.build().empty()) {
-        fprintf(stderr, "No manifest found at '%s/Manifest.plist'\n", options.dylibCacheDir.c_str());
-        exit(-1);
-    }
-
-    // List configurations if requested.
-    if (options.listConfigs) {
-        manifest.forEachConfiguration([](const std::string& configName) {
-            printf("%s\n", configName.c_str());
-        });
-        // If we weren't passed a configuration then exit
-        if (options.configuration.empty())
-            exit(0);
-    }
-
-    // Stop if the requested configuration is unavailable.
-    if (!manifest.filterForConfig(options.configuration)) {
-        reportUnknownConfiguration(options.configuration, manifest);
-        exit(-1);
-    }
-
-    // Now finish initializing the manifest.
-    // This step is slow so we defer it until after the checks above.
-    fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str());
-    manifest.populate(options.roots);
-
-    (void)mkpath_np((options.dstRoot + "/System/Library/Caches/com.apple.dyld/").c_str(), 0755);
-    bool cacheBuildSuccess = false;
-    if (options.useMRM) {
-
-        std::ofstream jsonFile;
-        if (!options.emitJSONPath.empty()) {
-            jsonFile.open(options.emitJSONPath, std::ofstream::out);
-            if (!jsonFile.is_open()) {
-                diags.verbose("can't open file '%s'\n", options.emitJSONPath.c_str());
-                return;
-            }
-        }
-        dyld3::json::Node buildInvocationNode;
-
-        buildInvocationNode.map["version"].value = "1";
-
-        // Find the archs for the configuration we want.
-        __block std::set<std::string> validArchs;
-        manifest.configuration(options.configuration).forEachArchitecture(^(const std::string& path) {
-            validArchs.insert(path);
-        });
-
-        if (validArchs.size() != 1) {
-            fprintf(stderr, "MRM doesn't support more than one arch per configuration: %s\n",
-                    options.configuration.c_str());
-            exit(-1);
-        }
-
-        const char* archs[validArchs.size()];
-        uint64_t archIndex = 0;
-        for (const std::string& arch : validArchs) {
-            archs[archIndex++] = arch.c_str();
-        }
-
-        BuildOptions_v1 buildOptions;
-        buildOptions.version                            = 1;
-        buildOptions.updateName                         = manifest.build().c_str();
-        buildOptions.deviceName                         = options.configuration.c_str();
-        buildOptions.disposition                        = Disposition::Unknown;
-        buildOptions.platform                           = (Platform)manifest.platform();
-        buildOptions.archs                              = archs;
-        buildOptions.numArchs                           = validArchs.size();
-        buildOptions.verboseDiagnostics                 = options.debug;
-        buildOptions.isLocallyBuiltCache                = true;
-
-        __block struct SharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions);
-        buildInvocationNode.map["buildOptions"] = getBuildOptionsNode(buildOptions);
-
-        std::set<std::string> requiredBinaries =  {
-            "/usr/lib/libSystem.B.dylib"
-        };
-
-        // Get the file data for every MachO in the BOM.
-        __block dyld3::json::Node filesNode;
-        __block std::vector<std::pair<const void*, size_t>> mappedFiles;
-        manifest.forEachMachO(options.configuration, ^(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf) {
-
-            // Filter based on arch as the Manifest adds the file once for each UUID.
-            if (!validArchs.count(arch))
-                return;
-
-            struct stat stat_buf;
-            int fd = ::open(buildPath.c_str(), O_RDONLY, 0);
-            if (fd == -1) {
-                diags.verbose("can't open file '%s', errno=%d\n", buildPath.c_str(), errno);
-                return;
-            }
-
-            if (fstat(fd, &stat_buf) == -1) {
-                diags.verbose("can't stat open file '%s', errno=%d\n", buildPath.c_str(), errno);
-                ::close(fd);
-                return;
-            }
-
-            const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-            if (buffer == MAP_FAILED) {
-                diags.verbose("mmap() for file at %s failed, errno=%d\n", buildPath.c_str(), errno);
-                ::close(fd);
-            }
-            ::close(fd);
-
-            mappedFiles.emplace_back(buffer, (size_t)stat_buf.st_size);
-            FileFlags fileFlags = FileFlags::NoFlags;
-            if (requiredBinaries.count(runtimePath))
-                fileFlags = FileFlags::RequiredClosure;
-            addFile(sharedCacheBuilder, runtimePath.c_str(), (uint8_t*)buffer, (size_t)stat_buf.st_size, fileFlags);
-
-            dyld3::json::Node fileNode;
-            fileNode.map["path"].value  = runtimePath;
-            fileNode.map["flags"].value = fileFlagsToString(fileFlags);
-            filesNode.array.push_back(fileNode);
-        });
-
-        __block dyld3::json::Node symlinksNode;
-        manifest.forEachSymlink(options.configuration, ^(const std::string &fromPath, const std::string &toPath) {
-            addSymlink(sharedCacheBuilder, fromPath.c_str(), toPath.c_str());
-
-            dyld3::json::Node symlinkNode;
-            symlinkNode.map["from-path"].value  = fromPath;
-            symlinkNode.map["to-path"].value    = toPath;
-            symlinksNode.array.push_back(symlinkNode);
-        });
-
-        buildInvocationNode.map["symlinks"] = symlinksNode;
-
-        std::string orderFileData;
-        if (!manifest.dylibOrderFile().empty()) {
-            orderFileData = loadOrderFile(manifest.dylibOrderFile());
-            if (!orderFileData.empty()) {
-                addFile(sharedCacheBuilder, "*order file data*", (uint8_t*)orderFileData.data(), orderFileData.size(), FileFlags::DylibOrderFile);
-                dyld3::json::Node fileNode;
-                fileNode.map["path"].value  = manifest.dylibOrderFile();
-                fileNode.map["flags"].value = fileFlagsToString(FileFlags::DylibOrderFile);
-                filesNode.array.push_back(fileNode);
-            }
-        }
-
-        std::string dirtyDataOrderFileData;
-        if (!manifest.dirtyDataOrderFile().empty()) {
-            dirtyDataOrderFileData = loadOrderFile(manifest.dirtyDataOrderFile());
-            if (!dirtyDataOrderFileData.empty()) {
-                addFile(sharedCacheBuilder, "*dirty data order file data*", (uint8_t*)dirtyDataOrderFileData.data(), dirtyDataOrderFileData.size(), FileFlags::DirtyDataOrderFile);
-                dyld3::json::Node fileNode;
-                fileNode.map["path"].value  = manifest.dirtyDataOrderFile();
-                fileNode.map["flags"].value = fileFlagsToString(FileFlags::DirtyDataOrderFile);
-                filesNode.array.push_back(fileNode);
-            }
-        }
-
-        buildInvocationNode.map["files"] = filesNode;
-
-        if (jsonFile.is_open()) {
-            dyld3::json::printJSON(buildInvocationNode, 0, jsonFile);
-            jsonFile.close();
-        }
-
-        cacheBuildSuccess = runSharedCacheBuilder(sharedCacheBuilder);
-
-        writeMRMResults(cacheBuildSuccess, sharedCacheBuilder, options);
-
-        destroySharedCacheBuilder(sharedCacheBuilder);
-
-        for (auto mappedFile : mappedFiles)
-            ::munmap((void*)mappedFile.first, mappedFile.second);
-    } else {
-        manifest.calculateClosure();
-
-        cacheBuildSuccess = build(diags, manifest, options.dstRoot, false, options.debug, false, false, options.emitDevCaches, true);
-    }
-
-    if (!cacheBuildSuccess) {
-        exit(-1);
-    }
-
-    // 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> baselineDylibs = manifest.resultsForConfiguration(options.configuration);
-
-        std::set<std::string> newDylibs;
-        std::map<std::string, std::string> missingDylibReasons;
-        manifest.forEachConfiguration([&manifest, &newDylibs, &missingDylibReasons](const std::string& configName) {
-            for (auto& arch : manifest.configuration(configName).architectures) {
-                for (auto& dylib : arch.second.results.dylibs) {
-                    if (dylib.second.included) {
-                        newDylibs.insert(manifest.installNameForUUID(dylib.first));
-                    } else {
-                        missingDylibReasons[manifest.installNameForUUID(dylib.first)] = dylib.second.exclusionInfo;
-                    }
-                }
-            }
-        });
-
-        // Work out the set of dylibs in the old cache but not the new one
-        std::map<std::string, std::string> dylibsMissingFromNewCache;
-        if (options.baselineCopyRoots || !options.baselineDifferenceResultPath.empty()) {
-            for (const std::string& baselineDylib : baselineDylibs) {
-                if (!newDylibs.count(baselineDylib)) {
-                    auto reasonIt = missingDylibReasons.find(baselineDylib);
-                    if (reasonIt != missingDylibReasons.end())
-                        dylibsMissingFromNewCache[baselineDylib] = reasonIt->second;
-                    else
-                        dylibsMissingFromNewCache[baselineDylib] = "";
-                }
-            }
-
-            if (!dylibsMissingFromNewCache.empty()) {
-                // Work out which dylibs are missing from the new cache, but are not
-                // coming from the -root which already has them on disk
-                std::set<std::string> pathsNotInRoots;
-                for (std::pair<std::string, std::string> dylibMissingFromNewCache : dylibsMissingFromNewCache) {
-                    const std::string& dylibInstallName = dylibMissingFromNewCache.first;
-                    bool foundInRoot = false;
-                    for (auto& root : options.roots) {
-                        struct stat sb;
-                        std::string filePath = root + "/" + dylibInstallName;
-                        if (!stat(filePath.c_str(), &sb)) {
-                            foundInRoot = true;
-                        }
-                    }
-                    if (!foundInRoot)
-                        pathsNotInRoots.insert(dylibInstallName);
-                }
-
-                BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
-                BOMCopierSetUserData(copier, (void*)&pathsNotInRoots);
-                BOMCopierSetCopyFileStartedHandler(copier, filteredCopyIncludingPaths);
-                std::string dylibCacheRootDir = realFilePath(options.dylibCacheDir + "/Root");
-                if (dylibCacheRootDir == "") {
-                    fprintf(stderr, "Could not find dylib Root directory to copy baseline roots from\n");
-                    exit(1);
-                }
-                BOMCopierCopy(copier, dylibCacheRootDir.c_str(), options.dstRoot.c_str());
-                BOMCopierFree(copier);
-
-                for (std::pair<std::string, std::string> dylibMissingFromNewCache : dylibsMissingFromNewCache) {
-                    if (dylibMissingFromNewCache.second.empty())
-                        diags.verbose("Dylib missing from new cache: '%s'\n", dylibMissingFromNewCache.first.c_str());
-                    else
-                        diags.verbose("Dylib missing from new cache: '%s' because '%s'\n",
-                                      dylibMissingFromNewCache.first.c_str(), dylibMissingFromNewCache.second.c_str());
-                }
-            }
-        }
-
-        if (!options.baselineDifferenceResultPath.empty()) {
-            auto cppToObjStr = [](const std::string& str) {
-                return [NSString stringWithUTF8String:str.c_str()];
-            };
-
-            // Work out the set of dylibs in the cache and taken from the -root
-            NSMutableArray<NSString*>* dylibsFromRoots = [NSMutableArray array];
-            for (auto& root : options.roots) {
-                for (const std::string& dylibInstallName : newDylibs) {
-                    struct stat sb;
-                    std::string filePath = root + "/" + dylibInstallName;
-                    if (!stat(filePath.c_str(), &sb)) {
-                        [dylibsFromRoots addObject:cppToObjStr(dylibInstallName)];
-                    }
-                }
-            }
-
-            // Work out the set of dylibs in the new cache but not in the baseline cache.
-            NSMutableArray<NSString*>* dylibsMissingFromBaselineCache = [NSMutableArray array];
-            for (const std::string& newDylib : newDylibs) {
-                if (!baselineDylibs.count(newDylib))
-                    [dylibsMissingFromBaselineCache addObject:cppToObjStr(newDylib)];
-            }
-
-            // If a dylib which was cached is no longer eligible, say why
-            NSMutableArray<NSDictionary*>* dylibsReasonsMissingFromNewCache = [NSMutableArray array];
-            for (std::pair<std::string, std::string> dylibMissingFromNewCache : dylibsMissingFromNewCache) {
-                NSMutableDictionary* reasonDict = [[NSMutableDictionary alloc] init];
-                reasonDict[@"path"] = cppToObjStr(dylibMissingFromNewCache.first);
-                reasonDict[@"reason"] = cppToObjStr(dylibMissingFromNewCache.second);
-                [dylibsReasonsMissingFromNewCache addObject:reasonDict];
-            }
-
-            NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
-            cacheDict[@"root-paths-in-cache"] = dylibsFromRoots;
-            cacheDict[@"device-paths-to-delete"] = dylibsMissingFromBaselineCache;
-            cacheDict[@"baseline-paths-evicted-from-cache"] = dylibsReasonsMissingFromNewCache;
-
-            NSError* error = nil;
-            NSData*  outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
-                                                                          format:NSPropertyListBinaryFormat_v1_0
-                                                                         options:0
-                                                                           error:&error];
-            (void)[outData writeToFile:cppToObjStr(options.baselineDifferenceResultPath) atomically:YES];
-        }
-    }
-
-    if (options.copyRoots) {
-        std::set<std::string> cachePaths;
-        manifest.forEachConfiguration([&manifest, &cachePaths](const std::string& configName) {
-            for (auto& arch : manifest.configuration(configName).architectures) {
-                for (auto& dylib : arch.second.results.dylibs) {
-                    if (dylib.second.included) {
-                        cachePaths.insert(manifest.installNameForUUID(dylib.first));
-                    }
-                }
-            }
-        });
-
-        BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
-        BOMCopierSetUserData(copier, (void*)&cachePaths);
-        BOMCopierSetCopyFileStartedHandler(copier, filteredCopyExcludingPaths);
-        for (auto& root : options.roots) {
-            BOMCopierCopy(copier, root.c_str(), options.dstRoot.c_str());
-        }
-        BOMCopierFree(copier);
-    }
-
-    int err = sync_volume_np(options.dstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT);
-    if (err) {
-        fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err));
-    }
-
-    // Now that all the build commands have been issued lets put a barrier in after then which can tear down the app after
-    // everything is written.
-
-    if (!options.resultPath.empty()) {
-        manifest.write(options.resultPath);
-    }
-}
-
 static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuilderOptions& options,
                                        const std::string& jsonManifestPath) {
     dyld3::json::Node manifestNode = dyld3::json::readJSON(diags, jsonManifestPath.c_str());
@@ -970,7 +507,7 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
     if (diags.hasError())
         return;
 
-    struct SharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions);
+    struct MRMSharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions);
 
     // Parse the files
     if (filesNode.array.empty()) {
@@ -1256,11 +793,8 @@ int main(int argc, const char* argv[])
                     exit(-1);
                 }
             } else {
-                if (!options.configuration.empty()) {
-                    fprintf(stderr, "You may only specify one configuration\n");
-                    exit(-1);
-                }
-                options.configuration = argv[i];
+                fprintf(stderr, "unknown option: %s\n", arg);
+                exit(-1);
             }
         }
         (void)options.emitElidedDylibs; // not implemented yet
@@ -1283,8 +817,8 @@ int main(int argc, const char* argv[])
             exit(-1);
         }
 
-        if (options.configuration.empty() && jsonManifestPath.empty() && options.buildAllPath.empty()) {
-            fprintf(stderr, "Must specify a configuration OR a -json_manifest path OR a -build_all path\n");
+        if (jsonManifestPath.empty() && options.buildAllPath.empty()) {
+            fprintf(stderr, "Must specify a -json_manifest path OR a -build_all path\n");
             exit(-1);
         }
 
@@ -1293,10 +827,6 @@ int main(int argc, const char* argv[])
                 fprintf(stderr, "Cannot combine -dst_root and -build_all\n");
                 exit(-1);
             }
-            if (!options.configuration.empty()) {
-                fprintf(stderr, "Cannot combine configuration and -build_all\n");
-                exit(-1);
-            }
             if (!jsonManifestPath.empty()) {
                 fprintf(stderr, "Cannot combine -json_manifest and -build_all\n");
                 exit(-1);
@@ -1319,8 +849,8 @@ int main(int argc, const char* argv[])
                 exit(-1);
             }
 
-            if (options.configuration.empty() && jsonManifestPath.empty()) {
-                fprintf(stderr, "Must specify a configuration OR -json_manifest path OR -list_configs\n");
+            if (jsonManifestPath.empty()) {
+                fprintf(stderr, "Must specify a -json_manifest path OR -list_configs\n");
                 exit(-1);
             }
         }
@@ -1465,10 +995,8 @@ int main(int argc, const char* argv[])
 
                     if (requiresConcurrencyLimit) { dispatch_semaphore_signal(concurrencyLimit); }
                 });
-            } else if (!jsonManifestPath.empty()) {
-                buildCacheFromJSONManifest(diags, options, jsonManifestPath);
             } else {
-                buildCacheFromPListManifest(diags, options);
+                buildCacheFromJSONManifest(diags, options, jsonManifestPath);
             }
 
             const char* args[8];
index 0e1817eec520d37f9600ddb59c4f3ca350cae9f0..70ab9857c8d00f879efeb71e7331f6892fa721d1 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
  *
- * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2018-2019 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
 #include "MachOAnalyzer.h"
 #include "ClosureFileSystemPhysical.h"
 
-static bool printSharedRegion = false;
-static bool printFunctionStarts = false;
-static bool printDataCode = false;
-
 
 static void versionToString(uint32_t value, char buffer[32])
 {
@@ -141,8 +137,12 @@ 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";
         case DYLD_CHAINED_PTR_64:
             return "generic 64-bit";
+        case DYLD_CHAINED_PTR_64_OFFSET:
+            return "generic 64-bit offset";
         case DYLD_CHAINED_PTR_32:
             return "generic 32-bit";
         case DYLD_CHAINED_PTR_32_CACHE:
@@ -167,13 +167,101 @@ static void printChains(const dyld3::MachOAnalyzer* ma)
             printf("  segment_offset:  0x%08llX\n",   seg->segment_offset);
             printf("  max_pointer:     0x%08X\n",     seg->max_valid_pointer);
             printf("  pages:         %d\n",           seg->page_count);
-            for (int p=0; p < seg->page_count; ++p) {
-                printf("    start[% 2d]:  0x%04X\n",   p, seg->page_start[p]);
+            for (int pageIndex=0; pageIndex < seg->page_count; ++pageIndex) {
+                uint16_t offsetInPage = seg->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 (!chainEnd) {
+                        chainEnd = (seg->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST);
+                        offsetInPage = (seg->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST);
+                        printf("    start[% 2d]:  0x%04X\n",   pageIndex, offsetInPage);
+                        ++overflowIndex;
+                    }
+                }
+                else {
+                    // one chain per page
+                    printf("    start[% 2d]:  0x%04X\n",   pageIndex, offsetInPage);
+                }
             }
         }
    });
 }
 
+
+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) {
+            uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
+            switch (segInfo->pointer_format) {
+                case DYLD_CHAINED_PTR_ARM64E:
+                case DYLD_CHAINED_PTR_ARM64E_OFFSET:
+                   if ( fixupLoc->arm64e.authRebase.auth ) {
+                        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);
+                        }
+                        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,
+                                  fixupLoc->arm64e.authRebase.next,  fixupLoc->arm64e.keyName(),
+                                  fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authRebase.target);
+                        }
+                    }
+                    else {
+                        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);
+                        }
+                        else {
+                            printf("  0x%08llX:  raw: 0x%016llX       rebase: (next: %03d, target: 0x%011llX, high8: 0x%02X)\n", vmOffset, fixupLoc->raw64,
+                                  fixupLoc->arm64e.rebase.next, fixupLoc->arm64e.rebase.target, fixupLoc->arm64e.rebase.high8);
+                        }
+                    }
+                    break;
+                case DYLD_CHAINED_PTR_64:
+                case DYLD_CHAINED_PTR_64_OFFSET:
+                    if ( fixupLoc->generic64.rebase.bind ) {
+                        printf("  0x%08llX:  raw: 0x%016llX         bind: (next: %03d, ordinal: %06X, addend: %d)\n", vmOffset, fixupLoc->raw64,
+                              fixupLoc->generic64.bind.next, fixupLoc->generic64.bind.ordinal, fixupLoc->generic64.bind.addend);
+                    }
+                    else {
+                         printf("  0x%08llX:  raw: 0x%016llX       rebase: (next: %03d, target: 0x%011llX, high8: 0x%02X)\n", vmOffset, fixupLoc->raw64,
+                               fixupLoc->generic64.rebase.next, fixupLoc->generic64.rebase.target, fixupLoc->generic64.rebase.high8);
+                    }
+                    break;
+                case DYLD_CHAINED_PTR_32:
+                    if ( fixupLoc->generic32.bind.bind ) {
+                        printf("  0x%08llX:  raw: 0x%08X    bind: (next:%02d ordinal:%05X addend:%d)\n", vmOffset, fixupLoc->raw32,
+                              fixupLoc->generic32.bind.next, fixupLoc->generic32.bind.ordinal, fixupLoc->generic32.bind.addend);
+                    }
+                    else if ( fixupLoc->generic32.rebase.target > segInfo->max_valid_pointer ) {
+                        uint32_t bias  = (0x04000000 + segInfo->max_valid_pointer)/2;
+                        uint32_t value = fixupLoc->generic32.rebase.target - bias;
+                        printf("  0x%08llX:  raw: 0x%08X  nonptr: (next:%02d value: 0x%08X)\n", vmOffset, fixupLoc->raw32,
+                              fixupLoc->generic32.rebase.next, value);
+                    }
+                    else {
+                        printf("  0x%08llX:  raw: 0x%08X  rebase: (next:%02d target: 0x%07X)\n", vmOffset, fixupLoc->raw32,
+                              fixupLoc->generic32.rebase.next, fixupLoc->generic32.rebase.target);
+                    }
+                    break;
+                default:
+                    fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format);
+                    break;
+            }
+         });
+    });
+    if ( diag.hasError() )
+        fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
+}
+
+
 struct FixupInfo
 {
     const char* segName;
@@ -229,6 +317,8 @@ public:
     const char*      segmentName(uint64_t vmOffset) const;
     const char*      sectionName(uint64_t vmOffset) const;
     uint64_t         baseAddress() const { return _baseAddress; }
+    uint64_t         currentSectionAddress() const { return _lastSection.sectAddr; }
+    bool             isNewSection(uint64_t vmOffset) const;
 
 private:
     void             updateLastSection(uint64_t vmOffset) const;
@@ -247,10 +337,16 @@ SectionFinder::SectionFinder(const dyld3::MachOAnalyzer* ma)
     _lastSection.sectSize = 0;
 }
 
-void SectionFinder::updateLastSection(uint64_t vmOffset) const
+bool SectionFinder::isNewSection(uint64_t vmOffset) const
 {
     uint64_t vmAddr = _baseAddress + vmOffset;
-    if ( (vmAddr < _lastSection.sectAddr) || (vmAddr >= _lastSection.sectAddr+_lastSection.sectSize) ) {
+    return ( (vmAddr < _lastSection.sectAddr) || (vmAddr >= _lastSection.sectAddr+_lastSection.sectSize) );
+}
+
+void SectionFinder::updateLastSection(uint64_t vmOffset) const
+{
+    if ( isNewSection(vmOffset) ) {
+        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;
@@ -332,7 +428,7 @@ static void printChainedFixups(const dyld3::MachOAnalyzer* ma)
 
     uint64_t baseAddress = ma->preferredLoadAddress();
 
-    printf("        segment     section           address       type             (dvrsty  addr  key)  target\n");
+    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) {
@@ -342,16 +438,17 @@ static void printChainedFixups(const dyld3::MachOAnalyzer* ma)
                    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\n",
+                                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);
+                                       fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, bindTarget.addend, weakImportString);
                             else
-                                printf("        %-12s %-16s 0x%08llX  %16s  (0x%04X   %d     %s)   %s/%s\n",
+                                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);
+                                       fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, weakImportString);
                          }
                         else {
                             uint64_t targetAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
@@ -365,14 +462,15 @@ static void printChainedFixups(const dyld3::MachOAnalyzer* ma)
                         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\n",
+                                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);
+                                        "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
                             else
-                                printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s\n",
+                                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);
+                                        "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
                         }
                         else {
                             uint64_t targetAddr = fixupLoc->arm64e.unpackTarget();
@@ -382,18 +480,65 @@ static void printChainedFixups(const dyld3::MachOAnalyzer* ma)
                         }
                     }
                     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\n",
+                            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);
+                                    "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
                         else
-                            printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s\n",
+                            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);
+                                    "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
                     }
                     else {
                         uint64_t targetAddr = fixupLoc->generic64.unpackedTarget();
@@ -402,18 +547,40 @@ static void printChainedFixups(const dyld3::MachOAnalyzer* ma)
                                 "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\n",
+                            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);
+                                    "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
                         else
-                            printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s\n",
+                            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);
+                                    "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
                     }
                     else {
                         uint32_t targetAddr = fixupLoc->generic32.rebase.target;
@@ -428,6 +595,8 @@ static void printChainedFixups(const dyld3::MachOAnalyzer* ma)
             }
          });
     });
+    if ( diag.hasError() )
+        fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
 }
 
 static void printOpcodeFixups(const dyld3::MachOAnalyzer* ma)
@@ -500,11 +669,304 @@ static void printOpcodeFixups(const dyld3::MachOAnalyzer* ma)
 
 }
 
+static inline std::string decimal(int64_t value) {
+    char buff[64];
+    sprintf(buff, "%lld", value);
+    return buff;
+}
+
+static std::string rebaseTargetString(const dyld3::MachOAnalyzer* ma, uint64_t vmAddr)
+{
+    uint64_t    targetLoadAddr = (uint64_t)ma+vmAddr;
+    const char* targetSymbolName;
+    uint64_t    targetSymbolLoadAddr;
+    if ( ma->findClosestSymbol(targetLoadAddr, &targetSymbolName, &targetSymbolLoadAddr) ) {
+        uint64_t delta = targetLoadAddr - targetSymbolLoadAddr;
+        if ( delta == 0 ) {
+            return targetSymbolName;
+        }
+        else {
+            return std::string(targetSymbolName) + std::string("+") + decimal(delta);
+        }
+    }
+    else {
+        __block std::string result;
+        ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+            if ( (sectInfo.sectAddr <= vmAddr) && (vmAddr < sectInfo.sectAddr+sectInfo.sectSize) ) {
+                if ( (sectInfo.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) {
+                    const char* cstring = (char*)ma + (vmAddr-ma->preferredLoadAddress());
+                    result = std::string("\"") + cstring + std::string("\"");
+                }
+                else {
+                    result = std::string(sectInfo.segInfo.segName) + "/" + sectInfo.sectName + "+" + decimal(vmAddr - sectInfo.sectAddr);
+                }
+            }
+        });
+       return result;
+    }
+}
+
+static void printSymbolicChainedFixups(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;
+
+    // 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());
+}
+
+struct SymbolicFixupInfo
+{
+    uint64_t    address;
+    const char* kind;
+    std::string target;
+};
+
+static void printSymbolicOpcodeFixups(const dyld3::MachOAnalyzer* ma)
+{
+    Diagnostics diag;
+    __block std::vector<SymbolicFixupInfo> 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));
+        SymbolicFixupInfo fixup;
+        fixup.address      = locVmAddr;
+        fixup.kind         = rebaseTypeName(type);
+        fixup.target       = rebaseTargetString(ma, value);
+        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;
+        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]";
+        fixups.push_back(fixup);
+    },^(const char* symbolName) {
+    },^() { });
+
+
+    std::sort(fixups.begin(), fixups.end(), [](const SymbolicFixupInfo& l, const SymbolicFixupInfo& r) {
+        if ( &l == &r )
+            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;
+        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;
+        if ( ma->findClosestSymbol((uint64_t)ma+vmOffset, &symbolName, &symbolLoadAddr) ) {
+            uint64_t symbolVmOffset = symbolLoadAddr - (uint64_t)ma;
+            if ( symbolVmOffset != lastSymbolVmOffset ) {
+                printf("        %s:\n", symbolName);
+                lastSymbolVmOffset = symbolVmOffset;
+            }
+        }
+        printf("           +0x%04llX  %16s   %s\n", vmOffset - lastSymbolVmOffset, fixup.kind, fixup.target.c_str());
+    }
+}
 
 static void printFixups(const dyld3::MachOAnalyzer* ma)
 {
     printf("    -fixups:\n");
-    if ( ma->isPreload() || ma->isStaticExecutable() ) {
+    if ( ma->isPreload() || (ma->isStaticExecutable() && !ma->hasChainedFixups()) ) {
         printPreloadChainedFixups(ma);
     }
     else if ( ma->hasChainedFixups() ) {
@@ -513,7 +975,23 @@ static void printFixups(const dyld3::MachOAnalyzer* 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)
@@ -842,6 +1320,14 @@ int main(int argc, const char* argv[])
                     printChains(ma);
                     somethingPrinted = true;
                 }
+                else if ( strcmp(arg, "-fixup_chain_details") == 0 ) {
+                    printChainDetails(ma);
+                    somethingPrinted = true;
+                }
+                else if ( strcmp(arg, "-symbolic_fixups") == 0 ) {
+                     printSymbolicFixups(ma);
+                     somethingPrinted = true;
+                }
                 else if ( strcmp(arg, "-opcodes") == 0 ) {
                 }
                 else if ( strcmp(arg, "-shared_region") == 0 ) {
diff --git a/dyld3/shared-cache/make_ios_dyld_cache.cpp b/dyld3/shared-cache/make_ios_dyld_cache.cpp
deleted file mode 100644 (file)
index 20f58c3..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
- *
- * Copyright (c) 2016 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <fcntl.h>
-#include <dlfcn.h>
-#include <signal.h>
-#include <errno.h>
-#include <assert.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/resource.h>
-#include <dirent.h>
-#include <rootless.h>
-#include <dscsym.h>
-#include <dispatch/dispatch.h>
-#include <pthread/pthread.h>
-
-#include <algorithm>
-#include <vector>
-#include <unordered_set>
-#include <unordered_set>
-#include <iostream>
-#include <fstream>
-
-#include "MachOFile.h"
-#include "FileUtils.h"
-#include "StringUtils.h"
-#include "DyldSharedCache.h"
-
-
-
-struct MappedMachOsByCategory
-{
-    std::string                                 archName;
-    std::vector<DyldSharedCache::MappedMachO>   dylibsForCache;
-    std::vector<DyldSharedCache::MappedMachO>   otherDylibsAndBundles;
-    std::vector<DyldSharedCache::MappedMachO>   mainExecutables;
-};
-
-static bool verbose = false;
-
-
-static bool addIfMachO(const std::string& buildRootPath, const std::string& runtimePath, const struct stat& statBuf, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
-{
-    // read start of file to determine if it is mach-o or a fat file
-    std::string fullPath = buildRootPath + runtimePath;
-    int fd = ::open(fullPath.c_str(), O_RDONLY);
-    if ( fd < 0 )
-        return false;
-    bool result = false;
-    const void* wholeFile = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-    if ( wholeFile != MAP_FAILED ) {
-        Diagnostics diag;
-        bool usedWholeFile = false;
-        for (MappedMachOsByCategory& file : files) {
-            uint64_t sliceOffset = 0;
-            uint64_t sliceLength = statBuf.st_size;
-            bool fatButMissingSlice;
-            const void* slice = MAP_FAILED;
-            const dyld3::FatFile* fh = (dyld3::FatFile*)wholeFile;
-            const dyld3::MachOFile* mh = (dyld3::MachOFile*)wholeFile;
-            if ( fh->isFatFileWithSlice(diag, statBuf.st_size, file.archName.c_str(), sliceOffset, sliceLength, fatButMissingSlice) ) {
-                slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
-                if ( slice != MAP_FAILED ) {
-                    //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str());
-                    mh = (dyld3::MachOFile*)slice;
-                    if ( !mh->isMachO(diag, sliceLength) ) {
-                        ::munmap((void*)slice, sliceLength);
-                        slice = MAP_FAILED;
-                    }
-                }
-            }
-            else if ( !fatButMissingSlice && mh->isMachO(diag, sliceLength) ) {
-                slice           = wholeFile;
-                sliceLength     = statBuf.st_size;
-                sliceOffset     = 0;
-                usedWholeFile   = true;
-                //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str());
-            }
-            if ( slice != MAP_FAILED ) {
-                mh = (dyld3::MachOFile*)slice;
-                if ( mh->platform() != platform ) {
-                    fprintf(stderr, "skipped wrong platform binary: %s\n", fullPath.c_str());
-                    result = false;
-                }
-                else {
-                    bool sip = true; // assume anything found in the simulator runtime is a platform binary
-                    if ( mh->isDynamicExecutable() ) {
-                        bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
-                        file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
-                    }
-                    else {
-                        if ( parser.canBePlacedInDyldCache(runtimePath) ) {
-                            file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
-                        }
-                    }
-                    result = true;
-                }
-            }
-        }
-        if ( !usedWholeFile )
-            ::munmap((void*)wholeFile, statBuf.st_size);
-    }
-    ::close(fd);
-    return result;
-}
-
-
-static bool parsePathsFile(const std::string& filePath, std::vector<std::string>& paths) {
-    std::ifstream myfile( filePath );
-    if ( myfile.is_open() ) {
-    std::string line;
-        while ( std::getline(myfile, line) ) {
-            size_t pos = line.find('#');
-            if ( pos != std::string::npos )
-                line.resize(pos);
-            while ( line.size() != 0 && isspace(line.back()) ) {
-                line.pop_back();
-            }
-            if ( !line.empty() )
-                paths.push_back(line);
-        }
-        myfile.close();
-        return true;
-    }
-    return false;
-}
-
-
-static void mapAllFiles(const std::string& dylibsRootDir, const std::vector<std::string>& paths, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
-{
-    for (const std::string& runtimePath : paths) {
-        std::string fullPath = dylibsRootDir + runtimePath;
-        struct stat statBuf;
-        if ( (stat(fullPath.c_str(), &statBuf) != 0) || !addIfMachO(dylibsRootDir, runtimePath, statBuf, platform, files) )
-             fprintf(stderr, "could not load: %s\n", fullPath.c_str());
-    }
-}
-
-
-
-inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
-{
-    return (uint32_t)(abstime/1000/1000);
-}
-
-
-#define TERMINATE_IF_LAST_ARG( s )      \
-    do {                                \
-        if ( i == argc - 1 ) {          \
-            fprintf(stderr, s );        \
-            return 1;                   \
-        }                               \
-    } while ( 0 )
-
-int main(int argc, const char* argv[])
-{
-    std::string                     rootPath;
-    std::string                     dylibListFile;
-    bool                            force = false;
-    std::string                     cacheDir;
-    std::string                     dylibsList;
-    std::unordered_set<std::string> archStrs;
-
-    dyld3::Platform platform = dyld3::Platform::iOS;
-
-    // parse command line options
-    for (int i = 1; i < argc; ++i) {
-        const char* arg = argv[i];
-        if (strcmp(arg, "-debug") == 0) {
-            verbose = true;
-        }
-        else if (strcmp(arg, "-verbose") == 0) {
-            verbose = true;
-        }
-        else if (strcmp(arg, "-tvOS") == 0) {
-            platform = dyld3::Platform::tvOS;
-        }
-        else if (strcmp(arg, "-iOS") == 0) {
-            platform = dyld3::Platform::iOS;
-        }
-        else if (strcmp(arg, "-watchOS") == 0) {
-            platform = dyld3::Platform::watchOS;
-        }
-        else if ( strcmp(arg, "-root") == 0 ) {
-            TERMINATE_IF_LAST_ARG("-root missing path argument\n");
-            rootPath = argv[++i];
-        }
-        else if ( strcmp(arg, "-dylibs_list") == 0 ) {
-            TERMINATE_IF_LAST_ARG("-dylibs_list missing path argument\n");
-            dylibsList = argv[++i];
-        }
-        else if (strcmp(arg, "-cache_dir") == 0) {
-            TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n");
-            cacheDir = argv[++i];
-        }
-        else if (strcmp(arg, "-arch") == 0) {
-            TERMINATE_IF_LAST_ARG("-arch missing argument\n");
-            archStrs.insert(argv[++i]);
-        }
-        else if (strcmp(arg, "-force") == 0) {
-            force = true;
-        }
-        else {
-            //usage();
-            fprintf(stderr, "update_dyld_sim_shared_cache: unknown option: %s\n", arg);
-            return 1;
-        }
-    }
-
-    if ( cacheDir.empty() ) {
-        fprintf(stderr, "missing -cache_dir <path> option to specify directory in which to write cache file(s)\n");
-        return 1;
-    }
-
-    if ( rootPath.empty() ) {
-        fprintf(stderr, "missing -runtime_dir <path> option to specify directory which is root of simulator runtime)\n");
-        return 1;
-    }
-    else {
-        // canonicalize rootPath
-        char resolvedPath[PATH_MAX];
-        if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
-            rootPath = resolvedPath;
-        }
-        if ( rootPath.back() != '/' )
-            rootPath = rootPath + "/";
-    }
-
-    int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
-    if ( (err != 0) && (err != EEXIST) ) {
-        fprintf(stderr, "mkpath_np fail: %d", err);
-        return 1;
-    }
-
-    if ( archStrs.empty() ) {
-        switch ( platform ) {
-            case dyld3::Platform::iOS:
-            case dyld3::Platform::tvOS:
-                archStrs.insert("arm64");
-                break;
-            case dyld3::Platform::watchOS:
-                archStrs.insert("armv7k");
-                archStrs.insert("arm64_32");
-                break;
-             case dyld3::Platform::unknown:
-             case dyld3::Platform::macOS:
-                assert(0 && "macOS not support with this tool");
-                break;
-        }
-    }
-
-    uint64_t t1 = mach_absolute_time();
-
-    // find all mach-o files for requested architectures
-    std::vector<MappedMachOsByCategory> allFileSets;
-    if ( archStrs.count("arm64") )
-        allFileSets.push_back({"arm64"});
-    if ( archStrs.count("arm64_32") )
-        allFileSets.push_back({"arm64_32"});
-    if ( archStrs.count("armv7k") )
-        allFileSets.push_back({"armv7k"});
-    std::vector<std::string> paths;
-    parsePathsFile(dylibsList, paths);
-    mapAllFiles(rootPath, paths, platform, allFileSets);
-
-    uint64_t t2 = mach_absolute_time();
-
-    fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
-
-    // build all caches in parallel
-    __block bool cacheBuildFailure = false;
-    dispatch_apply(allFileSets.size(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
-        const MappedMachOsByCategory& fileSet = allFileSets[index];
-        const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName;
-
-        fprintf(stderr, "make %s cache with %lu dylibs, %lu other dylibs, %lu programs\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size());
-
-        // build cache new cache file
-        DyldSharedCache::CreateOptions options;
-        options.archName                     = fileSet.archName;
-        options.platform                     = platform;
-        options.excludeLocalSymbols          = true;
-        options.optimizeStubs                = false;
-        options.optimizeObjC                 = true;
-        options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ?
-                                        DyldSharedCache::Agile : DyldSharedCache::SHA256only;
-        options.dylibsRemovedDuringMastering = true;
-        options.inodesAreSameAsRuntime       = false;
-        options.cacheSupportsASLR            = true;
-        options.forSimulator                 = false;
-        options.isLocallyBuiltCache          = true;
-        options.verbose                      = verbose;
-        options.evictLeafDylibsOnOverflow    = false;
-        DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
-
-        // print any warnings
-        for (const std::string& warn : results.warnings) {
-            fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str());
-        }
-        if ( !results.errorMessage.empty() ) {
-            // print error (if one)
-            fprintf(stderr, "update_dyld_sim_shared_cache: %s\n", results.errorMessage.c_str());
-            cacheBuildFailure = true;
-        }
-        else {
-            // save new cache file to disk and write new .map file
-            assert(results.cacheContent != nullptr);
-            if ( !safeSave(results.cacheContent, results.cacheLength, outFile) )
-                cacheBuildFailure = true;
-            if ( !cacheBuildFailure ) {
-                std::string mapStr = results.cacheContent->mapFile();
-                std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
-                safeSave(mapStr.c_str(), mapStr.size(), outFileMap);
-            }
-            // free created cache buffer
-            vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
-        }
-    });
-
-    // we could unmap all input files, but tool is about to quit
-
-    return (cacheBuildFailure ? 1 : 0);
-}
-
index 44533758d97a7cbf7947fe4fe43849549e4e5348..c01339324e1372dae2edfaa149d212bb094b7c1a 100644 (file)
@@ -23,7 +23,7 @@
  */
 
 #include "mrm_shared_cache_builder.h"
-#include "CacheBuilder.h"
+#include "SharedCacheBuilder.h"
 #include "ClosureFileSystem.h"
 #include "FileUtils.h"
 #include <pthread.h>
@@ -194,7 +194,7 @@ private:
 
 struct BuildInstance {
     std::unique_ptr<DyldSharedCache::CreateOptions> options;
-    std::unique_ptr<CacheBuilder>                   builder;
+    std::unique_ptr<SharedCacheBuilder>             builder;
     std::vector<CacheBuilder::InputFile>            inputFiles;
     std::vector<const char*>                        errors;
     std::vector<const char*>                        warnings;
@@ -214,8 +214,8 @@ struct BuildFileResult {
     uint64_t                                    size;
 };
 
-struct SharedCacheBuilder {
-    SharedCacheBuilder(const BuildOptions_v1* options);
+struct MRMSharedCacheBuilder {
+    MRMSharedCacheBuilder(const BuildOptions_v1* options);
     const BuildOptions_v1*          options;
     dyld3::closure::FileSystemMRM   fileSystem;
 
@@ -273,11 +273,11 @@ struct SharedCacheBuilder {
     }
 };
 
-SharedCacheBuilder::SharedCacheBuilder(const BuildOptions_v1* options) : options(options), lock(PTHREAD_MUTEX_INITIALIZER) {
+MRMSharedCacheBuilder::MRMSharedCacheBuilder(const BuildOptions_v1* options) : options(options), lock(PTHREAD_MUTEX_INITIALIZER) {
 
 }
 
-void validiateBuildOptions(const BuildOptions_v1* options, SharedCacheBuilder& builder) {
+void validiateBuildOptions(const BuildOptions_v1* options, MRMSharedCacheBuilder& builder) {
     if (options->version < kMinBuildVersion) {
         builder.error("Builder version %llu is less than minimum supported version of %llu", options->version, kMinBuildVersion);
     }
@@ -330,8 +330,8 @@ void getVersion(uint32_t *major, uint32_t *minor) {
     *minor = MinorVersion;
 }
 
-struct SharedCacheBuilder* createSharedCacheBuilder(const BuildOptions_v1* options) {
-    SharedCacheBuilder* builder = new SharedCacheBuilder(options);
+struct MRMSharedCacheBuilder* createSharedCacheBuilder(const BuildOptions_v1* options) {
+    MRMSharedCacheBuilder* builder = new MRMSharedCacheBuilder(options);
 
     // Check the option struct values are valid
     validiateBuildOptions(options, *builder);
@@ -339,10 +339,10 @@ struct SharedCacheBuilder* createSharedCacheBuilder(const BuildOptions_v1* optio
     return builder;
 }
 
-bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags) {
+bool addFile(struct MRMSharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags) {
     __block bool success = false;
     builder->runSync(^() {
-        if (builder->state != SharedCacheBuilder::AcceptingFiles) {
+        if (builder->state != MRMSharedCacheBuilder::AcceptingFiles) {
             builder->error("Cannot add file: '%s' as we have already started building", path);
             return;
         }
@@ -388,10 +388,10 @@ bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data
     return success;
 }
 
-bool addSymlink(struct SharedCacheBuilder* builder, const char* fromPath, const char* toPath) {
+bool addSymlink(struct MRMSharedCacheBuilder* builder, const char* fromPath, const char* toPath) {
     __block bool success = false;
     builder->runSync(^() {
-        if (builder->state != SharedCacheBuilder::AcceptingFiles) {
+        if (builder->state != MRMSharedCacheBuilder::AcceptingFiles) {
             builder->error("Cannot add file: '%s' as we have already started building", fromPath);
             return;
         }
@@ -481,14 +481,14 @@ static const char* dispositionName(Disposition disposition) {
     }
 }
 
-bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) {
+bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
     __block bool success = false;
     builder->runSync(^() {
-        if (builder->state != SharedCacheBuilder::AcceptingFiles) {
+        if (builder->state != MRMSharedCacheBuilder::AcceptingFiles) {
             builder->error("Builder has already been run");
             return;
         }
-        builder->state = SharedCacheBuilder::Building;
+        builder->state = MRMSharedCacheBuilder::Building;
         if (builder->fileSystem.fileCount() == 0) {
             builder->error("Cannot run builder with no files");
         }
@@ -552,7 +552,7 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) {
                 options->dylibOrdering = parseOrderFile(builder->dylibOrderFileData);
                 options->dirtyDataSegmentOrdering = parseOrderFile(builder->dirtyDataOrderFileData);
 
-                auto cacheBuilder = std::make_unique<CacheBuilder>(*options.get(), builder->fileSystem);
+                auto cacheBuilder = std::make_unique<SharedCacheBuilder>(*options.get(), builder->fileSystem);
                 builder->builders.emplace_back((BuildInstance) { std::move(options), std::move(cacheBuilder), inputFiles });
             }
         };
@@ -574,7 +574,7 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) {
 
         // FIXME: This step can run in parallel.
         for (auto& buildInstance : builder->builders) {
-            CacheBuilder* cacheBuilder = buildInstance.builder.get();
+            SharedCacheBuilder* cacheBuilder = buildInstance.builder.get();
             cacheBuilder->build(buildInstance.inputFiles, aliases);
 
             // First put the warnings in to a vector to own them.
@@ -619,7 +619,7 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) {
         // 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) {
-            CacheBuilder* cacheBuilder = buildInstance.builder.get();
+            SharedCacheBuilder* cacheBuilder = buildInstance.builder.get();
 
             CacheResult cacheBuildResult;
             cacheBuildResult.version                = 1;
@@ -676,43 +676,43 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) {
                 return;
         }
 
-        builder->state = SharedCacheBuilder::FinishedBuilding;
+        builder->state = MRMSharedCacheBuilder::FinishedBuilding;
         success = true;
     });
     return success;
 }
 
-const char* const* getErrors(const struct SharedCacheBuilder* builder, uint64_t* errorCount) {
+const char* const* getErrors(const struct MRMSharedCacheBuilder* builder, uint64_t* errorCount) {
     if (builder->errors.empty())
         return nullptr;
     *errorCount = builder->errors.size();
     return builder->errors.data();
 }
 
-const struct FileResult* const* getFileResults(struct SharedCacheBuilder* builder, uint64_t* resultCount) {
+const struct FileResult* const* getFileResults(struct MRMSharedCacheBuilder* builder, uint64_t* resultCount) {
     if (builder->fileResults.empty())
         return nullptr;
     *resultCount = builder->fileResults.size();
     return builder->fileResults.data();
 }
 
-const struct CacheResult* const* getCacheResults(struct SharedCacheBuilder* builder, uint64_t* resultCount) {
+const struct CacheResult* const* getCacheResults(struct MRMSharedCacheBuilder* builder, uint64_t* resultCount) {
     if (builder->cacheResults.empty())
         return nullptr;
     *resultCount = builder->cacheResults.size();
     return builder->cacheResults.data();
 }
 
-const char* const* getFilesToRemove(const struct SharedCacheBuilder* builder, uint64_t* fileCount) {
+const char* const* getFilesToRemove(const struct MRMSharedCacheBuilder* builder, uint64_t* fileCount) {
     if (builder->filesToRemove.empty())
         return nullptr;
     *fileCount = builder->filesToRemove.size();
     return builder->filesToRemove.data();
 }
 
-void destroySharedCacheBuilder(struct SharedCacheBuilder* builder) {
+void destroySharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
     for (auto& buildInstance : builder->builders) {
-        CacheBuilder* cacheBuilder = buildInstance.builder.get();
+        SharedCacheBuilder* cacheBuilder = buildInstance.builder.get();
         cacheBuilder->deleteBuffer();
     }
     for (auto &fileResult : builder->fileResultStorage) {
index 33c4a69cbd80999db54819441f6b0eb1efe17c64..983b1ca058e17ec6d9be9efd55f7ce4f4cda3b18 100644 (file)
@@ -115,38 +115,38 @@ struct CacheResult
     const char*                                 mapJSON;
 };
 
-struct SharedCacheBuilder;
+struct MRMSharedCacheBuilder;
 
 __API_AVAILABLE(macos(10.12))
 void getVersion(uint32_t *major, uint32_t *minor);
 
 __API_AVAILABLE(macos(10.12))
-struct SharedCacheBuilder* createSharedCacheBuilder(const struct BuildOptions_v1* options);
+struct MRMSharedCacheBuilder* createSharedCacheBuilder(const struct BuildOptions_v1* options);
 
 // Add a file.  Returns true on success.
 __API_AVAILABLE(macos(10.12))
-bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, enum FileFlags fileFlags);
+bool addFile(struct MRMSharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, enum FileFlags fileFlags);
 
 __API_AVAILABLE(macos(10.12))
-bool addSymlink(struct SharedCacheBuilder* builder, const char* fromPath, const char* toPath);
+bool addSymlink(struct MRMSharedCacheBuilder* builder, const char* fromPath, const char* toPath);
 
 __API_AVAILABLE(macos(10.12))
-bool runSharedCacheBuilder(struct SharedCacheBuilder* builder);
+bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder);
 
 __API_AVAILABLE(macos(10.12))
-const char* const* getErrors(const struct SharedCacheBuilder* builder, uint64_t* errorCount);
+const char* const* getErrors(const struct MRMSharedCacheBuilder* builder, uint64_t* errorCount);
 
 __API_AVAILABLE(macos(10.12))
-const struct FileResult* const* getFileResults(struct SharedCacheBuilder* builder, uint64_t* resultCount);
+const struct FileResult* const* getFileResults(struct MRMSharedCacheBuilder* builder, uint64_t* resultCount);
 
 __API_AVAILABLE(macos(10.12))
-const struct CacheResult* const* getCacheResults(struct SharedCacheBuilder* builder, uint64_t* resultCount);
+const struct CacheResult* const* getCacheResults(struct MRMSharedCacheBuilder* builder, uint64_t* resultCount);
 
 __API_AVAILABLE(macos(10.12))
-const char* const* getFilesToRemove(const struct SharedCacheBuilder* builder, uint64_t* fileCount);
+const char* const* getFilesToRemove(const struct MRMSharedCacheBuilder* builder, uint64_t* fileCount);
 
 __API_AVAILABLE(macos(10.12))
-void destroySharedCacheBuilder(struct SharedCacheBuilder* builder);
+void destroySharedCacheBuilder(struct MRMSharedCacheBuilder* builder);
 
 #ifdef __cplusplus
 }
diff --git a/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm b/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm
deleted file mode 100644 (file)
index 49e58a2..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
- *
- * Copyright (c) 2016 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <Bom/Bom.h>
-#include <dispatch/dispatch.h>
-#include <copyfile.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <fcntl.h>
-#include <dlfcn.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/resource.h>
-#include <dirent.h>
-#include <libgen.h>
-#include <pthread.h>
-
-#include <vector>
-#include <array>
-#include <set>
-#include <map>
-#include <algorithm>
-
-#include "Manifest.h"
-#include "FileUtils.h"
-#include "BuilderUtils.h"
-
-#define CACHE_BUILDER_COPY_FILE_MODE COPYFILE_ALL
-
-#if !__has_feature(objc_arc)
-#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks
-#endif
-
-static dispatch_queue_t build_queue = dispatch_queue_create("com.apple.dyld.cache-builder.build", DISPATCH_QUEUE_CONCURRENT);
-
-#define kDylibCachePrefix "/AppleInternal/Developer/DylibCaches/"
-
-void createArtifact(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& dylibCachePath, bool includeExecutables)
-{
-    auto copy_state = copyfile_state_alloc();
-    mkpath_np((dylibCachePath + "/Metadata").c_str(), 0755);
-    (void)copyfile(manifest.metabomFile().c_str(), (dylibCachePath + "/Metadata/metabom.bom").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
-
-    if (!manifest.dylibOrderFile().empty()) {
-        (void)copyfile(realPath(manifest.dylibOrderFile()).c_str(), (dylibCachePath + "/Metadata/dylibOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
-    }
-
-    if (!manifest.dirtyDataOrderFile().empty()) {
-        (void)copyfile(realPath(manifest.dirtyDataOrderFile()).c_str(), (dylibCachePath + "/Metadata/dirtyDataOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
-    }
-
-    std::set<dyld3::UUID> uuids;
-    std::map<std::string, std::string> copy_pairs;
-
-    manifest.forEachConfiguration([&manifest, &uuids](const std::string& config) {
-        manifest.configuration(config).forEachArchitecture([&manifest, &config, &uuids](const std::string& arch) {
-            auto results = manifest.configuration(config).architecture(arch).results;
-            for (const auto& image : results.dylibs) {
-                uuids.insert(image.first);
-            }
-            for (const auto& image : results.bundles) {
-                uuids.insert(image.first);
-            }
-            for (const auto& image : results.executables) {
-                uuids.insert(image.first);
-            }
-        });
-    });
-
-    for (auto& uuid : uuids) {
-        auto buildPath = manifest.buildPathForUUID(uuid);
-        auto installPath = manifest.runtimePathForUUID(uuid);
-        assert(!buildPath.empty() && !installPath.empty());
-        copy_pairs.insert(std::make_pair(installPath, buildPath));
-    }
-
-    for (const auto& copy_pair : copy_pairs) {
-        std::string from = realPath(copy_pair.second);
-        std::string to = dylibCachePath + "/Root/" + copy_pair.first;
-        mkpath_np(dirPath(to).c_str(), 0755);
-        int err = copyfile(from.c_str(), to.c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
-        diags.verbose("COPYING (%d) %s -> %s\n", err, from.c_str(), to.c_str());
-
-    }
-    copyfile_state_free(copy_state);
-
-    fprintf(stderr, "[Artifact] dylibs copied\n");
-}
-
-void addArtifactPaths(Diagnostics& diags, dyld3::Manifest& manifest)
-{
-    manifest.setDylibOrderFile("./Metadata/dylibOrderFile.txt");
-    manifest.setDirtyDataOrderFile("./Metadata/dirtyDataOrderFile.txt");
-    manifest.setMetabomFile("./Metadata/metabom.bom");
-
-    for (auto& projects : manifest.projects()) {
-        manifest.addProjectSource(projects.first, "./Root", true);
-    }
-}
-
-int main(int argc, const char* argv[])
-{
-    @autoreleasepool {
-        __block Diagnostics diags;
-        bool                verbose = false;
-        std::string         masterDstRoot;
-        std::string         dylibCacheDir;
-        std::string         resultPath;
-        std::string         manifestPath;
-        bool                preflight = false;
-        __block bool        allBuildsSucceeded = true;
-        bool                skipWrites = false;
-        bool                skipBuilds = false;
-        bool                agileChooseSHA256CdHash = false;
-        time_t              mytime = time(0);
-        fprintf(stderr, "Started: %s", asctime(localtime(&mytime)));
-
-        // parse command line options
-        for (int i = 1; i < argc; ++i) {
-            const char* arg = argv[i];
-            if (arg[0] == '-') {
-                if (strcmp(arg, "-debug") == 0) {
-                    verbose = true;
-                    diags = Diagnostics(true);
-                } else if (strcmp(arg, "-skip_writes") == 0) {
-                    skipWrites = true;
-                } else if (strcmp(arg, "-skip_builds") == 0) {
-                    skipBuilds = true;
-                } else if (strcmp(arg, "-delete_writes") == 0) {
-                    skipWrites = true;
-                } else if (strcmp(arg, "-dylib_cache") == 0) {
-                    dylibCacheDir = argv[++i];
-                } else if (strcmp(arg, "-preflight") == 0) {
-                    preflight = true;
-                    skipWrites = true;
-                } else if (strcmp(arg, "-master_dst_root") == 0) {
-                    masterDstRoot = argv[++i];
-                    if (masterDstRoot.empty()) {
-                        diags.error("-master_dst_root missing path argument");
-                    }
-                } else if (strcmp(arg, "-results") == 0) {
-                    resultPath = argv[++i];
-                } else if (strcmp(arg, "-plist") == 0) {
-                    manifestPath = argv[++i];
-                } else if (strcmp(arg, "-agile_choose_sha256_cdhash") == 0) {
-                    agileChooseSHA256CdHash = true;
-                } else {
-                    // usage();
-                    diags.error("unknown option: %s", arg);
-                }
-            } else {
-                manifestPath = argv[i];
-            }
-        }
-
-        if (getenv("LGG_SKIP_CACHE_FUN") != nullptr) {
-            skipBuilds = true;
-        }
-
-        if (diags.hasError()) {
-            printf("%s\n", diags.errorMessage().c_str());
-            exit(-1);
-        }
-
-        dispatch_async(dispatch_get_main_queue(), ^{
-            if (manifestPath.empty()) {
-                fprintf(stderr, "mainfest path argument is required\n");
-                exit(-1);
-            }
-
-            (void)chdir(dirname(strdup(manifestPath.c_str())));
-            __block auto manifest = dyld3::Manifest(diags, manifestPath);
-
-            if (manifest.build().empty()) {
-                fprintf(stderr, "No version found in manifest\n");
-                exit(-1);
-            }
-
-            fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str());
-
-            if (masterDstRoot.empty()) {
-                fprintf(stderr, "-master_dst_root required path argument\n");
-                exit(-1);
-            }
-
-            if (manifest.version() < 4) {
-                fprintf(stderr, "must specify valid manifest file\n");
-                exit(-1);
-            }
-
-            struct rlimit rl = { OPEN_MAX, OPEN_MAX };
-            (void)setrlimit(RLIMIT_NOFILE, &rl);
-
-            manifest.calculateClosure();
-
-            if (!skipWrites && !skipBuilds) {
-                (void)mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755);
-                dispatch_group_async(buildGroup(), build_queue, ^{
-                    createArtifact(diags, manifest, masterDstRoot + "/Artifact.dlc/", true);
-                });
-            }
-
-            if (!dylibCacheDir.empty()) {
-                dispatch_group_async(buildGroup(), build_queue, ^{
-                    createArtifact(diags, manifest, dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/", false);
-                });
-            }
-
-            if (!skipBuilds) {
-                dispatch_group_async(buildGroup(), build_queue, ^{
-                    makeBoms(manifest, masterDstRoot);
-                });
-                allBuildsSucceeded = build(diags, manifest, masterDstRoot, true, verbose, skipWrites,
-                                           agileChooseSHA256CdHash, true, false);
-            }
-
-            manifest.write(resultPath);
-
-            addArtifactPaths(diags, manifest);
-            if (!dylibCacheDir.empty()) {
-                manifest.write(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.plist");
-                manifest.writeJSON(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.json");
-            }
-
-            if (!skipWrites) {
-                mkpath_np((masterDstRoot + "/Artifact.dlc").c_str(), 0755);
-                auto copy_state = copyfile_state_alloc();
-                (void)copyfile(realPath(manifestPath).c_str(), (masterDstRoot + "/Artifact.dlc/BNIManifest.plist").c_str(), copy_state, COPYFILE_ALL);
-                copyfile_state_free(copy_state);
-                manifest.write(masterDstRoot + "/Artifact.dlc/Manifest.plist");
-                manifest.writeJSON(masterDstRoot + "/Artifact.dlc/Manifest.json");
-            }
-
-            dispatch_group_wait(buildGroup(), DISPATCH_TIME_FOREVER);
-            time_t mytime = time(0);
-            fprintf(stderr, "Finished: %s", asctime(localtime(&mytime)));
-
-            if (preflight && !allBuildsSucceeded) {
-                exit(-1);
-            }
-
-            exit(0);
-        });
-    }
-    dispatch_main();
-    return 0;
-}
index 907a0e7d1268c2058df2e26e6c67493006d3f1f9..2aba7bf6ab8aa293e77e491bca317a4dfeb984c1 100644 (file)
@@ -23,7 +23,7 @@
  */
 
 #ifndef __MACH_O_FIXUP_CHAINS__
-#define __MACH_O_FIXUP_CHAINS__
+#define __MACH_O_FIXUP_CHAINS__ 4
 
 
 #include <stdint.h>
@@ -88,20 +88,26 @@ struct dyld_chained_starts_offsets
 
 // values for dyld_chained_starts_in_segment.pointer_format
 enum {
-    DYLD_CHAINED_PTR_ARM64E      = 1,
-    DYLD_CHAINED_PTR_64          = 2,
-    DYLD_CHAINED_PTR_32          = 3,
-    DYLD_CHAINED_PTR_32_CACHE    = 4,
-    DYLD_CHAINED_PTR_32_FIRMWARE = 5,
+    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
 struct dyld_chained_ptr_arm64e_rebase
 {
-    uint64_t    target   : 43,    // vmaddr
+    uint64_t    target   : 43,
                 high8    :  8,
-                next     : 11,    // 8-byte stide
+                next     : 11,    // 4 or 8-byte stide
                 bind     :  1,    // == 0
                 auth     :  1;    // == 0
 };
@@ -111,8 +117,8 @@ struct dyld_chained_ptr_arm64e_bind
 {
     uint64_t    ordinal   : 16,
                 zero      : 16,
-                addend    : 19,
-                next      : 11,    // 8-byte stide
+                addend    : 19,    // +/-256K
+                next      : 11,    // 4 or 8-byte stide
                 bind      :  1,    // == 1
                 auth      :  1;    // == 0
 };
@@ -124,7 +130,7 @@ struct dyld_chained_ptr_arm64e_auth_rebase
                 diversity : 16,
                 addrDiv   :  1,
                 key       :  2,
-                next      : 11,    // 8-byte stide
+                next      : 11,    // 4 or 8-byte stide
                 bind      :  1,    // == 0
                 auth      :  1;    // == 1
 };
@@ -137,16 +143,16 @@ struct dyld_chained_ptr_arm64e_auth_bind
                 diversity : 16,
                 addrDiv   :  1,
                 key       :  2,
-                next      : 11,    // 8-byte stide
+                next      : 11,    // 4 or 8-byte stide
                 bind      :  1,    // == 1
                 auth      :  1;    // == 1
 };
 
-// DYLD_CHAINED_PTR_64
+// DYLD_CHAINED_PTR_64/DYLD_CHAINED_PTR_64_OFFSET
 struct dyld_chained_ptr_64_rebase
 {
-    uint64_t    target    : 36,    // vmaddr, 64GB max image size
-                high8     :  8,    // top 8 bits set to this after slide added
+    uint64_t    target    : 36,    // 64GB max image size (DYLD_CHAINED_PTR_64 => vmAddr, DYLD_CHAINED_PTR_64_OFFSET => runtimeOffset)
+                high8     :  8,    // top 8 bits set to this (DYLD_CHAINED_PTR_64 => after slide added, DYLD_CHAINED_PTR_64_OFFSET => before slide added)
                 reserved  :  7,    // all zeros
                 next      : 12,    // 4-byte stride
                 bind      :  1;    // == 0
@@ -162,6 +168,18 @@ struct dyld_chained_ptr_64_bind
                 bind      :  1;   // == 1
 };
 
+// DYLD_CHAINED_PTR_64_KERNEL_CACHE
+struct dyld_chained_ptr_64_kernel_cache_rebase
+{
+    uint64_t    target     : 30,   // basePointers[cacheLevel] + target
+                cacheLevel :  2,   // what level of cache to bind to (indexes a mach_header array)
+                diversity  : 16,
+                addrDiv    :  1,
+                key        :  2,
+                next       : 12,    // 4-byte stide
+                isAuth     :  1;    // 0 -> not authenticated.  1 -> authenticated
+};
+
 // DYLD_CHAINED_PTR_32
 // Note: for DYLD_CHAINED_PTR_32 some non-pointer values are co-opted into the chain
 // as out of range rebases.  If an entry in the chain is > max_valid_pointer, then it
index 6c95c13da2a716db1721c83610f52377e66b3372..716895168a049a07dbc18181f7dee4b16ed86434 100644 (file)
@@ -780,8 +780,12 @@ public:
             }
         }
 
-               if (strcmp(segname, "__DATA") == 0)
-                       return getSection("__DATA_CONST", sectname);
+        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;
     }
 
index 5f817ad7015b8c453bc053ffe8e3fce3d5da9a22..a4a81b2aa1e5bd25e21f352a921e03f6335b378e 100644 (file)
 #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"
@@ -56,6 +60,9 @@
 #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)
 {
@@ -117,6 +124,8 @@ enum Mode {
     modeInfo,
     modeSize,
     modeObjCProtocols,
+    modeObjCClasses,
+    modeObjCSelectors,
     modeExtract
 };
 
@@ -248,6 +257,14 @@ int main (int argc, const char* argv[]) {
                 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;
@@ -320,6 +337,10 @@ int main (int argc, const char* argv[]) {
             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 ) {
@@ -859,6 +880,464 @@ int main (int argc, const char* argv[]) {
             }
         }
     }
+    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;
@@ -1102,6 +1581,8 @@ int main (int argc, const char* argv[]) {
             case modeSectionSizes:
             case modeStrings:
             case modeObjCProtocols:
+            case modeObjCClasses:
+            case modeObjCSelectors:
             case modeExtract:
                 break;
         }
diff --git a/local_test_runner/ContainerizedTestRunner.mm b/local_test_runner/ContainerizedTestRunner.mm
new file mode 100644 (file)
index 0000000..14c7533
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+* 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@
+*/
+
+#import <XCTest/XCTest.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <objc/runtime.h>
+
+#include "test_support.h"
+
+struct TestInfo
+{
+    const char*         testName;
+    const char*         runLine;
+};
+
+#include "XCTestGenerated.h"
+
+@interface ContainerizedTestRunner : XCTestCase
+- (void)launchTest:(const char *)test withRunLine:(const char *)runLine;
+@end
+
+@implementation ContainerizedTestRunner
+#if 1
++ (void)registerTest:(const TestInfo &)info withEnv:(const char *)env andExtensions:(const char *)extension {
+    char *fullRunline = NULL;
+    asprintf(&fullRunline, "TMPDIR=/tmp/ TEST_OUTPUT=XCTest  %s %s", env, info.runLine);
+    unsigned char hash[CC_SHA1_DIGEST_LENGTH];
+    char hashStr[CC_SHA1_DIGEST_LENGTH*2+1];
+    CC_SHA1(fullRunline, (CC_LONG)strlen(fullRunline), &hash[0]);
+    snprintf(&hashStr[0], CC_SHA1_DIGEST_LENGTH*2+1, "%x%x%x%x", hash[0], hash[1], hash[2], hash[3]);
+    char buffer[4096];
+    snprintf(&buffer[0], 4096, "test_%s_%s_%s", info.testName, extension, hashStr);
+    SEL newSel = sel_registerName(buffer);
+    IMP newIMP = imp_implementationWithBlock(^(id self) {
+        [self launchTest:info.testName withRunLine:fullRunline];
+    });
+    class_addMethod([self class], newSel, newIMP, "v@:");
+}
+
++ (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 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"];
+#endif
+    };
+}
+
+#else
+// This would be the way to do it if XCTest did not insist on using the selector name for differentiating results in the Xcode UI
++ (NSArray<NSInvocation *> *)testInvocations {
+    static NSArray<NSInvocation *> *invocations = nil;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        NSMutableArray<NSInvocation *> *invocationArray = [[NSMutableArray alloc] init];
+        for (const TestInfo& info : sTests) {
+            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[ContainerizedTestRunner class]
+                                                instanceMethodSignatureForSelector:@selector(launchTest:withRunLine:)]];
+            invocation.selector = @selector(launchTest:withRunLine:);
+            [invocation setArgument:(void*)&info.testName atIndex:2];
+            [invocation setArgument:(void*)&info.runLine atIndex:3];
+            [invocationArray addObject:invocation];
+        }
+        invocations = [NSArray arrayWithArray:invocationArray];
+    });
+    return [invocations copy];
+}
+
+- (NSString *) name {
+    const char *testName = NULL;
+    const char *runLine = NULL;
+    [self.invocation getArgument:(void*)&testName atIndex:2];
+    [self.invocation getArgument:(void*)&runLine atIndex:3];
+    return [NSString stringWithFormat:@"%s: %s", testName, runLine];
+}
+
+- (NSString *)nameForLegacyLogging
+{
+    return self.name;
+}
+#endif
+
+- (void)setUp {
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        _process process;
+        fprintf(stderr, "CHROOT: %s\n", CHROOT_PATH);
+        process.set_executable_path("/usr/sbin/chroot");
+        const char *args[] = {CHROOT_PATH, "/bin/sh", "-c", "/sbin/mount -t devfs devfs /dev", NULL};
+        process.set_args(args);
+        process.launch();
+    });
+}
+
+- (void)tearDown {
+    // Put teardown code here. This method is called after the invocation of each test method in the class.
+}
+
+- (void) executeCommandInContainer:(const char *)command {
+    _process process;
+    process.set_executable_path("/usr/sbin/chroot");
+    const char *args[] = {CHROOT_PATH, "/bin/sh", "-c", command, NULL};
+    process.set_args(args);
+    __block dispatch_data_t output = NULL;
+    process.set_stdout_handler(^(int fd) {
+        ssize_t size = 0;
+        do {
+            char buffer[16384];
+            size = read(fd, &buffer[0], 16384);
+            if (size == -1) { break; }
+            dispatch_data_t data = dispatch_data_create(&buffer[0], size, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
+            if (!output) {
+                output = data;
+            } else {
+                output = dispatch_data_create_concat(output, data);
+            }
+        } while (size > 0);
+    });
+    process.set_exit_handler(^(pid_t pid) {
+        int status = 0;
+        (void)waitpid(pid, &status, 0);
+
+        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];
+            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];
+            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];
+            return;
+        }
+        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];
+            return;
+        }
+    });
+    process.launch();
+}
+
+
+- (void) launchTest:(const char *)test withRunLine:(const char *)runLine {
+    char command[4096];
+    snprintf(&command[0], 4096, "cd /AppleInternal/CoreOS/tests/dyld/%s; %s", test, runLine);
+    [self executeCommandInContainer:command];
+// sudo chroot . /bin/sh -c 'TEST_OUTPUT=BATS /AppleInternal/CoreOS/tests/dyld/dyld_get_sdk_version/sdk-check.exe'
+
+}
+
+//
+//- (void)testExample {
+//    // This is an example of a functional test case.
+//    // Use XCTAssert and related functions to verify your tests produce the correct results.
+//}
+//
+//- (void)testPerformanceExample {
+//    // This is an example of a performance test case.
+//    [self measureBlock:^{
+//        // Put the code you want to measure the time of here.
+//    }];
+//}
+
+@end
diff --git a/local_test_runner/Info.plist b/local_test_runner/Info.plist
new file mode 100644 (file)
index 0000000..64d65ca
--- /dev/null
@@ -0,0 +1,22 @@
+<?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>CFBundleDevelopmentRegion</key>
+       <string>$(DEVELOPMENT_LANGUAGE)</string>
+       <key>CFBundleExecutable</key>
+       <string>$(EXECUTABLE_NAME)</string>
+       <key>CFBundleIdentifier</key>
+       <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>$(PRODUCT_NAME)</string>
+       <key>CFBundlePackageType</key>
+       <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleVersion</key>
+       <string>1</string>
+</dict>
+</plist>
index 839ab99436e00dac5229c2dd95ee8feda5b7f07b..dbadce357c1cd5f769287033ca56db79904042df 100644 (file)
@@ -402,10 +402,8 @@ uintptr_t ImageLoader::interposedAddress(const LinkContext& context, uintptr_t a
 void ImageLoader::applyInterposingToDyldCache(const LinkContext& context) {
        if (!context.dyldCache)
                return;
-#if !__arm64e__ // until arm64e cache builder sets builtFromChainedFixups
        if (!context.dyldCache->header.builtFromChainedFixups)
                return;
-#endif
        if (fgInterposingTuples.empty())
                return;
        // For each of the interposed addresses, see if any of them are in the shared cache.  If so, find
index df2f44b0640aa364d743018088593af3d57982d8..70107dca44351ab1ba7534b7373242c11fe15dcb 100644 (file)
@@ -82,7 +82,7 @@
        #define SUPPORT_CLASSIC_MACHO                   __arm__
        #define SUPPORT_ZERO_COST_EXCEPTIONS    (!__USING_SJLJ_EXCEPTIONS__)
        #define INITIAL_IMAGE_COUNT                             150
-       #define SUPPORT_ACCELERATE_TABLES               !TARGET_OS_SIMULATOR
+       #define SUPPORT_ACCELERATE_TABLES               0
        #define SUPPORT_ROOT_PATH                               TARGET_OS_SIMULATOR
 #else
        #define SPLIT_SEG_SHARED_REGION_SUPPORT 0
index 7067ed82b4c7c8eeb665f61b6e0444507d042dd6..1768df8a738fa866fdeb9028f5c6d83b97329fc7 100644 (file)
@@ -556,6 +556,10 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat
 
        if ( needsAddedLibSystemDepency(*libCount, mh) )
                *libCount = 1;
+
+       // dylibs that use LC_DYLD_CHAINED_FIXUPS have that load command removed when put in the dyld cache
+       if ( !*compressed && (mh->flags & MH_DYLIB_IN_CACHE) )
+               *compressed = true;
 }
 
 
@@ -2343,8 +2347,8 @@ void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
                                                        dyld::throwf("__init_offsets section is not in read-only segment %s\n", this->getPath());
                                                for (size_t j=0; j < count; ++j) {
                                                        uint32_t funcOffset = inits[j];
-                                                       // verify initializers are in TEXT segment
-                                                       if ( funcOffset > seg->filesize ) {
+                                                       // verify initializers are in image
+                                                       if ( ! this->containsAddress((uint8_t*)this->machHeader() + funcOffset) ) {
                                                                dyld::throwf("initializer function offset 0x%08X not in mapped image for %s\n", funcOffset, this->getPath());
                                                        }
                                                        if ( ! dyld::gProcessInfo->libSystemInitialized ) {
@@ -2356,6 +2360,9 @@ void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
                             Initializer func = (Initializer)((uint8_t*)this->machHeader() + funcOffset);
                                                        if ( context.verboseInit )
                                                                dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
+#if __has_feature(ptrauth_calls)
+                                                       func = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
+#endif
                                                        bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
                                                        {
                                                                dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
index 2b85fb8c8e0f69ce638cf077068411a267210059..1e01ed17cce5ce599c3f3ebe3b45715cb7548da5 100644 (file)
@@ -885,7 +885,7 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa
        if ( this->usablePrebinding(context) ) {
                // don't need to bind
                // except weak which may now be inline with the regular binds
-               if (this->participatesInCoalescing()) {
+               if ( this->participatesInCoalescing() && (fDyldInfo != nullptr) ) {
                        // run through all binding opcodes
                        eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
                                                                uintptr_t addr, uint8_t type, const char* symbolName,
@@ -911,7 +911,7 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa
                        const dyld_chained_fixups_header* fixupsHeader = (dyld_chained_fixups_header*)(fLinkEditBase + fChainedFixups->dataoff);
                        doApplyFixups(context, fixupsHeader);
                }
-               else {
+               else if ( fDyldInfo != nullptr ) {
                #if TEXT_RELOC_SUPPORT
                        // if there are __TEXT fixups, temporarily make __TEXT writable
                        if ( fTextSegmentBinds )
@@ -993,14 +993,22 @@ void ImageLoaderMachOCompressed::doApplyFixups(const LinkContext& context, const
        // build table of resolved targets for each symbol ordinal
        STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const void*, targetAddrs, 128);
        targetAddrs.reserve(fixupsHeader->imports_count);
-       Diagnostics diag;
+       __block Diagnostics diag;
        const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)ml;
        ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
                const ImageLoader*      targetImage;
                uint8_t symbolFlags = weakImport ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0;
-               uintptr_t symbolAddress = this->resolve(context, symbolName, symbolFlags, libOrdinal, &targetImage, NULL, true);
-               targetAddrs.push_back((void*)(symbolAddress + addend));
+               try {
+                       uintptr_t symbolAddress = this->resolve(context, symbolName, symbolFlags, libOrdinal, &targetImage, NULL, true);
+                       targetAddrs.push_back((void*)(symbolAddress + addend));
+               }
+               catch (const char* msg) {
+                       stop = true;
+                       diag.error("%s", msg);
+               }
        });
+       if ( diag.hasError() )
+               throw strdup(diag.errorMessage());
 
        auto logFixups = ^(void* loc, void* newValue) {
                dyld::log("dyld: fixup: %s:%p = %p\n", this->getShortName(), loc, newValue);
@@ -1009,6 +1017,8 @@ void ImageLoaderMachOCompressed::doApplyFixups(const LinkContext& context, const
                logFixups = nullptr;
 
        ml->fixupAllChainedFixups(diag, starts, fSlide, targetAddrs, logFixups);
+       if ( diag.hasError() )
+               throw strdup(diag.errorMessage());
 }
 
 void ImageLoaderMachOCompressed::registerInterposing(const LinkContext& context)
@@ -1838,7 +1848,7 @@ void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context)
                dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath());
 
        const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)fMachOData;
-       if ( !ma->hasChainedFixups() ) {
+       if ( !ma->hasChainedFixups() && (fDyldInfo != nullptr) ) {
                // Note: all binds that happen as part of normal loading and fixups will have interposing applied.
                // There is only two cases where we need to parse bind opcodes and apply interposing:
 
index 40fbd5b27780a1c2dbc914dfa32826a306a684aa..18aec931c680e52d1f0fd094ca9f0df2cdd56cd0 100644 (file)
@@ -3373,20 +3373,37 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
                if ( sAllCacheImagesProxy->hasDylib(path, &cacheIndex) )
                        return sAllCacheImagesProxy;
        }
-#endif
-#if TARGET_OS_SIMULATOR
-       // in simulators, 'path' has DYLD_ROOT_PATH prepended, but cache index does not have the prefix, so use orgPath
-       const char* pathToFindInCache = orgPath;
-#else
-       const char* pathToFindInCache = path;
 #endif
        uint statErrNo;
        struct stat statBuf;
        bool didStat = false;
        bool existsOnDisk;
-       dyld3::SharedCacheFindDylibResults shareCacheResults;
+       __block dyld3::SharedCacheFindDylibResults shareCacheResults;
        shareCacheResults.image = nullptr;
-       if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, pathToFindInCache, &shareCacheResults) ) {
+
+#if TARGET_OS_SIMULATOR
+
+       auto findSharedCacheImage = ^() {
+               // in simulators, 'path' has DYLD_ROOT_PATH prepended, but cache index does not have the prefix, so use orgPath
+               return dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, orgPath, &shareCacheResults);
+       };
+
+#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);
+       };
+
+#endif
+
+       if ( findSharedCacheImage() ) {
                // see if this image in the cache was already loaded via a different path
                for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) {
                        ImageLoader* anImage = *it;
@@ -5210,14 +5227,11 @@ void notifyKernelAboutImage(const struct macho_header* mh, const char* fileInfo)
 }
 
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
-typedef int (*open_proc_t)(const char*, int, int);
-typedef int (*fcntl_proc_t)(int, int, void*);
-typedef int (*ioctl_proc_t)(int, unsigned long, void*);
 static void* getProcessInfo() { return dyld::gProcessInfo; }
 static const SyscallHelpers sSysCalls = {
                12,
                // added in version 1
-               (open_proc_t)&open, 
+               &open,
                &close, 
                &pread, 
                &write, 
@@ -5225,8 +5239,8 @@ static const SyscallHelpers sSysCalls = {
                &munmap, 
                &madvise,
                &stat, 
-               (fcntl_proc_t)&fcntl, 
-               (ioctl_proc_t)&ioctl, 
+               &fcntl,
+               &ioctl, 
                &issetugid, 
                &getcwd, 
                &realpath, 
@@ -5904,6 +5918,21 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
 
        // send info on all images to libdyld.dylb
        libDyldEntry->setVars(mainExecutableMH, argc, argv, envp, apple);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+       uint32_t progVarsOffset;
+       if ( mainClosure->hasProgramVars(progVarsOffset) ) {
+               if ( libDyldEntry->vectorVersion >= 8 ) {
+                       // main executable contains globals to hold argc, argv, envp, and progname, but they need to be filled in
+                       ProgramVars* vars = (ProgramVars*)((uint8_t*)mainExecutableMH + progVarsOffset);
+                       *vars->NXArgcPtr        = argc;
+                       *vars->NXArgvPtr        = argv;
+                       *vars->environPtr       = envp;
+                       *vars->__prognamePtr = (argv[0] != NULL) ? basename(argv[0]) : "";
+                       // set up so libSystem gets ProgramVars struct embedded in main executable
+                       libDyldEntry->setProgramVars(vars);
+               }
+       }
+#endif
        if ( libDyldEntry->vectorVersion > 4 )
                libDyldEntry->setRestrictions(gLinkContext.allowAtPaths, gLinkContext.allowEnvVarsPath, gLinkContext.allowClassicFallbackPaths);
        libDyldEntry->setHaltFunction(&halt);
@@ -6466,6 +6495,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                                }
                        }
                        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);
index c8ea4dfadaddb1b7f54e7052693a5777cf4d1154..ad689a2f00d70abcee5c37e52d3bd39180642aef 100644 (file)
 #ifndef __DYLD_SYSCALL_HELPERS__
 #define __DYLD_SYSCALL_HELPERS__
 
+#include <sys/cdefs.h>
+
 #include <dirent.h>
+#if __has_include(<libamfi.h>)
+#include <libamfi.h>
+#else
+__BEGIN_DECLS
+extern int amfi_check_dyld_policy_self(uint64_t input_flags, uint64_t* output_flags);
+__END_DECLS
+#endif
+#include <libkern/OSAtomic.h>
+#include <libproc.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
 #include <mach-o/loader.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/kdebug_private.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <System/sys/reason.h>
+
+#define DYLD_SYSCALL_VTABLE_ENTRY(x) __typeof__ (x) *x
 
 #if __cplusplus
 namespace dyld {
 #endif
-
        //
        // This file contains the table of function pointers the host dyld supplies
        // to the iOS simulator dyld.
@@ -40,76 +63,84 @@ namespace dyld {
        struct SyscallHelpers
        {
                uintptr_t               version;
-               int                             (*open)(const char* path, int oflag, int extra);
-               int                             (*close)(int fd);
-               ssize_t                 (*pread)(int fd, void* buf, size_t nbyte, off_t offset);
-               ssize_t                 (*write)(int fd, const void* buf, size_t nbyte);
-               void*                   (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset);
-               int                             (*munmap)(void* addr, size_t len);
-               int                             (*madvise)(void* addr, size_t len, int advice);
-               int                             (*stat)(const char* path, struct stat* buf);
-               int                             (*fcntl)(int fildes, int cmd, void* result);
-               int                             (*ioctl)(int fildes, unsigned long request, void* result);
-               int                             (*issetugid)(void);
-               char*                   (*getcwd)(char* buf, size_t size);
-               char*                   (*realpath)(const char* file_name, char* resolved_name);
-               kern_return_t   (*vm_allocate)(vm_map_t target_task, vm_address_t *address, vm_size_t size, int flags);
-               kern_return_t   (*vm_deallocate)(vm_map_t target_task, vm_address_t address, vm_size_t size);
-               kern_return_t   (*vm_protect)(vm_map_t target_task, vm_address_t address, vm_size_t size, boolean_t max, vm_prot_t prot);
+               DYLD_SYSCALL_VTABLE_ENTRY(open);
+               DYLD_SYSCALL_VTABLE_ENTRY(close);
+               DYLD_SYSCALL_VTABLE_ENTRY(pread);
+               DYLD_SYSCALL_VTABLE_ENTRY(write);
+               DYLD_SYSCALL_VTABLE_ENTRY(mmap);
+               DYLD_SYSCALL_VTABLE_ENTRY(munmap);
+               DYLD_SYSCALL_VTABLE_ENTRY(madvise);
+               DYLD_SYSCALL_VTABLE_ENTRY(stat);
+               DYLD_SYSCALL_VTABLE_ENTRY(fcntl);
+               DYLD_SYSCALL_VTABLE_ENTRY(ioctl);
+               DYLD_SYSCALL_VTABLE_ENTRY(issetugid);
+               DYLD_SYSCALL_VTABLE_ENTRY(getcwd);
+               DYLD_SYSCALL_VTABLE_ENTRY(realpath);
+               DYLD_SYSCALL_VTABLE_ENTRY(vm_allocate);
+               DYLD_SYSCALL_VTABLE_ENTRY(vm_deallocate);
+               DYLD_SYSCALL_VTABLE_ENTRY(vm_protect);
                void                    (*vlog)(const char* format, va_list list);
                void                    (*vwarn)(const char* format, va_list list);
-               int                             (*pthread_mutex_lock)(pthread_mutex_t* m);
-               int                             (*pthread_mutex_unlock)(pthread_mutex_t* m);
-               mach_port_t             (*mach_thread_self)(void);
-               kern_return_t   (*mach_port_deallocate)(ipc_space_t task, mach_port_name_t name);
-               mach_port_name_t(*task_self_trap)(void);
-               kern_return_t   (*mach_timebase_info)(mach_timebase_info_t info);
+               DYLD_SYSCALL_VTABLE_ENTRY(pthread_mutex_lock);
+               DYLD_SYSCALL_VTABLE_ENTRY(pthread_mutex_unlock);
+               DYLD_SYSCALL_VTABLE_ENTRY(mach_thread_self);
+               DYLD_SYSCALL_VTABLE_ENTRY(mach_port_deallocate);
+               DYLD_SYSCALL_VTABLE_ENTRY(task_self_trap);
+               DYLD_SYSCALL_VTABLE_ENTRY(mach_timebase_info);
+#if OSATOMIC_USE_INLINED
                bool                    (*OSAtomicCompareAndSwapPtrBarrier)(void* old, void* nw, void * volatile *value);
                void                    (*OSMemoryBarrier)(void);
+#else
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+               DYLD_SYSCALL_VTABLE_ENTRY(OSAtomicCompareAndSwapPtrBarrier);
+               DYLD_SYSCALL_VTABLE_ENTRY(OSMemoryBarrier);
+#pragma clang diagnostic pop
+#endif
                void*                   (*getProcessInfo)(void); // returns dyld_all_image_infos*;
                int*                    (*errnoAddress)(void);
-               uint64_t                (*mach_absolute_time)(void);
+               DYLD_SYSCALL_VTABLE_ENTRY(mach_absolute_time);
                // Added in version 2
-               kern_return_t   (*thread_switch)(mach_port_name_t, int, mach_msg_timeout_t);
+               DYLD_SYSCALL_VTABLE_ENTRY(thread_switch);
                // Added in version 3
-               DIR*                    (*opendir)(const char* path);
-               int                     (*readdir_r)(DIR* dirp, struct dirent* entry, struct dirent **result);
-               int                     (*closedir)(DIR* dirp);
+               DYLD_SYSCALL_VTABLE_ENTRY(opendir);
+               DYLD_SYSCALL_VTABLE_ENTRY(readdir_r);
+               DYLD_SYSCALL_VTABLE_ENTRY(closedir);
                // Added in version 4
                void                    (*coresymbolication_load_notifier)(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header);
                void                    (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header);
                // Added in version 5
-               int                             (*proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t buffersize);
-               int                             (*getpid)(void);
-               kern_return_t   (*mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly);
-               kern_return_t   (*mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*);
-               kern_return_t   (*mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t);
+               DYLD_SYSCALL_VTABLE_ENTRY(proc_regionfilename);
+               DYLD_SYSCALL_VTABLE_ENTRY(getpid);
+               DYLD_SYSCALL_VTABLE_ENTRY(mach_port_insert_right);
+               DYLD_SYSCALL_VTABLE_ENTRY(mach_port_allocate);
+               DYLD_SYSCALL_VTABLE_ENTRY(mach_msg);
                // Added in version 6
-               void                    (*abort_with_payload)(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags);
+               DYLD_SYSCALL_VTABLE_ENTRY(abort_with_payload);
                // Add in version 7
-               kern_return_t   (*task_register_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt);
-               kern_return_t   (*task_unregister_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt);
-               kern_return_t   (*task_get_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t *dyld_images, mach_msg_type_number_t *dyld_imagesCnt);
-               kern_return_t   (*task_register_dyld_shared_cache_image_info)(task_t task, dyld_kernel_image_info_t dyld_cache_image, boolean_t no_cache, boolean_t private_cache);
-               kern_return_t   (*task_register_dyld_set_dyld_state)(task_t task, uint8_t dyld_state);
-        kern_return_t   (*task_register_dyld_get_process_state)(task_t task, dyld_kernel_process_info_t *dyld_process_state);
-        kern_return_t   (*task_info)(task_name_t target_task, task_flavor_t flavor, task_info_t task_info_out, mach_msg_type_number_t *task_info_outCnt);
-        kern_return_t   (*thread_info)(thread_inspect_t target_act, thread_flavor_t flavor, thread_info_t thread_info_out, mach_msg_type_number_t *thread_info_outCnt);
-        // Add in version 8
-        bool            (*kdebug_is_enabled)(uint32_t code);
-        int             (*kdebug_trace)(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
-        // Add in version 9
-        uint64_t        (*kdebug_trace_string)(uint32_t debugid, uint64_t str_id, const char *str);
-        // Add in version 10
-        int             (*amfi_check_dyld_policy_self)(uint64_t input_flags, uint64_t* output_flags);
-        // Add in version 11
-        void            (*notifyMonitoringDyldMain)(void);
-        void            (*notifyMonitoringDyld)(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[], const char* imagePaths[]);
-        // Add in version 12
-        void              (*mach_msg_destroy)(mach_msg_header_t *msg);
-        kern_return_t     (*mach_port_construct)(ipc_space_t task, mach_port_options_ptr_t options, mach_port_context_t context, mach_port_name_t *name);
-        kern_return_t     (*mach_port_destruct)(ipc_space_t task, mach_port_name_t name, mach_port_delta_t srdelta, mach_port_context_t guard);
-    };
+               DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_image_infos);
+               DYLD_SYSCALL_VTABLE_ENTRY(task_unregister_dyld_image_infos);
+               DYLD_SYSCALL_VTABLE_ENTRY(task_get_dyld_image_infos);
+               DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_shared_cache_image_info);
+               DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_set_dyld_state);
+               DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_get_process_state);
+               DYLD_SYSCALL_VTABLE_ENTRY(task_info);
+               DYLD_SYSCALL_VTABLE_ENTRY(thread_info);
+               // Add in version 8
+               DYLD_SYSCALL_VTABLE_ENTRY(kdebug_is_enabled);
+               DYLD_SYSCALL_VTABLE_ENTRY(kdebug_trace);
+               // Add in version 9
+               DYLD_SYSCALL_VTABLE_ENTRY(kdebug_trace_string);
+               // Add in version 10
+               DYLD_SYSCALL_VTABLE_ENTRY(amfi_check_dyld_policy_self);
+               // Add in version 11
+               void                    (*notifyMonitoringDyldMain)(void);
+               void                    (*notifyMonitoringDyld)(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[], const char* imagePaths[]);
+               // Add in version 12
+               DYLD_SYSCALL_VTABLE_ENTRY(mach_msg_destroy);
+               DYLD_SYSCALL_VTABLE_ENTRY(mach_port_construct);
+               DYLD_SYSCALL_VTABLE_ENTRY(mach_port_destruct);
+       };
        extern const struct SyscallHelpers* gSyscallHelpers;
 
 
index 92875de7c26b6eef56d5a5fd1b66f9608ad98631..8b02b746e08f1e0b1e7da235db2e26216e0c366e 100644 (file)
@@ -552,11 +552,13 @@ kern_return_t mach_timebase_info(mach_timebase_info_t info) {
        return gSyscallHelpers->mach_timebase_info(info);
 }
 
-bool OSAtomicCompareAndSwapPtrBarrier(void* old, void* new, void * volatile *value) {
+bool myOSAtomicCompareAndSwapPtrBarrier(void* old, void* new, void * volatile *value) __asm("_OSAtomicCompareAndSwapPtrBarrier");
+bool myOSAtomicCompareAndSwapPtrBarrier(void* old, void* new, void * volatile *value) {
        return gSyscallHelpers->OSAtomicCompareAndSwapPtrBarrier(old, new, value);
 }
 
-void OSMemoryBarrier()  {
+void myOSMemoryBarrier(void) __asm("_OSMemoryBarrier");
+void myOSMemoryBarrier()  {
        return gSyscallHelpers->OSMemoryBarrier();
 }
 
@@ -931,7 +933,7 @@ uint64_t kdebug_trace_string(uint32_t debugid, uint64_t str_id, const char *str)
     return 0;
 }
 
-uint64_t amfi_check_dyld_policy_self(uint64_t inFlags, uint64_t* outFlags)
+int amfi_check_dyld_policy_self(uint64_t inFlags, uint64_t* outFlags)
 {
     if ( gSyscallHelpers->version >= 10 )
         return gSyscallHelpers->amfi_check_dyld_policy_self(inFlags, outFlags);
index bb821c90283fc6309ede0dddbe86ac0350263f08..89ee46f96dedfbe7d166e28d112e3ff7fe885d91 100755 (executable)
@@ -12,31 +12,33 @@ Example, main.c may contain:
 
 When build lines are executed, the current directory is set to the test case's .dtest dir.
 Build lines may contain the follow variables:
-    $BUILD_DIR   - expands to the directory in $DSTROOT where this test case binaries are installed
-    $RUN_DIR     - expands to the directory in /AppleInternal/ where this test case binaries will be run
-    $TEMP_DIR    - expands to a temporary directory that will be delete after this test case is built
-    $CC          - expands to the C compiler command line for the current platform.  It includes
-                   the min-os-version option, then SDK option, and the architectures.
-    $CXX         - expands to the C++ compiler plus standard options
+    $BUILD_DIR    - expands to the directory in $DSTROOT where this test case binaries are installed
+    $RUN_DIR      - expands to the directory in /AppleInternal/ where this test case binaries will be run
+    $TEMP_DIR     - expands to a temporary directory that will be delete after this test case is built
+    $CC           - expands to the C compiler command line for the current platform.  It includes
+                    the min-os-version option, then SDK option, and the architectures.
+    $CXX          - expands to the C++ compiler plus standard options
+    $DEPENDS_ON   - adds a build time dependency to the next argument on the commandline
+    $SKIP_INSTALL - prevents the built binary from being installed
+    $SYM_LINK     - creates a symlink
+    $CP           - copies a file
 
 When run lines are executed, the current directory is set to what $RUN_DIR was during build.
 Run lines may contain the follow variables:
     $RUN_DIR       - expands to the directory in /AppleInternal/ where this test case binaries are installed
-    $REQUIRE_CRASH - expands to the 'nocr' tool which is used when a program is expected to "crash" in order to pass
 
 
-When a test program runs, it should initially print out the name of the test case. Ex:
- [BEGIN] dlfoo-thread-safe
-Then it should print a pass or fail message with the same test name. Ex:
- [PASS] dlfoo-thread-safe
+The file "test_support.h" provides support functions to correctly write tests. The primariy interfaces provided are
+three printf like functions (technially they are implemented as macros in order to capture line info for logging):
 
+    void PASS(const char* format, ...);
+    void FAIL(const char* format, ...);
+    void LOG(const char* format, ...);
 
-To support tests that dyld is supposed to terminate, use $REQUIRE_CRASH on the RUN: line
-along with setting env var NOCR_TEST_NAME to the name of the test case.  When that env
-var is set, the nocr tool wil print out the [BEGIN], then [PASS] if the test crashes
-otherwise [FAIL].  Ex:
-  // RUN:  NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe
-
+PASS() and FAIL() will take care of appropriately formatting the messages for the various test environments that dyld_tests
+run in. LOG() will capture messages in a per image queue. By default these logs are emitted if a test fails, and they are
+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
@@ -44,4 +46,5 @@ new min OS version can also be specified via the BUILD_MIN_OS option.  For insta
     // 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.
diff --git a/testing/build_ninja.py b/testing/build_ninja.py
new file mode 100755 (executable)
index 0000000..ea958c9
--- /dev/null
@@ -0,0 +1,512 @@
+#!/usr/bin/python2.7
+
+import plistlib
+import string
+import argparse
+import sys
+import os
+import tempfile
+import shutil
+import subprocess
+import re
+import hashlib
+import textwrap
+from string import Template
+
+class BufferedFile:
+    def __init__(self, fileName):
+        self.data = ""
+       self.fileName = os.path.abspath(fileName)
+    def __enter__(self):
+        return self
+    def __exit__(self, type, value, traceback):
+        if os.path.exists(self.fileName):
+            with open(self.fileName, "r") as file:
+                fileData = file.read()
+                if fileData == self.data: return
+        else:
+            dir = os.path.dirname(self.fileName)
+            if not os.path.exists(dir):
+                os.makedirs(dir)
+        with open(self.fileName, "w") as file:
+            file.write(self.data)
+    def write(self, str):
+        self.data += str
+
+class NinjaFile:
+    class Variable:
+        def __init__(self, name, value):
+            self.name = name
+            self.value = value
+        def __lt__(self, other):
+            return self.name.__lt__(other.name)
+        def __str__(self):
+            return NinjaFile.lineWrap("{} = {}".format(self.name, self.value))
+    
+    class Rule:
+        def __init__(self, name, command, depfile):
+            self.name = name
+            self.command = command
+            self.depfile = depfile
+        def __lt__(self, other):
+            return self.name.__lt__(other.name)
+        def __str__(self):
+            result = NinjaFile.lineWrap("rule {}".format(self.name))
+            if self.command: result += ("\n"+ NinjaFile.lineWrap("    command = {}".format(self.command)))
+            if self.depfile:
+                result += ("\n" + NinjaFile.lineWrap("    deps = gcc"))
+                result += ("\n" + NinjaFile.lineWrap("    depfile = {}".format(self.depfile)))
+            return result
+    class Target:
+        def __init__(self, rule):
+            self.rule = rule
+            self.output = ""
+            self.inputs = []
+            self.variables = []
+            self.dependencies = []
+        def __lt__(self, other):
+            return self.output.__lt__(other.output)
+        def __str__(self):
+            self.inputs.sort()
+            self.variables.sort()
+            self.dependencies.sort()
+            buildLine = "build {}: {}".format(self.output, self.rule)
+            if self.inputs: buildLine += " {}".format(" ".join(self.inputs))
+            if self.dependencies: buildLine += " | {}".format(" ".join(self.dependencies))
+            result = NinjaFile.lineWrap(buildLine)
+            for variable in self.variables: result += ("\n" + NinjaFile.lineWrap("    " + str(variable)))
+            return result
+        def addVariable(self, name, value): self.variables.append(NinjaFile.Variable(name, value))
+        def addDependency(self, dependency):
+            if isinstance(dependency, str):
+                self.dependencies.append(dependency)
+            elif isinstance(dependency, NinjaFile.Target):
+                self.dependencies.append(dependency.output)
+            else:
+                raise ValueError("dependency must be a string or NinjaFile.Target")
+        def addInput(self, input):
+            if isinstance(input, str):
+                self.inputs.append(input)
+            elif isinstance(input, NinjaFile.Target):
+                self.inputs.append(input.output)
+            else:
+                raise ValueError("input must be a string or NinjaFile.Target")
+    class Include:
+        def __init__(self, file):
+            self.file = file
+        def __lt__(self, other):
+            return self.file.__lt__(other.file)
+        def __str__(self):
+            return NinjaFile.lineWrap("include {}".format(self.file))
+
+    def __init__(self, fileName):
+        self.fileName = os.path.abspath(fileName)
+        self.rules = []
+        self.variables = []
+        self.targets = []
+        self.includes = []
+    def __enter__(self):
+        return self
+    def __exit__(self, type, value, traceback):
+        with BufferedFile(self.fileName) as file:
+            file.write(str(self))
+    def addRule(self, name, command, deps): self.rules.append(NinjaFile.Rule(name, command, deps))
+    def addVariable(self, name, value): self.variables.append(NinjaFile.Variable(name, value))
+    def addInclude(self, file): self.includes.append(NinjaFile.Include(file))
+    def newTarget(self, type, name):
+        target = NinjaFile.Target(type)
+        target.output = name
+        self.targets.append(target)
+        return target
+    def findTarget(self, name):
+        #PERF If this gets to be significant we can sort the array and binary search it
+        for target in self.targets:
+            if target.output == name: return target
+        raise ValueError("Target \"{}\"  not found".format(name))
+    def deleteTarget(self, name): self.targets.remove(self.findTarget(name))
+    def __str__(self):
+        self.variables.sort()
+        self.rules.sort()
+        self.targets.sort()
+        self.includes.sort()
+        subs = {
+            "VARIABLES": "\n".join(map(str, self.variables)),
+            "RULES": "\n\n".join(map(str, self.rules)),
+            "TARGETS": "\n\n".join(map(str, self.targets)),
+            "INCLUDES": "\n\n".join(map(str, self.includes))
+        }
+        return string.Template(
+"""ninja_required_version = 1.6
+
+$INCLUDES
+
+$VARIABLES
+
+$RULES
+
+$TARGETS
+
+""").safe_substitute(subs)
+#    wrapper = textwrap.TextWrapper(width = 130, subsequent_indent = "    ", break_long_words = False)
+    @classmethod
+    def lineWrap(cls, line):
+        if len(line) <= 132: return line
+        result = ""
+        currentIdx = 0
+        wrappedLineLeadingSpace = "  "
+        firstLineIndent = 0
+        if line[0].isspace():
+            firstLineIndent = 4
+            result = "    "
+            wrappedLineLeadingSpace = "      "
+        trailer = " $"
+        wrappedLineLeadingSpaceLen = len(wrappedLineLeadingSpace)
+        lineSpaceAvailable = 132-(firstLineIndent+wrappedLineLeadingSpaceLen)
+        words = line.split()
+        wordsCount = len(words)-1
+        for idx, word in enumerate(words):
+            wordLen = len(word)
+            if (wordLen <= lineSpaceAvailable and idx == wordsCount):
+                result += word
+            elif wordLen <= lineSpaceAvailable+2:
+                result += "{} ".format(word)
+                lineSpaceAvailable -= (wordLen)
+            else:
+                result += "$\n{}{} ".format(wrappedLineLeadingSpace, word)
+                lineSpaceAvailable = 132-(wrappedLineLeadingSpaceLen+wordLen)
+        return result
+
+def processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, 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:
+        args = buildLine.split()
+        if args[0] == "$DTRACE":
+            target = None
+            for idx, arg in enumerate(args):
+                if arg == "-o": target = ninja.newTarget("dtrace", args[idx+1])
+            for idx, arg in enumerate(args):
+                if arg == "-s": target.addInput(testSrcDir + "/" + args[idx+1])
+        elif args[0] == "$CP":
+            target = ninja.newTarget("cp", args[2])
+            target.addInput(testSrcDir + "/" + args[1])
+            testTarget.addInput(target)
+            installTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(args[2][9:]))
+            installTarget.addInput(target.output)
+            testInstallTarget.addInput(installTarget)
+        elif args[0] == "$SYMLINK":
+            target = ninja.newTarget("symlink", args[2])
+            target.addVariable("source", args[1])
+            testTarget.addInput(target)
+            installTarget = ninja.newTarget("symlink", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(args[2][9:]))
+            installTarget.addVariable("source", args[1])
+            testInstallTarget.addInput(installTarget)
+        elif args[0] == "$STRIP":
+            target = ninja.findTarget(args[1])
+            target.addVariable("extraCmds", "&& strip {}".format(target.output))
+        elif args[0] == "$SKIP_INSTALL":
+            target = "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(args[1][9:])
+            ninja.deleteTarget(target)
+            testInstallTarget.inputs.remove(target)
+        elif args[0] == "$DYLD_ENV_VARS_ENABLE":
+            if not macOSBuild:
+                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:
+                target = ninja.findTarget(args[1])
+                target.addVariable("entitlements", "--entitlements $SRCROOT/testing/task_for_pid_entitlement.plist")
+        elif args[0] in ["$CC", "$CXX"]:
+            tool = args[0][1:].lower()
+            sources = []
+            cflags = []
+            ldflags = []
+            dependencies = []
+            skipCount = 0
+            linkTarget = None
+            isMainExecutable = True
+            targetNames = [target.output for target in ninja.targets]
+            args = [escapedArg.replace("\"", "\\\"") for escapedArg in args[1:]]
+            #First find the target
+            for idx, arg in enumerate(args):
+                if arg == "-o":
+                    linkTarget = ninja.newTarget("{}-link".format(tool), args[idx+1])
+                    linkTarget.addDependency("$BUILT_PRODUCTS_DIR/libtest_support.a")
+                    testTarget.addInput(linkTarget);
+                    break
+            skipCount = 0
+            for idx, arg in enumerate(args):
+                if skipCount: skipCount -= 1
+                elif arg == "-o":
+                    skipCount = 1
+                elif arg == "$DEPENDS_ON":
+                    skipCount = 1
+                    dependencies.append(args[idx+1])
+                elif arg in ["-arch"]:
+                    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]
+                    ldflags.append(arg)
+                    ldflags.append(nextArg)
+                elif arg == "-sectcreate":
+                    skipCount = 3
+                    ldflags.append(arg)
+                    ldflags.append(args[idx+1])
+                    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 in ["-dynamiclib","-bundle"]:
+                    ldflags.append(arg)
+                    isMainExecutable = False
+                elif arg.endswith((".s", ".c", ".cpp", ".cxx", ".m", ".mm")):
+                    sources.append(testSrcDir + "/" +arg)
+                elif arg in targetNames:
+                    linkTarget.addInput(arg)
+                elif arg[:4] == "-Wl,":
+                    linkerArgs = arg.split(",")
+                    for linkerArg in linkerArgs:
+                        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)
+                    ldflags.append(arg)
+                elif arg[:7] == "-weak-l":
+                    candidate = "{}/lib{}.dylib".format(testDstDir, arg[7:])
+                    if candidate in targetNames: linkTarget.addDependency(candidate)
+                    ldflags.append(arg)
+                elif arg[:9] == "-upward-l":
+                    candidate = "{}/lib{}.dylib".format(testDstDir, arg[9:])
+                    if candidate in targetNames: linkTarget.addDependency(candidate)
+                    ldflags.append(arg)
+                else:
+                    cflags.append(arg)
+            if isMainExecutable:
+                ldflags.append("-force_load $BUILT_PRODUCTS_DIR/libtest_support.a")
+            for source in sources:
+                objectHash = hashlib.sha1(linkTarget.output+source+tool+"".join(cflags)).hexdigest()
+                target = ninja.newTarget(tool, "$OBJROOT/dyld_tests.build/Objects-normal/" + objectHash + ".o")
+                target.addInput(source)
+                target.dependencies = dependencies
+                if cflags: target.addVariable("cflags", " ".join(cflags))
+                if minOS: target.addVariable("minOS", minOS)
+                linkTarget.addInput(target)
+            if ldflags: linkTarget.addVariable("ldflags", " ".join(ldflags))
+            if minOS: linkTarget.addVariable("minOS", minOS)
+            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):
+    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");
+            for runLine in runLines:
+                runFile.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=2 DYLD_SHARED_REGION=avoid {}\n".format(runLine))
+
+        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))
+
+        if macOSBuild:
+            runFile.write("echo \"run in dyld3 mode with no shared cache\" \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:]))
+                else:
+                    runFile.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=3 DYLD_SHARED_REGION=avoid DYLD_USE_CLOSURES=1 {}\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)
+
+
+if __name__ == "__main__":
+    configPath = sys.argv[1]
+    configMap = {}
+    with open(configPath) as configFile:
+        for line in configFile.read().splitlines():
+            args = line.split()
+            configMap[args[0]] = args[2:]
+    sys.stderr.write("CONFIG: {}\n".format(configMap))
+    srcRoot = configMap["SRCROOT"][0]
+    symRoot = configMap["SYMROOT"][0]
+    sdkRoot = configMap["SDKROOT"][0]
+    objRoot = configMap["OBJROOT"][0]
+    osFlag = configMap["OSFLAG"][0]
+    osVers = configMap["OSVERSION"][0]
+    linkerFlags = configMap["LDFLAGS"][0];
+    installOwner = configMap["INSTALL_OWNER"][0];
+    installGroup = configMap["INSTALL_GROUP"][0];
+    installMode = configMap["INSTALL_MODE_FLAG"][0];
+    installDir = configMap["INSTALL_DIR"][0];
+    userHeaderSearchPaths = configMap["USER_HEADER_SEARCH_PATHS"]
+    systemHeaderSearchPaths = configMap["SYSTEM_HEADER_SEARCH_PATHS"]
+
+    derivedFilesDir = configMap["DERIVED_FILES_DIR"][0]
+    archs = configMap["ARCHS"]
+
+    if not os.path.exists(derivedFilesDir): os.makedirs(derivedFilesDir)
+    if not os.path.exists(objRoot): os.makedirs(objRoot)
+
+    sys.stderr.write("srcRoot = {}\n".format(srcRoot))
+    sys.stderr.write("sdkRoot = {}\n".format(sdkRoot))
+    sys.stderr.write("objRoot = {}\n".format(objRoot))
+    sys.stderr.write("osFlag = {}\n".format(osFlag))
+    sys.stderr.write("osVers = {}\n".format(osVers))
+    sys.stderr.write("archs = {}\n".format(archs))
+    sys.stderr.write("derivedFilesDir = {}\n".format(derivedFilesDir))
+
+    testSrcRoot = os.path.abspath(srcRoot + "/testing/test-cases")
+    ccTool = os.popen("xcrun --sdk " + sdkRoot + " --find clang").read().rstrip()
+    cxxTool = os.popen("xcrun --sdk " + sdkRoot + " --find clang++").read().rstrip()
+    headerPaths = " -isysroot " + sdkRoot
+    for headerPath in userHeaderSearchPaths:  headerPaths += " -I{}".format(headerPath)
+    for headerPath in systemHeaderSearchPaths:  headerPaths += " -I{}".format(headerPath)
+    macOSBuild = False
+    sudoCmd = ""
+    if osFlag == "mmacosx-version-min":
+        macOSBuild = True
+        sudoCmd = "sudo"
+
+    with NinjaFile(derivedFilesDir + "/build.ninja") as ninja:
+        ninja.addInclude("config.ninja")
+        ninja.addVariable("minOS", "-" + osFlag + "=" + osVers)
+        ninja.addVariable("archs", " ".join(["-arch {}".format(arch) for arch in archs]))
+        ninja.addVariable("mode", "0755")
+        ninja.addVariable("headerpaths", headerPaths)
+
+        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("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)
+        ninja.addRule("symlink", "ln -sfh $source $out", False)
+
+        allTarget = ninja.newTarget("phony", "all")
+        masterInstallTarget = ninja.newTarget("phony", "install")
+
+        runAllScriptPath = "{}/run_all_dyld_tests.sh".format(derivedFilesDir)
+        xctestPath = "{}/dyld_xctest.h".format(derivedFilesDir)
+        if "XCTestGenPath" in os.environ: xctestPath = os.environ["XCTestGenPath"]
+        batsTests = []
+        batsSuppressedCrashes = []
+        xctestInvocations = []
+        with BufferedFile(runAllScriptPath) as runAllScript:
+            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 = []
+                    buildLines = []
+                    minOS = None
+
+                    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,
+                            "SRC_DIR":              testSrcDir
+                        }
+                        runSubs = {
+                            "RUN_DIR":        testRunDir,
+                            "SUDO":           sudoCmd,
+                        }
+                        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:
+                                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:")
+                                    if idx != -1:
+                                        buildLines.append(string.Template(line[idx+6:]).safe_substitute(buildSubs))
+                                        continue
+                                    idx = string.find(line,"RUN:")
+                                    if idx != -1:
+                                        if "$SUDO" in line: batsTest["AsRoot"] = True
+                                        runLines.append(string.Template(line[idx+4:]).safe_substitute(runSubs))
+                                        continue
+                                    idx = string.find(line,"RUN_TIMEOUT:")
+                                    if idx != -1:
+                                        batsTest["Timeout"] = line[idx+12:].lstrip()
+                                        continue
+                                    idx = string.find(line,"BOOT_ARGS:")
+                                    if idx != -1:
+                                        batsTest["BootArgsSet"] = ",".join(line[idx+9:].split())
+                                        continue
+                                    idx = string.find(line,"NO_CRASH_LOG:")
+                                    if idx != -1:
+                                        batsSuppressedCrashes.append(line[idx+13:].lstrip())
+                                        continue
+                    if buildLines and runLines:
+                        processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir, testSrcDir)
+                        processRunLines(ninja, runLines, testName, macOSBuild, 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))
+            with BufferedFile(xctestPath) as xcTestFile:
+                xcTestFile.write("static const TestInfo sTests[] = {\n")
+                xcTestFile.write(",\n".join(xctestInvocations))
+                xcTestFile.write("\n};")
+        os.chmod(runAllScriptPath, 0755)
+        runAllFilesInstallTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/run_all_dyld_tests.sh")
+        runAllFilesInstallTarget.addInput("$DERIVED_FILES_DIR/run_all_dyld_tests.sh")
+        masterInstallTarget.addInput(runAllFilesInstallTarget)
+        batsFilePath = derivedFilesDir + "/dyld.plist"
+        batsTests.sort(key=lambda test: test["TestName"])
+        with BufferedFile(batsFilePath) as batsFile:
+            batsConfig = { "BATSConfigVersion": "0.1.0",
+                         "Project":           "dyld_tests",
+                         "Tests":             batsTests }
+            if batsSuppressedCrashes: batsConfig["IgnoreCrashes"] = batsSuppressedCrashes
+            batsFile.write(plistlib.writePlistToString(batsConfig))
+        os.system('plutil -convert binary1 ' + batsFilePath) # convert the plist in place to binary
+        batsConfigInstallTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/BATS/unit_tests/dyld.plist")
+        batsConfigInstallTarget.addInput(batsFilePath)
+        batsConfigInstallTarget.addVariable("mode", "0644")
+        masterInstallTarget.addInput(batsConfigInstallTarget)
+    sys.stdout.write("DONE\n")
+
diff --git a/testing/build_tests.py b/testing/build_tests.py
deleted file mode 100755 (executable)
index f33149f..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-#!/usr/bin/python2.7
-
-import plistlib
-import string
-import argparse
-import sys
-import os
-import tempfile
-import shutil
-import subprocess
-import uuid
-
-
-#
-# Scan files in .dtest directory looking for BUILD: or RUN: directives.
-# Return a dictionary of all directives.
-#
-def parseDirectives(testCaseSourceDir):
-    onlyLines = []
-    buildLines = []
-    extractLines = []
-    runLines = []
-    minOS = ""
-    timeout = ""
-    noCrashLogs = []
-    bootArgs = []
-    for file in os.listdir(testCaseSourceDir):
-        if file.endswith((".c", ".cpp", ".cxx", ".m", ".mm")):
-            with open(testCaseSourceDir + "/" + file) as f:
-                for line in f.read().splitlines():
-                    buildIndex = string.find(line, "BUILD:")
-                    if buildIndex != -1:
-                        buildLines.append(line[buildIndex + 6:].lstrip())
-                    runIndex = string.find(line, "RUN:")
-                    if runIndex != -1:
-                        runLines.append(line[runIndex+4:].lstrip())
-                    onlyIndex = string.find(line, "BUILD_ONLY:")
-                    if onlyIndex != -1:
-                        onlyLines.append(line[onlyIndex+11:].lstrip())
-                    minOsIndex = string.find(line, "BUILD_MIN_OS:")
-                    if minOsIndex != -1:
-                        minOS = line[minOsIndex+13:].lstrip()
-                    timeoutIndex = string.find(line, "RUN_TIMEOUT:")
-                    if timeoutIndex != -1:
-                        timeout = line[timeoutIndex+12:].lstrip()
-                    noCrashLogsIndex = string.find(line, "NO_CRASH_LOG:")
-                    if noCrashLogsIndex != -1:
-                        noCrashLogs.append(line[noCrashLogsIndex+13:].lstrip())
-                    bootArgsIndex = string.find(line, "BOOT_ARGS:")
-                    if bootArgsIndex != -1:
-                        bootArgs.append(line[bootArgsIndex+10:].lstrip())
-    return {
-        "BUILD":        buildLines,
-        "BUILD_ONLY":   onlyLines,
-        "BUILD_MIN_OS": minOS,
-        "RUN":          runLines,
-        "RUN_TIMEOUT":  timeout,
-        "NO_CRASH_LOG": noCrashLogs,
-        "BOOT_ARGS":    bootArgs,
-   }
-
-
-#
-# Look at directives dictionary to see if this test should be skipped for this platform
-#
-def useTestCase(testName, testCaseDirectives, platformName):
-    onlyLines = testCaseDirectives["BUILD_ONLY"]
-    for only in onlyLines:
-        if only == "MacOSX" and platformName != "macosx":
-            return False
-        if only == "iOS" and platformName != "iphoneos":
-            return False
-    return True
-
-
-#
-# Use BUILD directives to construct the test case
-# Use RUN directives to generate a shell script to run test(s)
-#
-def buildTestCase(testCaseDirectives, testCaseSourceDir, toolsDir, sdkDir, dyldIncludesDir, minOsOptionsName, defaultMinOS, archOptions, testCaseDestDirBuild, testCaseDestDirRun, plistDir):
-    scratchDir = tempfile.mkdtemp()
-    if testCaseDirectives["BUILD_MIN_OS"]:
-        minOS = testCaseDirectives["BUILD_MIN_OS"]
-    else:
-        minOS = defaultMinOS
-    compilerSearchOptions = " -isysroot " + sdkDir + " -I" + sdkDir + "/System/Library/Frameworks/System.framework/PrivateHeaders" + " -I" + dyldIncludesDir + " -I" + testsSrcTopDir + "../include/"
-    defines = " -DINSTALL_PATH=\"" + testCaseDestDirRun + "\""
-    if minOsOptionsName == "mmacosx-version-min":
-        taskForPidCommand = "touch "
-        envEnableCommand  = "touch "
-    else:
-        taskForPidCommand = "codesign --force --sign - --entitlements " + testCaseSourceDir + "/../../task_for_pid_entitlement.plist "
-        envEnableCommand  = "codesign --force --sign - --entitlements " + testCaseSourceDir + "/../../get_task_allow_entitlement.plist "
-    buildSubs = {
-        "CC":                   toolsDir + "/usr/bin/clang "   + archOptions + " -" + minOsOptionsName + "=" + str(minOS) + compilerSearchOptions + defines,
-        "CXX":                  toolsDir + "/usr/bin/clang++ " + archOptions + " -" + minOsOptionsName + "=" + str(minOS) + compilerSearchOptions + defines,
-        "BUILD_DIR":            testCaseDestDirBuild,
-        "RUN_DIR":              testCaseDestDirRun,
-        "TEMP_DIR":             scratchDir,
-        "TASK_FOR_PID_ENABLE":  taskForPidCommand,
-        "DYLD_ENV_VARS_ENABLE": envEnableCommand
-    }
-    os.makedirs(testCaseDestDirBuild)
-    os.chdir(testCaseSourceDir)
-    outputfiles = []
-    alreadySigned = []
-    print >> sys.stderr, "cd " + testCaseSourceDir
-    for line in testCaseDirectives["BUILD"]:
-        cmd = string.Template(line).safe_substitute(buildSubs)
-        if "codesign" in cmd:
-            alreadySigned.append(string.split(cmd).pop())
-        print >> sys.stderr, cmd
-        if "&&" in cmd:
-            result = subprocess.call(cmd, shell=True)
-        else:
-            cmdList = []
-            cmdList = string.split(cmd)
-            result = subprocess.call(cmdList)
-        if result:
-            return result
-        args = cmd.split()
-        for index, arg in enumerate(args):
-            if arg == "-o":
-                outputfiles.append(args[index+1])
-                break
-    print >> sys.stderr, "outfiles: " +  ' '.join(outputfiles)  + "already signed: " + ' '.join(alreadySigned)
-    for outfile in outputfiles:
-        if outfile not in alreadySigned:
-            cmd = "codesign --force --sign - " + outfile
-            print >> sys.stderr, cmd
-            subprocess.call(string.split(cmd))
-    shutil.rmtree(scratchDir, ignore_errors=True)
-    sudoSub = ""
-    if minOsOptionsName == "mmacosx-version-min":
-        sudoSub = "sudo"
-    runSubs = {
-        "RUN_DIR":        testCaseDestDirRun,
-        "REQUIRE_CRASH":  "nocr -require_crash",
-        "SUDO":           sudoSub,
-    }
-    runFilePath = testCaseDestDirBuild + "/run.sh"
-    with open(runFilePath, "a") as runFile:
-        runFile.write("#!/bin/sh\n")
-        runFile.write("cd " + testCaseDestDirRun + "\n")
-        os.chmod(runFilePath, 0755)
-
-        runFile.write("echo \"run in dyld2 mode\" \n");
-        for runline in testCaseDirectives["RUN"]:
-          subLine = string.Template(runline).safe_substitute(runSubs)
-          subLine = "TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0 " + subLine
-          runFile.write(subLine + "\n")
-
-        if minOsOptionsName == "mmacosx-version-min":
-            runFile.write("echo \"run in dyld2 mode with no shared cache\" \n");
-            for runline in testCaseDirectives["RUN"]:
-                subLine = string.Template(runline).safe_substitute(runSubs)
-                subLine = "TEST_DYLD_MODE=2 DYLD_SHARED_REGION=avoid " + subLine
-                runFile.write(subLine + "\n")
-
-        runFile.write("echo \"run in dyld3 mode\" \n");
-        for runline in testCaseDirectives["RUN"]:
-            subLine = string.Template(runline).safe_substitute(runSubs)
-            if subLine.startswith("sudo "):
-                subLine = "sudo TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 " + subLine[5:]
-            else:
-                subLine = "TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 " + subLine
-            runFile.write(subLine + "\n")
-
-        if minOsOptionsName == "mmacosx-version-min":
-            runFile.write("echo \"run in dyld3 mode with no shared cache\" \n");
-            for runline in testCaseDirectives["RUN"]:
-                subLine = string.Template(runline).safe_substitute(runSubs)
-                if subLine.startswith("sudo "):
-                    subLine = "sudo TEST_DYLD_MODE=3 DYLD_SHARED_REGION=avoid DYLD_USE_CLOSURES=1 " + subLine[5:]
-                else:
-                    subLine = "TEST_DYLD_MODE=3 DYLD_SHARED_REGION=avoid DYLD_USE_CLOSURES=1 " + subLine
-                runFile.write(subLine + "\n")
-
-        runFile.write("\n")
-        runFile.close()
-    for runline in testCaseDirectives["RUN"]:
-        runTarget = runline.split().pop()
-        os.system("xcrun dt_extractmeta extract -i " + testCaseDestDirRun + "/" + runTarget + " -b " + testCaseDestDirBuild + "/" + runTarget + " -o " + plistDir + "/" + str(uuid.uuid4()) + ".plist 2> /dev/null")
-    return 0
-
-
-
-#
-# Use XCode build settings to build all unit tests for specified platform
-# Generate a .plist for BATS to use to run all tests
-#
-if __name__ == "__main__":
-    dstDir = os.getenv("DSTROOT", "/tmp/dyld_tests/")
-    testsRunDstTopDir = "/AppleInternal/CoreOS/tests/dyld/"
-    testsBuildDstTopDir = dstDir + testsRunDstTopDir
-    # If we want to run directly from the dstroot then override that now
-    runFromDstRoot = os.getenv("RUN_FROM_DSTROOT", "")
-    if runFromDstRoot:
-        testsRunDstTopDir = testsBuildDstTopDir
-    shutil.rmtree(testsBuildDstTopDir, ignore_errors=True)
-    dyldSrcDir = os.getenv("SRCROOT", "")
-    if not dyldSrcDir:
-        dyldSrcDir = os.getcwd()
-    testsSrcTopDir = dyldSrcDir + "/testing/test-cases/"
-    dyldIncludesDir = dyldSrcDir + "/include/"
-    sdkDir = os.getenv("SDKROOT", "")
-    if not sdkDir:
-        sdkDir = subprocess.check_output(["xcrun", "-sdk", "macosx.internal", "--show-sdk-path"]).rstrip()
-    toolsDir = os.getenv("TOOLCHAIN_DIR", "/")
-    defaultMinOS = ""
-    minVersNum = "10.14"
-    minOSOption = os.getenv("DEPLOYMENT_TARGET_CLANG_FLAG_NAME", "")
-    if minOSOption:
-        minOSVersName = os.getenv("DEPLOYMENT_TARGET_CLANG_ENV_NAME", "")
-        if minOSVersName:
-            minVersNum = os.getenv(minOSVersName, "")
-    else:
-        minOSOption = "mmacosx-version-min"
-    platformName = os.getenv("PLATFORM_NAME", "macosx")
-    archOptions = ""
-    archList = os.getenv("RC_ARCHS", "")
-    if archList:
-        for arch in string.split(archList, " "):
-            archOptions = archOptions + " -arch " + arch
-    else:
-        if platformName == "watchos":
-            archOptions = "-arch armv7k"
-        elif platformName == "appletvos":
-            archOptions = "-arch arm64"
-        elif platformName == "macosx":
-            archList = os.getenv("ARCHS_STANDARD_64_BIT", "")
-            if archList:
-                for arch in string.split(archList, " "):
-                    archOptions = archOptions + " -arch " + arch
-            else:
-                archOptions = "-arch x86_64"
-        else:
-            archList = os.getenv("ARCHS_STANDARD_32_64_BIT", "")
-            if archList:
-                for arch in string.split(archList, " "):
-                    archOptions = archOptions + " -arch " + arch
-            else:
-                archOptions = "-arch x86_64"
-    allTests = []
-    suppressCrashLogs = []
-    plistDir = tempfile.mkdtemp()
-    for f in sorted(os.listdir(testsSrcTopDir)):
-        if f.endswith(".dtest"):
-            testName = f[0:-6]
-            outDirBuild = testsBuildDstTopDir + testName
-            outDirRun = testsRunDstTopDir + testName
-            testCaseDir = testsSrcTopDir + f
-            onlyTestDir = os.getenv("ONLY_BUILD_TEST", "")
-            if onlyTestDir:
-                if onlyTestDir != testName:
-                    continue
-                print >> sys.stderr, "Going to build " + testName
-            testCaseDirectives = parseDirectives(testCaseDir)
-            if useTestCase(testName, testCaseDirectives, platformName):
-                result = buildTestCase(testCaseDirectives, testCaseDir, toolsDir, sdkDir, dyldIncludesDir, minOSOption, minVersNum, archOptions, outDirBuild, outDirRun, plistDir)
-                if result:
-                    sys.exit(result)
-                mytest = {}
-                mytest["TestName"] = testName
-                mytest["Arch"] = "platform-native"
-                mytest["WorkingDirectory"] = testsRunDstTopDir + testName
-                mytest["Command"] = []
-                mytest["Command"].append("./run.sh")
-                for runline in testCaseDirectives["RUN"]:
-                    if "$SUDO" in runline:
-                        mytest["AsRoot"] = True
-                if testCaseDirectives["RUN_TIMEOUT"]:
-                    mytest["Timeout"] = testCaseDirectives["RUN_TIMEOUT"]
-                if testCaseDirectives["BOOT_ARGS"]:
-                    mytest["BootArgsSet"] = ",".join(testCaseDirectives["BOOT_ARGS"]);
-                allTests.append(mytest)
-            if testCaseDirectives["NO_CRASH_LOG"]:
-                for skipCrash in testCaseDirectives["NO_CRASH_LOG"]:
-                    suppressCrashLogs.append(skipCrash)
-    batsInfo = { "BATSConfigVersion": "0.1.0",
-                 "Project":           "dyld_tests",
-                 "Tests":             allTests }
-    if suppressCrashLogs:
-        batsInfo["IgnoreCrashes"] = []
-        for skipCrash in suppressCrashLogs:
-            batsInfo["IgnoreCrashes"].append(skipCrash)
-    batsFileDir = dstDir + "/AppleInternal/CoreOS/BATS/unit_tests/"
-    shutil.rmtree(batsFileDir, ignore_errors=True)
-    os.makedirs(batsFileDir)
-    batsFilePath = batsFileDir + "dyld.plist"
-    with open(batsFilePath, "w") as batsFile:
-        batsFile.write(plistlib.writePlistToString(batsInfo))
-        batsFile.close()
-    os.system('plutil -convert binary1 ' + batsFilePath) # convert the plist in place to binary
-    runHelper = dstDir + "/AppleInternal/CoreOS/tests/dyld/run_all_dyld_tests.sh"
-    print runHelper
-    with open(runHelper, "w") as shFile:
-        shFile.write("#!/bin/sh\n")
-        for test in allTests:
-            shFile.write(test["WorkingDirectory"] + "/run.sh\n")
-        shFile.close()
-    os.chmod(runHelper, 0755)
-    if not os.path.exists(dstDir + "/AppleInternal/CoreOS/tests/metadata/dyld/"): os.makedirs(dstDir + "/AppleInternal/CoreOS/tests/metadata/dyld/")
-    os.system("xcrun dt_extractmeta merge -o " + dstDir + "/AppleInternal/CoreOS/tests/metadata/dyld/dyld.plist " + plistDir + "/*")
-#   FIXME: Enable this once all tests move to darwintest
-#    os.system("xcrun dt_convertmeta " + dstDir + "/AppleInternal/CoreOS/BATS/unit_tests/dyld.plist dyld_tests " + dstDir + "/AppleInternal/CoreOS/tests/metadata/dyld/dyld.plist")
-    shutil.rmtree(plistDir, ignore_errors=True)
-
diff --git a/testing/include/dyld_test.h b/testing/include/dyld_test.h
deleted file mode 100644 (file)
index fe2227b..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-#include <mach/mach.h>
-
-#include "darwintest.h"
-
-T_GLOBAL_META(T_META_NAMESPACE("dyld"));
-
-extern char** environ;
-
-#if __x86_64__
-cpu_type_t otherArch[] = { CPU_TYPE_I386 };
-#elif __i386__
-cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
-#elif __arm64__
-cpu_type_t otherArch[] = { CPU_TYPE_ARM };
-#elif __arm__
-cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
-#endif
-
-#define T_DECL_DYLD(name, description, ...)                                                           \
-    static void dyld_test_ ## name(void);                                                           \
-    T_DECL(name, description, ## __VA_ARGS__) {                                                     \
-        dyld_test_ ## name();                                                                       \
-    }                                                                                               \
-    static void dyld_test_ ## name(void)
-
-/* Since the test runner manually invokes us in both dyld2 and dyld3 mode we do not need this yet
- T_DECL(name ## _dyld3, description, T_META_ENVVAR("DYLD_USE_CLOSURES=1"), ## __VA_ARGS__) {     \
- dyld_test_ ## name();                                                                       \
- }                                                                                               \
- */
-
-
-#define T_DLOPEN_EXPECT_NOTNULL(path, mode)                                                             \
-({                                                                                                      \
-    void* handle = dlopen(path, mode);                                                            \
-    T_QUIET; T_ASSERT_NOTNULL(handle, "Image \"%s\" failed to load with error: %s", path, dlerror());   \
-    T_QUIET; T_ASSERT_NULL(dlerror(), "dlerror() should be null after successfull dloepn()");           \
-    handle;                                                                                             \
-})
-
-#define T_DLSYM_EXPECT_NOTNULL(handle, symbol)                              \
-({                                                                          \
-    const void* sym = dlsym((void *)handle, symbol);                        \
-    T_QUIET; T_ASSERT_NOTNULL(sym, "dlsym(%s) should not be null", symbol); \
-    sym;                                                                    \
-})
-
-#define T_DLCLOSE_EXPECT_NULL(handle)                                                   \
-({                                                                                      \
-    int result = dlclose((void *)handle);                                               \
-    T_QUIET; T_ASSERT_EQ_INT(result, 0, "dlclose() failed with error: %s", dlerror());  \
-    result;                                                                             \
-})
-
-#define T_POSIXSPAWN_ASSERT(launchSuspended, launchOtherArch, program)                                                  \
-({                                                                                                                      \
-    pid_t result = 0;                                                                                                   \
-    posix_spawnattr_t attr = 0;                                                                                         \
-    T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_init(&attr), 0, "dyld_process_info posix_spawnattr_init() failed");        \
-    if ( launchSuspended ) {                                                                                            \
-        int result = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);                                      \
-        T_QUIET; T_ASSERT_EQ_INT(result, 0, "posix_spawnattr_setflags() failed");                                       \
-    }                                                                                                                   \
-    if ( launchOtherArch ) {                                                                                            \
-        size_t copied;                                                                                                  \
-        int result = posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied);                                       \
-        T_QUIET; T_ASSERT_EQ_INT(result, 0, "posix_spawnattr_setbinpref_np(), &copied) failed");                        \
-    }                                                                                                                   \
-    const char* argv[] = { program, NULL };                                                                             \
-    int psResult = posix_spawn(&result, program, NULL, &attr, (char**)argv, environ);                                   \
-    T_QUIET; T_ASSERT_EQ_INT(psResult, 0, "dyld_process_info posix_spawn(%s) failed, err=%d\n", program, psResult);     \
-    T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_destroy(&attr), KERN_SUCCESS, "posix_spawnattr_destroy() failed");         \
-    result;                                                                                                             \
-})
-
-#define T_TASK_FOR_PID_ASSERT(pid)                                                                          \
-({                                                                                                          \
-    task_t result;                                                                                          \
-    T_QUIET; T_ASSERT_MACH_SUCCESS(task_for_pid(mach_task_self(), pid, &result), "task_for_pid() failed");  \
-    result;                                                                                                 \
-})
-
-#pragma pack(4)
-typedef struct exception_data {
-    mach_msg_header_t Head;
-    mach_msg_body_t msgh_body;
-    mach_msg_port_descriptor_t thread;
-    mach_msg_port_descriptor_t task;
-    NDR_record_t NDR;
-    exception_type_t exception;
-    mach_msg_type_number_t codeCnt;
-    __int64_t code[2];
-} exception_data;
-#pragma pack()
-
-typedef bool(^exceptionValidator)(task_t task);
-
-#define T_POSIXSPAWN_CRASH(program, validatorFunc, ...)                                                                 \
-({                                                                                                                      \
-    pid_t pid = 0;                                                                                                      \
-    posix_spawnattr_t attr = 0;                                                                                         \
-    mach_port_t exceptionPort = MACH_PORT_NULL;                                                                         \
-    T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_init(&attr), 0, "dyld_process_info posix_spawnattr_init() failed");        \
-    mach_port_options_t options = { .flags = MPO_CONTEXT_AS_GUARD | MPO_STRICT | MPO_INSERT_SEND_RIGHT, .mpl = { 1 }};  \
-    T_QUIET; T_ASSERT_MACH_SUCCESS(mach_port_construct(mach_task_self(), &options, (mach_port_context_t)exceptionPort,  \
-        &exceptionPort), "mach_port_construct() failed");                                                               \
-    int epResult = posix_spawnattr_setexceptionports_np(&attr, EXC_MASK_CORPSE_NOTIFY, exceptionPort,                   \
-        EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0);                                                                   \
-    T_QUIET; T_ASSERT_EQ_INT(epResult, 0, "posix_spawnattr_setexceptionports_np() failed");                             \
-    const char* argv[] = { program, NULL };                                                                             \
-    int psResult = posix_spawn(&pid, program, NULL, &attr, (char**)argv, environ);                                      \
-    T_QUIET; T_ASSERT_EQ_INT(psResult, 0, "dyld_process_info posix_spawn(%s) failed, err=%d\n", program, psResult);     \
-    T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_destroy(&attr), KERN_SUCCESS, "posix_spawnattr_destroy() failed");         \
-    uint8_t data[MACH_MSG_SIZE_RELIABLE];                                                                               \
-    exception_data* request = (exception_data*)&data[0];                                                                \
-    T_QUIET; T_ASSERT_MACH_SUCCESS(mach_msg(&request->Head, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,                         \
-        MACH_MSG_SIZE_RELIABLE, exceptionPort, 10000, MACH_PORT_NULL), "mach_msg() failed");                            \
-    validatorFunc((task_t)request->task.name);                                                                          \
-})
index 5f3fc833f78e59afce32f5777348e0940a3df054..d0541540806c71647065bab8aa7eef0b8a4aac30 100644 (file)
@@ -1,74 +1,79 @@
-#include <stdio.h>
-#include <stdarg.h>
+#ifndef __DYLD_TEST_SUPPORT_H__
+#define __DYLD_TEST_SUPPORT_H__ 1
+
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
 #include <unistd.h>
-#include <mach-o/dyld.h>
-#include <sys/param.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <dispatch/dispatch.h>
+
+#if __cplusplus
+};
+
+// Only allow this interface for Objective-C++ due to typename and ARC issues in the default constructor
+
+typedef void (^_dyld_test_reader_t)(int fd);
+typedef void (^_dyld_test_exit_handler_t)(pid_t pid);
+typedef void (^_dyld_test_crash_handler_t)(task_t task);
+
+struct _process {
+    _process();
+    ~_process();
+    void set_executable_path(const char* EP);
+    void set_args(const char** A);
+    void set_env(const char** E);
+    void set_stdout_handler(_dyld_test_reader_t SOH);
+    void set_stderr_handler(_dyld_test_reader_t SEH);
+    void set_exit_handler(_dyld_test_exit_handler_t EH);
+    void set_crash_handler(_dyld_test_crash_handler_t CH);
+    void set_launch_suspended(bool S);
+    void set_launch_async(bool S);
+    void set_launch_arch(cpu_type_t A);
+    pid_t launch();
+    void *operator new(size_t size);
+    void operator delete(void *ptr);
+private:
+    const char* executablePath;
+    const char** args;
+    const char** env;
+    _dyld_test_reader_t stdoutHandler;
+    _dyld_test_reader_t stderrHandler;
+    _dyld_test_crash_handler_t crashHandler;
+    _dyld_test_exit_handler_t exitHandler;
+    pid_t pid;
+    cpu_type_t arch;
+    bool suspended;
+    bool async;
+};
+#endif /* __cplusplus */
+
+#define PASS(...)           _PASS(__FILE__,__LINE__,__VA_ARGS__)
+#define FAIL(...)           _FAIL(__FILE__,__LINE__,__VA_ARGS__)
+#define LOG(...)            _LOG(__FILE__,__LINE__,__VA_ARGS__)
+#define TIMEOUT(seconds)    _TIMEOUT(__FILE__,__LINE__,seconds)
+
+// MARK: Private implementation details
 
-#if __LP64__
-extern struct mach_header_64 __dso_handle;
-#else
-extern struct mach_header __dso_handle;
-#endif
+#if __cplusplus
+extern "C" {
+#endif  /* __cplusplus */
+__attribute__((format(printf, 3, 4)))
+__attribute__ ((noreturn))
+extern void _PASS(const char* file, unsigned line, const char* format, ...);
 
-static bool sIsATTY = false;
-static const char * sTestName = NULL;
-static uint64_t sTestCount = 0;
+__attribute__((format(printf, 3, 4)))
+__attribute__ ((noreturn))
+extern void _FAIL(const char* file, unsigned line, const char* format, ...);
 
-__attribute__((constructor))
-static
-void BEGIN(int argc, const char* argv[], const char* envp[])  {
-    // Set up values we need to print in PASS() and FAIL()
-    sIsATTY = isatty(fileno(stdout));
-    sTestName = argv[0];
-    // Early returnif this not the main executbale, we only need to print the [BEGIN] line once
-    if (__dso_handle.filetype != MH_EXECUTE) {
-        return;
-    }
-    printf("[BEGIN]");
-    for (uint32_t i = 0; envp[i] != NULL; ++i) {
-        if (strncmp("DYLD_", envp[i], 5) == 0) {
-            printf(" %s", envp[i]);
-        }
-    }
-    char buffer[MAXPATHLEN];
-    uint32_t bufsize = MAXPATHLEN;
-    if (_NSGetExecutablePath(buffer, &bufsize) == 0) {
-        printf(" %s", buffer);
-    } else {
-        printf(" %s", argv[0]);
-    }
-    for (uint32_t i = 1; i < argc; ++i) {
-        printf (" %s", argv[i]);
-    }
-    printf("\n");
-}
+__attribute__((format(printf, 3, 4)))
+extern void _LOG(const char* file, unsigned line, const char* format, ...);
 
-__attribute__((format(printf, 1, 2)))
-static
-void PASS(const char *format, ...)  {
-    if (sIsATTY) {
-        printf("[\033[0;32mPASS\033[0m] %s (%llu): ", sTestName, sTestCount++);
-    } else {
-        printf("[PASS] %s (%llu): ", sTestName, sTestCount++);
-    }
-    va_list args;
-    va_start (args, format);
-    vprintf (format, args);
-    va_end (args);
-    printf("\n");
-}
+extern void _TIMEOUT(const char* file, unsigned line, uint64_t seconds);
+#if __cplusplus
+};
+#endif  /* __cplusplus */
 
-__attribute__((format(printf, 1, 2)))
-static
-void FAIL(const char *format, ...) {
-    if (sIsATTY) {
-        printf("[\033[0;31mFAIL\033[0m] %s (%llu): ", sTestName, sTestCount++);
-    } else {
-        printf("[FAIL] %s (%llu): ", sTestName, sTestCount++);
-    }
-    va_list args;
-    va_start (args, format);
-    vprintf (format, args);
-    va_end (args);
-    printf("\n");
-}
+#endif /* __DYLD_TEST_SUPPORT_H__ */
diff --git a/testing/lib/execserver.defs b/testing/lib/execserver.defs
new file mode 100644 (file)
index 0000000..69468ba
--- /dev/null
@@ -0,0 +1,2 @@
+#include <mach/mach_exc.defs>
+
diff --git a/testing/lib/test_support.cpp b/testing/lib/test_support.cpp
new file mode 100644 (file)
index 0000000..a98dc22
--- /dev/null
@@ -0,0 +1,721 @@
+#include <dlfcn.h>
+#include <Block.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <assert.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <os/lock.h>
+#include <sys/attr.h>
+#include <sys/wait.h>
+#include <mach/mach.h>
+#include <mach-o/dyld.h>
+#include <mach/mach_vm.h>
+#include <sys/fsgetpath.h>
+#include <mach-o/getsect.h>
+#include <mach/vm_region.h>
+#include <dispatch/private.h>
+#include <dispatch/dispatch.h>
+
+
+#include <atomic>
+#include <utility>
+#include <algorithm>
+
+extern "C" {
+#include "execserverServer.h"
+
+union catch_mach_exc_request_reply {
+    union __RequestUnion__catch_mach_exc_subsystem request;
+    union __ReplyUnion__catch_mach_exc_subsystem reply;
+};
+};
+
+#include "test_support.h"
+
+extern const int    NXArgc;
+extern const char**  NXArgv;
+extern const char**  environ;
+extern char*        __progname;
+#if __x86_64__
+static const cpu_type_t currentArch = CPU_TYPE_X86_64;
+#elif __i386__
+static const cpu_type_t currentArch = CPU_TYPE_I386;
+#elif __arm64__
+static const cpu_type_t currentArch = CPU_TYPE_ARM64;
+#elif __arm__
+static const cpu_type_t currentArch = CPU_TYPE_ARM;
+#endif
+
+namespace {
+struct ScopedLock {
+    ScopedLock() : _lock(OS_UNFAIR_LOCK_INIT) {}
+    template<typename F>
+    void withLock(F f) {
+        os_unfair_lock_lock(&_lock);
+        f();
+        os_unfair_lock_unlock(&_lock);
+    }
+private:
+    os_unfair_lock _lock;
+};
+
+template <typename T, int QUANT=4, int INIT=1>
+class GrowableArray
+{
+public:
+
+    T&              operator[](size_t idx)       { assert(idx < _usedCount); return _elements[idx]; }
+    const T&        operator[](size_t idx) const { assert(idx < _usedCount); return _elements[idx]; }
+    T&              back()                       { assert(_usedCount > 0); return _elements[_usedCount-1]; }
+    uintptr_t       count() const                { return _usedCount; }
+    uintptr_t       maxCount() const             { return _allocCount; }
+    bool            empty() const                { return (_usedCount == 0); }
+    uintptr_t       index(const T& element)      { return &element - _elements; }
+    void            push_back(const T& t)        { verifySpace(1); _elements[_usedCount++] = t; }
+    void            pop_back()                   { assert(_usedCount > 0); _usedCount--; }
+    T*              begin()                      { return &_elements[0]; }
+    T*              end()                        { return &_elements[_usedCount]; }
+    const T*        begin() const                { return &_elements[0]; }
+    const T*        end() const                  { return &_elements[_usedCount]; }
+    bool            contains(const T& targ) const { for (const T& a : *this) { if ( a == targ ) return true; } return false; }
+    void            erase(T& targ);
+
+protected:
+    void            growTo(uintptr_t n);
+    void            verifySpace(uintptr_t n)     { if (this->_usedCount+n > this->_allocCount) growTo(this->_usedCount + n); }
+
+private:
+    T*              _elements               = _initialAlloc;
+    uintptr_t       _allocCount             = INIT;
+    uintptr_t       _usedCount              = 0;
+    T               _initialAlloc[INIT]     = { };
+};
+
+
+template <typename T, int QUANT, int INIT>
+inline void GrowableArray<T,QUANT,INIT>::growTo(uintptr_t n)
+{
+    uintptr_t newCount = (n + QUANT - 1) & (-QUANT);
+    T* newArray = (T*)::malloc(sizeof(T)*newCount);
+    T* oldArray = this->_elements;
+    if ( this->_usedCount != 0 )
+        ::memcpy(newArray, oldArray, sizeof(T)*this->_usedCount);
+    this->_elements   = newArray;
+    this->_allocCount = newCount;
+    if ( oldArray != this->_initialAlloc )
+        ::free(oldArray);
+}
+
+template <typename T, int QUANT, int INIT>
+inline void GrowableArray<T,QUANT,INIT>::erase(T& targ)
+{
+    intptr_t index = &targ - _elements;
+    assert(index >= 0);
+    assert(index < (intptr_t)_usedCount);
+    intptr_t moveCount = _usedCount-index-1;
+    if ( moveCount > 0 )
+        ::memcpy(&_elements[index], &_elements[index+1], moveCount*sizeof(T));
+    _usedCount -= 1;
+}
+
+struct TestState {
+    TestState();
+    static TestState* getState();
+    void _PASSV(const char* file, unsigned line, const char* format, va_list args) __attribute__ ((noreturn));
+    void _FAILV(const char* file, unsigned line, const char* format, va_list args) __attribute__ ((noreturn));
+    void _LOGV(const char* file, unsigned line, const char* format, va_list args);
+    GrowableArray<std::pair<mach_port_t, _dyld_test_crash_handler_t>>& getCrashHandlers();
+private:
+    enum OutputStyle {
+        None,
+        BATS,
+        Console,
+        XCTest
+    };
+    void runLeaks();
+    void dumpLogs();
+    static uint8_t hexCharToUInt(const char hexByte, uint8_t* value);
+    static uint64_t hexToUInt64(const char* startHexByte, const char** endHexByte);
+    
+    ScopedLock _IOlock;
+    GrowableArray<const char *> logs;
+    const char *testName;
+    bool logImmediate;
+    bool logOnSuccess;
+    bool checkForLeaks;
+    OutputStyle output;
+    GrowableArray<std::pair<mach_port_t, _dyld_test_crash_handler_t>> crashHandlers;
+};
+
+// 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:
+//
+// 1. Embed a stuct containing a lock and a pointer to our global state object in eahc 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
+//
+// This should work because the image list forms a stable ordering. The one loose end is if an executable is  running where logging
+// is only used in dylibs that are all being dlopned() and dlclosed. Since many dylibs cannot be dlclosed that should be a non-issue
+// in practice.
+};
+
+__attribute__((section("__DATA,__dyld_test")))
+static std::atomic<TestState*> sState;
+
+kern_return_t
+catch_mach_exception_raise(mach_port_t exception_port,
+                           mach_port_t thread,
+                           mach_port_t task,
+                           exception_type_t exception,
+                           mach_exception_data_t code,
+                           mach_msg_type_number_t codeCnt)
+{
+    _dyld_test_crash_handler_t crashHandler = NULL;
+    for (const auto& handler : TestState::getState()->getCrashHandlers()) {
+        if (handler.first == exception_port) {
+            crashHandler = handler.second;
+        }
+    }
+    if (crashHandler) {
+        if (exception == EXC_CORPSE_NOTIFY) {
+            crashHandler(task);
+        } else {
+            return KERN_FAILURE;
+        }
+    }
+    return KERN_SUCCESS;
+}
+
+kern_return_t
+catch_mach_exception_raise_state(mach_port_t exception_port,
+                                 exception_type_t exception,
+                                 const mach_exception_data_t code,
+                                 mach_msg_type_number_t codeCnt,
+                                 int * flavor,
+                                 const thread_state_t old_state,
+                                 mach_msg_type_number_t old_stateCnt,
+                                 thread_state_t new_state,
+                                 mach_msg_type_number_t * new_stateCnt)
+{
+    return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t
+catch_mach_exception_raise_state_identity(mach_port_t exception_port,
+                                          mach_port_t thread,
+                                          mach_port_t task,
+                                          exception_type_t exception,
+                                          mach_exception_data_t code,
+                                          mach_msg_type_number_t codeCnt,
+                                          int * flavor,
+                                          thread_state_t old_state,
+                                          mach_msg_type_number_t old_stateCnt,
+                                          thread_state_t new_state,
+                                          mach_msg_type_number_t * new_stateCnt)
+{
+    return KERN_NOT_SUPPORTED;
+}
+
+_process::_process() :  executablePath(nullptr), args(nullptr), env(nullptr), stdoutHandler(nullptr), stderrHandler(nullptr),
+                        crashHandler(nullptr), exitHandler(nullptr), pid(0), arch(currentArch), suspended(false), async(false) {}
+_process::~_process() {
+    if (stdoutHandler) { Block_release(stdoutHandler);}
+    if (stderrHandler) { Block_release(stderrHandler);}
+    if (crashHandler) { Block_release(crashHandler);}
+    if (exitHandler) { Block_release(exitHandler);}
+}
+
+void _process::set_executable_path(const char* EP) { executablePath = EP; }
+void _process::set_args(const char** A) { args = A; }
+void _process::set_env(const char** E) { env = E; }
+void _process::set_stdout_handler(_dyld_test_reader_t SOH) { stdoutHandler = Block_copy(SOH); };
+void _process::set_stderr_handler(_dyld_test_reader_t SEH) { stderrHandler = Block_copy(SEH); }
+void _process::set_exit_handler(_dyld_test_exit_handler_t EH) { exitHandler = Block_copy(EH); }
+void _process::set_crash_handler(_dyld_test_crash_handler_t CH) { crashHandler = Block_copy(CH); }
+void _process::set_launch_suspended(bool S) { suspended = S; }
+void _process::set_launch_async(bool S) { async = S; }
+void _process::set_launch_arch(cpu_type_t A) { arch = A; }
+
+pid_t _process::launch() {
+    dispatch_queue_t queue = dispatch_queue_create("com.apple.dyld.test.launch", NULL);
+    dispatch_block_t oneShotSemaphoreBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
+    posix_spawn_file_actions_t fileActions = NULL;
+    posix_spawnattr_t attr = NULL;
+    dispatch_source_t stdoutSource = NULL;
+    dispatch_source_t stderrSource = NULL;
+    int stdoutPipe[2];
+    int stderrPipe[2];
+
+    if (posix_spawn_file_actions_init(&fileActions) != 0) {
+        FAIL("Setting up spawn filea actions");
+    }
+    if (posix_spawnattr_init(&attr) != 0) { FAIL("Setting up spawn attr"); }
+    if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0) {
+        FAIL("Setting up spawn attr: POSIX_SPAWN_START_SUSPENDED");
+    }
+
+    if (pipe(stdoutPipe) != 0) { FAIL("Setting up pipe"); }
+    if (posix_spawn_file_actions_addclose(&fileActions, stdoutPipe[0]) != 0) { FAIL("Setting up pipe"); }
+    if (posix_spawn_file_actions_adddup2(&fileActions, stdoutPipe[1], STDOUT_FILENO) != 0) { FAIL("Setting up pipe"); }
+    if (posix_spawn_file_actions_addclose(&fileActions, stdoutPipe[1]) != 0) { FAIL("Setting up pipe"); }
+    fcntl((int)stdoutPipe[0], F_SETFL, O_NONBLOCK);
+    stdoutSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)stdoutPipe[0], 0, queue);
+    dispatch_source_set_event_handler(stdoutSource, ^{
+        int fd = (int)dispatch_source_get_handle(stdoutSource);
+        if (stdoutHandler) {
+            stdoutHandler(fd);
+        } else {
+            char buffer[16384];
+            ssize_t size = 0;
+            do {
+                size = read(fd, &buffer[0], 16384);
+            } while (size > 0);
+        }
+    });
+    dispatch_source_set_cancel_handler(stdoutSource, ^{
+        dispatch_release(stdoutSource);
+    });
+    dispatch_resume(stdoutSource);
+
+    if (pipe(stderrPipe) != 0) { FAIL("Setting up pipe"); }
+    if (posix_spawn_file_actions_addclose(&fileActions, stderrPipe[0]) != 0) { FAIL("Setting up pipe"); }
+    if (posix_spawn_file_actions_adddup2(&fileActions, stderrPipe[1], STDERR_FILENO) != 0) { FAIL("Setting up pipe"); }
+    if (posix_spawn_file_actions_addclose(&fileActions, stderrPipe[1]) != 0) { FAIL("Setting up pipe"); }
+    fcntl((int)stderrPipe[0], F_SETFL, O_NONBLOCK);
+    stderrSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)stderrPipe[0], 0, queue);
+    dispatch_source_set_event_handler(stderrSource, ^{
+        int fd = (int)dispatch_source_get_handle(stderrSource);
+        if (stderrHandler) {
+            stderrHandler(fd);
+        } else {
+            char buffer[16384];
+            ssize_t size = 0;
+            do {
+                size = read(fd, &buffer[0], 16384);
+            } while (size > 0);
+        }
+    });
+    dispatch_source_set_cancel_handler(stderrSource, ^{
+        dispatch_release(stderrSource);
+    });
+    dispatch_resume(stderrSource);
+    
+    if (crashHandler) {
+        auto& crashHandlers = TestState::getState()->getCrashHandlers();
+        mach_port_t exceptionPort = MACH_PORT_NULL;
+        mach_port_options_t options = { .flags = MPO_CONTEXT_AS_GUARD | MPO_STRICT | MPO_INSERT_SEND_RIGHT, .mpl = { 1 }};
+        if ( mach_port_construct(mach_task_self(), &options, (mach_port_context_t)exceptionPort, &exceptionPort) != KERN_SUCCESS ) {
+            FAIL("Could not construct port");
+        }
+        if (posix_spawnattr_setexceptionports_np(&attr,  EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exceptionPort,
+                                                 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0) != 0) {
+            FAIL("posix_spawnattr_setexceptionports_np failed");
+        }
+        crashHandlers.push_back(std::make_pair(exceptionPort, crashHandler));
+        dispatch_source_t crashSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, exceptionPort, 0, queue);
+        dispatch_source_set_event_handler(crashSource, ^{
+            dispatch_mig_server(crashSource, sizeof(union catch_mach_exc_request_reply), ::mach_exc_server);
+        });
+        dispatch_source_set_cancel_handler(crashSource, ^{
+            mach_port_destruct(mach_task_self(), exceptionPort, 0, (mach_port_context_t)exceptionPort);
+        });
+        dispatch_resume(crashSource);
+    }
+
+    pid_t pid;
+    uint32_t argc = 0;
+    if (args) {
+        for (argc = 0; args[argc] != NULL; ++argc) {}
+    }
+    ++argc;
+    const char *argv[argc+1];
+    argv[0] = executablePath;
+    for (uint32_t i = 1; i < argc; ++i) {
+        argv[i] = args[i-1];
+    }
+    argv[argc] = NULL;
+    
+    int result = posix_spawn(&pid, executablePath, &fileActions, &attr, (char **)argv, (char **)env);
+    if ( result != 0 ) {
+        FAIL("posix_spawn(%s) failed, err=%d", executablePath, result);
+    }
+    dispatch_source_t exitSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (pid_t)pid,
+                                                            DISPATCH_PROC_EXIT, queue);
+    dispatch_source_set_event_handler(exitSource, ^{
+        if (exitHandler) {
+            exitHandler((pid_t)dispatch_source_get_handle(exitSource));
+        }
+        dispatch_source_cancel(exitSource);
+        if (stdoutSource) {
+            dispatch_source_cancel(stdoutSource);
+        }
+        if (stderrSource) {
+            dispatch_source_cancel(stderrSource);
+        }
+        oneShotSemaphoreBlock();
+        dispatch_source_cancel(exitSource);
+    });
+    dispatch_resume(exitSource);
+
+    if (stdoutHandler) {
+        close(stdoutPipe[1]);
+    }
+    if (stderrHandler) {
+        close(stderrPipe[1]);
+    }
+    if (fileActions) {
+        posix_spawn_file_actions_destroy(&fileActions);
+    }
+    posix_spawnattr_destroy(&attr);
+    if (!suspended) {
+        kill(pid, SIGCONT);
+    }
+    if (!async) {
+        dispatch_block_wait(oneShotSemaphoreBlock, DISPATCH_TIME_FOREVER);
+    }
+    Block_release(oneShotSemaphoreBlock);
+    dispatch_release(queue);
+    return pid;
+}
+
+void *_process::operator new(size_t size) {
+    return malloc(size);
+}
+
+void _process::operator delete(void *ptr) {
+    free(ptr);
+}
+
+// MARK: Private implementation details
+
+template<typename F>
+static
+void forEachEnvVar(const char* envp[], F&& f) {
+    for (uint32_t i = 0; envp[i] != nullptr; ++i) {
+        const char* envBegin = envp[i];
+        const char* envEnd = strchr(envp[i], '=');
+        if (!envEnd) { continue; }
+        size_t envSize = (envEnd-envBegin)+1;
+        const char* valBegin = envEnd+1;
+        const char* valEnd = strchr(envp[i], '\0');
+        if (!valEnd) { continue; }
+        size_t valSize = (valEnd-valBegin)+1;
+        char env[envSize];
+        char val[valSize];
+        strlcpy(&env[0], envBegin, envSize);
+        strlcpy(&val[0], valBegin, valSize);
+        f(&env[0], &val[0]);
+    }
+}
+
+uint8_t TestState::hexCharToUInt(const char hexByte, uint8_t* value) {
+    if (hexByte >= '0' && hexByte <= '9') {
+        *value = hexByte - '0';
+        return true;
+    } else if (hexByte >= 'A' && hexByte <= 'F') {
+        *value = hexByte - 'A' + 10;
+        return true;
+    } else if (hexByte >= 'a' && hexByte <= 'f') {
+        *value = hexByte - 'a' + 10;
+        return true;
+    }
+
+    return false;
+}
+
+uint64_t TestState::hexToUInt64(const char* startHexByte, const char** endHexByte) {
+    const char* scratch;
+    if (endHexByte == NULL) {
+        endHexByte = &scratch;
+    }
+    if (startHexByte == NULL)
+        return 0;
+    uint64_t retval = 0;
+    if (startHexByte[0] == '0' &&  startHexByte[1] == 'x') {
+        startHexByte +=2;
+    }
+    *endHexByte = startHexByte + 16;
+
+    //FIXME overrun?
+    for (uint32_t i = 0; i < 16; ++i) {
+        uint8_t value;
+        if (!hexCharToUInt(startHexByte[i], &value)) {
+            *endHexByte = &startHexByte[i];
+            break;
+        }
+        retval = (retval << 4) + value;
+    }
+    return retval;
+}
+
+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) {
+            logImmediate = true;
+        }
+        if (strcmp(env, "TEST_LOG_ON_SUCCESS") == 0) {
+            logOnSuccess = true;
+        }
+        if (strcmp(env, "MallocStackLogging") == 0) {
+            checkForLeaks = true;
+        }
+        if (strcmp(env, "TEST_OUTPUT") == 0) {
+            if (strcmp(val, "BATS") == 0) {
+                output = BATS;
+            } else if (strcmp(val, "XCTest") == 0) {
+                output = XCTest;
+            }
+        }
+    });
+    if (output == BATS) {
+        printf("[BEGIN]");
+        if (checkForLeaks) {
+            printf(" MallocStackLogging=1 MallocDebugReport=none");
+        }
+        forEachEnvVar(environ, [this](const char* env, const char* val) {
+            if ((strncmp(env, "DYLD_", 5) == 0) || (strncmp(env, "TEST_", 5) == 0)) {
+                printf(" %s=%s", env, val);
+            }
+        });
+        printf(" %s", testName);
+        for (uint32_t i = 1; i < NXArgc; ++i) {
+            printf(" %s", NXArgv[i]);
+        }
+        printf("\n");
+    }
+}
+
+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);
+            }
+        }
+        sState.store(state);
+    }
+    assert(sState != nullptr);
+    return sState;
+}
+
+__attribute__((noreturn))
+void TestState::runLeaks(void) {
+    auto testState = TestState::getState();
+    pid_t pid = getpid();
+    char pidString[32];
+    sprintf(&pidString[0], "%d", pid);
+    if (getuid() != 0) {
+        printf("Insufficient priviledges, skipping Leak check: %s\n", testState->testName);
+        exit(0);
+    }
+    const char *args[] = { pidString, NULL };
+    // We do this instead of using a dispatch_semaphore to prevent priority inversions
+    __block dispatch_data_t leaksOutput = NULL;
+    _process process;
+    process.set_executable_path("/usr/bin/leaks");
+    process.set_args(args);
+    process.set_stdout_handler(^(int fd) {
+        ssize_t size = 0;
+        do {
+            char buffer[16384];
+            size = read(fd, &buffer[0], 16384);
+            if (size == -1) { break; }
+            dispatch_data_t data = dispatch_data_create(&buffer[0], size, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
+            if (!leaksOutput) {
+                leaksOutput = data;
+            } else {
+                leaksOutput = dispatch_data_create_concat(leaksOutput, data);
+            }
+        } while (size > 0);
+    });
+    process.set_exit_handler(^(pid_t pid) {
+        int status = 0;
+        (void)waitpid(pid, &status, 0);
+
+        int exitStatus = WEXITSTATUS(status);
+        if (exitStatus == 0) {
+            PASS("No leaks");
+        } else {
+            if (leaksOutput) {
+                const void * buffer;
+                size_t size;
+                __unused dispatch_data_t map = dispatch_data_create_map(leaksOutput, &buffer, &size);
+                FAIL("Found Leaks:\n\n%s", buffer);
+            }
+        }
+    });
+
+    testState->checkForLeaks = false;
+    (void)process.launch();
+    exit(0);
+}
+
+void TestState::_PASSV(const char* file, unsigned line, const char* format, va_list args) {
+    if (output == None) {
+        exit(0);
+    }
+    if (checkForLeaks) {
+        runLeaks();
+    } else {
+        _IOlock.withLock([this,&format,&args,&file,&line](){
+            if (output == Console) {
+                printf("[\033[0;32mPASS\033[0m] %s: ", testName);
+                vprintf(format, args);
+                printf("\n");
+                if (logOnSuccess && logs.count()) {
+                    printf("[\033[0;33mLOG\033[0m]\n");
+                    for (const auto& log : logs) {
+                        printf("\t%s\n", log);
+                    }
+                }
+            } else if (output == BATS)  {
+                printf("[PASS] %s: ", testName);
+                vprintf(format, args);
+                printf("\n");
+                if (logOnSuccess && logs.count()) {
+                    printf("[LOG]\n");
+                    for (const auto& log : logs) {
+                        printf("\t%s\n", log);
+                    }
+                }
+            } else if (output == XCTest) {
+                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\">");
+                printf("<dict>");
+                printf("<key>PASS</key><true />");
+                printf("</dict>");
+                printf("</plist>");
+            }
+        });
+        exit(0);
+    }
+}
+
+void _PASS(const char* file, unsigned line, const char* format, ...)  {
+    va_list args;
+    va_start (args, format);
+    TestState::getState()->_PASSV(file, line, format, args);
+    va_end (args);
+}
+
+void TestState::_FAILV(const char* file, unsigned line, const char* format, va_list args) {
+    if (output == None) {
+        exit(0);
+    }
+    _IOlock.withLock([this,&format,&args,&file,&line](){
+        if (output == Console) {
+            printf("[\033[0;31mFAIL\033[0m] %s: ", testName);
+            vprintf(format, args);
+            printf("\n");
+            printf("[\033[0;33mLOG\033[0m]\n");
+            if (logs.count()) {
+                for (const auto& log : logs) {
+                    printf("\t%s\n", log);
+                }
+            }
+        } else if (output == BATS)  {
+            printf("[FAIL] %s: ", testName);
+            vprintf(format, args);
+            printf("\n");
+            if (logs.count()) {
+                printf("[LOG]\n");
+                for (const auto& log : logs) {
+                    printf("\t%s\n", log);
+                }
+            }
+        } 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\">");
+            printf("<dict>");
+            printf("<key>PASS</key><false />");
+            printf("<key>FILE</key><string>%s</string>", file);
+            printf("<key>LINE</key><integer>%u</integer>", line);
+            vasprintf(&buffer, format, args);
+            printf("<key>INFO</key><string>%s</string>", buffer);
+            free(buffer);
+            printf("</dict>");
+            printf("</plist>");
+        }
+    });
+    exit(0);
+}
+
+void _FAIL(const char* file, unsigned line, const char* format, ...)  {
+    va_list args;
+    va_start (args, format);
+    TestState::getState()->_FAILV(file, line, format, args);
+    va_end (args);
+}
+
+void TestState::_LOGV(const char* file, unsigned line, const char* format, va_list args) {
+    _IOlock.withLock([this,&format,&args](){
+        if (logImmediate) {
+            vprintf(format, args);
+            printf("\n");
+        } else {
+            char *str;
+            vasprintf(&str, format, args);
+            logs.push_back(str);
+        }
+    });
+}
+
+void _LOG(const char* file, unsigned line, const char* format, ...)  {
+    va_list args;
+    va_start (args, format);
+    TestState::getState()->_LOGV(file, line, format, args);
+    va_end (args);
+}
+
+void _TIMEOUT(const char* file, unsigned line, uint64_t seconds) {
+    _LOG(file, line, "Registering %llu second test timeout", seconds);
+    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
+    dispatch_time_t milestone = dispatch_time(DISPATCH_WALLTIME_NOW, seconds * NSEC_PER_SEC);
+    dispatch_source_set_timer(source, milestone, 0, 0);
+    dispatch_source_set_event_handler(source, ^{
+        FAIL("Test timed out");
+    });
+    dispatch_resume(source);
+}
diff --git a/testing/lib/test_support.exp b/testing/lib/test_support.exp
new file mode 100644 (file)
index 0000000..83a6dbf
--- /dev/null
@@ -0,0 +1,6 @@
+__PASS
+__FAIL
+__LOG
+__TIMEOUT
+__ZN8_process*
+__process*
diff --git a/testing/nocr/execserver.defs b/testing/nocr/execserver.defs
deleted file mode 100644 (file)
index e528df4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <mach/mach_exc.defs>
diff --git a/testing/nocr/nocr.c b/testing/nocr/nocr.c
deleted file mode 100644 (file)
index 74c1e88..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-#include "execserverServer.h"
-
-#include <mach/mach.h>
-#include <mach/vm_map.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <err.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <dispatch/dispatch.h>
-#include <errno.h>
-#include <signal.h>
-#include <libproc.h>
-#include <System/sys/reason.h>
-#include <System/sys/proc_info.h>
-
-
-
-static pid_t                sChildPid;
-static dispatch_semaphore_t sServerRunning;
-static bool                 sChildCrashed = false;
-static bool                 sChildTerminatedByDyld = false;
-
-/*
- * setup exception handling port for EXC_CRASH and EXC_CORPSE_NOTIFY.
- * runs mach_msg_server once for receiving exception messages from kernel.
- */
-static void* serverCode(void* arg)
-{
-       mach_port_t exception_port;
-
-       kern_return_t kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port);
-       if (kret != KERN_SUCCESS)
-               errx(1, "mach_port_allocate: %s (%d)", mach_error_string(kret), kret);
-
-       kret = mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND);
-       if (kret != KERN_SUCCESS)
-               errx(1, "mach_port_insert_right: %s (%d)", mach_error_string(kret), kret);
-
-       kret = task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exception_port,
-                                       EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0);
-       if (kret != KERN_SUCCESS)
-               errx(1, "task_set_exception_ports: %s (%d)", mach_error_string(kret), kret);
-
-       dispatch_semaphore_signal(sServerRunning);
-
-       kret = mach_msg_server(mach_exc_server, MACH_MSG_SIZE_RELIABLE, exception_port, 0);
-       if (kret != KERN_SUCCESS)
-               errx(1, "mach_msg_server: %s (%d)", mach_error_string(kret), kret);
-
-       return NULL;
-}
-
-
-static void childDied(int sig)
-{
-    struct proc_exitreasoninfo info;
-    bzero(&info, sizeof(info));
-    uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE];
-    bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE);
-    info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
-    info.eri_kcd_buf = (user_addr_t)packReasonData;
-    //fprintf(stderr, "info=%p\n", &info);
-    if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) {
-        printf("bad return size from proc_pidinfo()\n");
-        return;
-    }
-    sChildTerminatedByDyld = (info.eri_namespace == OS_REASON_DYLD);
- }
-
-
-int main(int argc, const char* argv[])
-{
-       if ( argc < 2 ) {
-               fprintf(stderr, "usage: nocr [-require_crash] prog args...\n");
-               return EXIT_FAILURE;
-       }
-    unsigned progArgIndex = 1;
-    bool requireCrash = false;
-    const char* testName = NULL;
-    if ( strcmp(argv[1], "-require_crash") == 0 ) {
-        progArgIndex = 2;
-        requireCrash = true;
-        testName = getenv("NOCR_TEST_NAME");
-        if ( testName )
-            printf("[BEGIN] %s\n", testName);
-    }
-
-    signal(SIGCHLD, childDied);
-
-       sServerRunning = dispatch_semaphore_create(0);
-
-    // start up thread for mach server which handles mach exception ports
-       pthread_t serverThread;
-       int result = pthread_create(&serverThread, NULL, serverCode, NULL);
-    if ( result )
-        err(EXIT_FAILURE, "pthread_create");
-
-    // wait until server is up before starting child
-       dispatch_semaphore_wait(sServerRunning, DISPATCH_TIME_FOREVER);
-
-    // fork and exec child
-       sChildPid = fork();
-       if ( sChildPid < 0 )
-               err(EXIT_FAILURE, "fork");
-       if ( sChildPid == 0 ) {
-               // child side
-               result = execvp(argv[progArgIndex], (char**)&argv[progArgIndex]);
-        err(EXIT_FAILURE, "exec(\"%s\",...)", argv[progArgIndex]);
-       }
-
-    // wait for child to finish (including crash)
-    int status;
-       int waitResult;
-       int childResult = EXIT_FAILURE;
-       do {
-        waitResult = waitpid(sChildPid, &status, 0);
-    } while ( (waitResult == -1) && (errno == EINTR) );
-    if ( waitResult != -1 ) {
-        if ( WIFEXITED(status) ) {
-                       childResult = WEXITSTATUS(status);
-        }
-    }
-
-    if ( requireCrash ) {
-        if ( testName ) {
-            if ( sChildCrashed || sChildTerminatedByDyld )
-                printf("[PASS] %s\n", testName);
-            else
-                printf("[FAIL] %s\n", testName);
-        }
-        return sChildCrashed ? EXIT_SUCCESS : EXIT_FAILURE;
-    }
-    else
-        return childResult;
-}
-
-
-
-
-// Mach exception handler routines needed by execserverServer.c
-
-kern_return_t
-catch_mach_exception_raise(mach_port_t exception_port,
-                           mach_port_t thread,
-                           mach_port_t task,
-                           exception_type_t exception,
-                           mach_exception_data_t code,
-                           mach_msg_type_number_t codeCnt)
-{
-       //fprintf(stderr, "child crashed\n");
-    sChildCrashed = true;
-    return KERN_SUCCESS;
-}
-
-kern_return_t
-catch_mach_exception_raise_state(mach_port_t exception_port,
-                                 exception_type_t exception,
-                                 const mach_exception_data_t code,
-                                 mach_msg_type_number_t codeCnt,
-                                 int * flavor,
-                                 const thread_state_t old_state,
-                                 mach_msg_type_number_t old_stateCnt,
-                                 thread_state_t new_state,
-                                 mach_msg_type_number_t * new_stateCnt)
-{
-       errx(1, "Unsupported catch_mach_exception_raise_state");
-       return KERN_NOT_SUPPORTED;
-}
-
-kern_return_t
-catch_mach_exception_raise_state_identity(mach_port_t exception_port,
-                                          mach_port_t thread,
-                                          mach_port_t task,
-                                          exception_type_t exception,
-                                          mach_exception_data_t code,
-                                          mach_msg_type_number_t codeCnt,
-                                          int * flavor,
-                                          thread_state_t old_state,
-                                          mach_msg_type_number_t old_stateCnt,
-                                          thread_state_t new_state,
-                                          mach_msg_type_number_t * new_stateCnt)
-{
-       errx(1, "Unsupported catch_mach_exception_raise_state_identity");
-       return KERN_NOT_SUPPORTED;
-}
-
-
-
diff --git a/testing/nocr/nocr.cpp b/testing/nocr/nocr.cpp
new file mode 100644 (file)
index 0000000..dd08d40
--- /dev/null
@@ -0,0 +1,35 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <mach/mach.h>
+
+#include "test_support.h"
+
+extern const char**  environ;
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    if ( argc < 2 ) {
+        fprintf(stderr, "usage: nocr prog args...\n");
+        return EXIT_FAILURE;
+    }
+    
+    _process process;
+    process.set_executable_path(argv[1]);
+    process.set_args(&argv[2]);
+    process.set_env(environ);
+    process.set_crash_handler(^(task_t task) {
+        exit(0);
+    });
+    process.set_exit_handler(^(pid_t pid) {
+        int status = 0;
+        (void)waitpid(pid, &status, 0);
+        
+        // Only call exit if the child exited normally, otherwise keep running to consume the crash
+        if (WIFEXITED(status)) {
+            exit(0);
+        }
+    });
+    process.launch();
+    dispatch_main();
+}
index 4c58f79bf02bf447d5abb6fec4f6f3a05876badf..4d9ff342da0fbfe55f443e3fd717197004b4b555 100644 (file)
@@ -1,5 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/hideyhole
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/hideyhole/libfoo1.dylib -install_name /bad/path/libfoo1.dylib
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/hideyhole/libfoo2.dylib -install_name /bad/path2/libfoo2.dylib
 // BUILD:  $CC main.c            -o $BUILD_DIR/LC_DYLD_ENV-DYLD_LIBRARY_PATH-main1.exe $BUILD_DIR/hideyhole/libfoo1.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@executable_path/hideyhole
 #include <stdlib.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
+
 /// Test that main executable's LC_DYLD_ENVIRONMENT can set DYLD_LIBRARY_PATH with @executable_path or @loader_path relative paths
 
 extern char* __progname;
 
-int main()
-{
-    printf("[BEGIN] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     void*h = dlopen("/other/path/libfoo2.dylib", 0);
 
        if ( h != NULL )
-        printf("[PASS] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
+        PASS("Success");
     else
-        printf("[FAIL] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
-
-       return 0;
+        FAIL("Could not load libfoo2.dylib via LC_DYLD_ENVIRONMENT -> DYLD_LIBRARY_PATH");
 }
 
index 5c870c46a3aeb5bff7f0b2957c79a207512f0081..f2597215c8eeca6db405ca7c74741fff01d73feb 100644 (file)
 #include <dlfcn.h>
 #include <mach-o/dyld.h>
 
+#include "test_support.h"
 
-int main(int arg, const char* argv[])
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     const char* path = argv[1];
-    printf("[BEGIN] NSAddImage-basic %s\n", path);
 
        const struct mach_header* mh = NSAddImage(path, NSADDIMAGE_OPTION_WITH_SEARCHING);
        if ( mh == NULL )
-        printf("[FAIL] NSAddImage-basic %s\n", path);
+        FAIL("Could not load \"%s\"", path);
        else
-        printf("[PASS] NSAddImage-basic %s\n", path);
-
-       return 0;
+        PASS("Success");
 }
 
diff --git a/testing/test-cases/NSAddImage-fail.dtest/main.c b/testing/test-cases/NSAddImage-fail.dtest/main.c
deleted file mode 100644 (file)
index 74bf245..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-// BUILD_ONLY: MacOSX
-
-// BUILD:  $CC main.c            -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations
-
-// RUN:  ./NSAddImage-fail.exe return
-// RUN:  NOCR_TEST_NAME="NSAddImage-fail expected abort" $REQUIRE_CRASH  ./NSAddImage-fail.exe abort
-
-
-
-#include <stdio.h>
-#include <string.h>
-#include <dlfcn.h>
-#include <mach-o/dyld.h>
-
-
-int main(int argc, const char* argv[])
-{
-    const char* arg = argv[1];
-
-    if ( strcmp(arg, "return") == 0 ) {
-        printf("[BEGIN] NSAddImage-fail %s\n", arg);
-        const struct mach_header* mh = NSAddImage("/xqz/42/libnotfound.xxx", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
-        if ( mh == NULL )
-            printf("[PASS] NSAddImage-fail %s\n", arg);
-        else
-            printf("[FAIL] NSAddImage-fail %s\n", arg);
-    }
-    else {
-        // run with nocr which print BEGIN/PASS/FAIL
-        NSAddImage("/xqz/42/libnotfound.xxx", 0);
-    }
-
-       return 0;
-}
-
diff --git a/testing/test-cases/NSAddImage-fail.dtest/main.cpp b/testing/test-cases/NSAddImage-fail.dtest/main.cpp
new file mode 100644 (file)
index 0000000..0dada7d
--- /dev/null
@@ -0,0 +1,93 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CXX main.cpp          -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./NSAddImage-fail.exe return
+// RUN:  ./NSAddImage-fail.exe abort
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <libproc.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+#include <System/sys/reason.h>
+#include <System/sys/proc_info.h>
+#include <System/kern/kern_cdata.h>
+
+#include "test_support.h"
+
+//FIXME:
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    const char* arg = argv[1];
+
+    if ( strcmp(arg, "return") == 0 ) {
+        const struct mach_header* mh = NSAddImage("/xqz/42/libnotfound.xxx", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
+        if ( mh == NULL )
+            PASS("Success");
+        else
+            FAIL("Got mh non-existent image");
+    } else if (strcmp(arg, "abort-child") == 0) {
+        // run with nocr which print BEGIN/PASS/FAIL
+        NSAddImage("/xqz/42/libnotfound.xxx", 0);
+    } else if (strcmp(arg, "abort") == 0) {
+        _process process;
+        process.set_executable_path(RUN_DIR "/NSAddImage-fail.exe");
+        const char* args[] = {"abort-child", NULL };
+        process.set_args(args);
+        const char* env[] = { "TEST_OUTPUT=None", NULL};
+        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) {
+                FAIL("Could not read corpse data");
+            }
+            kcdata_iter_t autopsyData = kcdata_iter((void*)corpse_data, corpse_size);
+            if (!kcdata_iter_valid(autopsyData)) {
+                FAIL("Corpse Data Invalid");
+            }
+            kcdata_iter_t exitReasonData = kcdata_iter_find_type(autopsyData, EXIT_REASON_SNAPSHOT);
+            if (!kcdata_iter_valid(exitReasonData)) {
+                FAIL("Could not find exit data");
+            }
+            struct exit_reason_snapshot *ers = (struct exit_reason_snapshot *)kcdata_iter_payload(exitReasonData);
+
+            if ( ers->ers_namespace != OS_REASON_DYLD ) {
+                FAIL("eri_namespace (%d) != OS_REASON_DYLD", ers->ers_namespace);
+            }
+            if ( ers->ers_code != DYLD_EXIT_REASON_OTHER ) {
+                FAIL("eri_code (%lld) != DYLD_EXIT_REASON_OTHER", ers->ers_code);
+            }
+            kcdata_iter_t iter = kcdata_iter((void*)corpse_data, corpse_size);
+
+            KCDATA_ITER_FOREACH(iter) {
+                if (kcdata_iter_type(iter) == KCDATA_TYPE_NESTED_KCDATA) {
+                    kcdata_iter_t nestedIter = kcdata_iter(kcdata_iter_payload(iter), kcdata_iter_size(iter));
+                    if ( kcdata_iter_type(nestedIter) != KCDATA_BUFFER_BEGIN_OS_REASON ){
+                        return;
+                    }
+                    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");
+                    }
+                    const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter);
+
+                    if ( dyldInfo->version != 1 ) {
+                        FAIL("dyld payload is not version 1");
+                    }
+                    PASS("Success");
+                }
+            }
+            FAIL("Did not find EXIT_REASON_USER_PAYLOAD");
+        });
+        process.launch();
+        dispatch_main();
+    }
+       return 0;
+}
+
index db7ca4b49a4914e2f408d286f18ad62678c22010..0a24cb854b1e7f7961f640abf363691906cbd3a2 100644 (file)
 #include <dlfcn.h>
 #include <mach-o/dyld.h>
 
+#include "test_support.h"
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] NSAddImage-loaded\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // verify value is returned for image already loaded
     const struct mach_header* mh = NSAddImage("/usr/lib/libSystem.B.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
     if ( mh == NULL )
-        printf("[FAIL] NSAddImage-loaded\n");
+        FAIL("Could not find mh for libSystem.B.dylib");
 
     // verify existing dylib is not loaded if it is not already loaded
     mh = NSAddImage("/usr/lib/libz.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
     if ( mh != NULL )
-        printf("[FAIL] NSAddImage-loaded\n");
-
-    printf("[PASS] NSAddImage-loaded\n");
+        FAIL("Found mh for unloaded dylib libz.dylib");
 
-       return 0;
+    PASS("Success");
 }
 
index ddd9c74c8a3a4f877a52c358c8e96b2fa370acfd..70fa3f024b88e7440b243c71056e4863383f62a6 100644 (file)
 #include <dlfcn.h>
 #include <mach-o/dyld.h>
 
-extern struct mach_header __dso_handle;
+#include "test_support.h"
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] NSAddressOfSymbol-basic\n");
+extern struct mach_header __dso_handle;
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
     if ( sym == NULL ) {
-        printf("[FAIL] NSAddressOfSymbol-basic can't find main\n");
-        return 0;
+        FAIL("can't find main");
     }
     void* mainAddr = NSAddressOfSymbol(sym);
     if ( mainAddr != &main ) {
-        printf("[FAIL] NSAddressOfSymbol-basic address returned %p is not &main=%p\n", mainAddr, &main);
-        return 0;
+        FAIL("address returned %p is not &main=%p", mainAddr, &main);
     }
 
     // verify NULL works
     if ( NSAddressOfSymbol(NULL) != NULL ) {
-        printf("[FAIL] NSAddressOfSymbol-basic NULL not handle\n");
-        return 0;
+        FAIL("NULL not handle");
     }
 
-    printf("[PASS] NSAddressOfSymbol-basic\n");
-       return 0;
+    PASS("Success");
 }
 
index 49341b38fff70a4f9bd99014d4637ee1cea376c6..b6eb2e043f6005833abe1c33902a788a677659d1 100644 (file)
 #include <dlfcn.h>
 #include <mach-o/dyld.h>
 
+#include "test_support.h"
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     const char* path = argv[1];
 
-       NSObjectFileImage ofi;
-       if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
-               printf("[FAIL] NSCreateObjectFileImageFromFile failed\n");
-               return 0;
-       }
-       
-       NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
-       if ( mod == NULL ) {
-               printf("[FAIL] NSLinkModule failed\n");
-               return 0;
-       }
+    NSObjectFileImage ofi;
+    if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
+        FAIL("NSCreateObjectFileImageFromFile failed");
+    }
+
+    NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
+    if ( mod == NULL ) {
+        FAIL("NSLinkModule failed");
+    }
        
-       NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
-       if ( sym == NULL ) {
-               printf("[FAIL] NSLookupSymbolInModule failed\n");
-               return 0;
-       }
+    NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
+    if ( sym == NULL ) {
+        FAIL("NSLookupSymbolInModule failed");
+    }
 
-       void* func = NSAddressOfSymbol(sym);
-       if ( func == NULL ) {
-               printf("[FAIL] NSAddressOfSymbol failed\n");
-               return 0;
-       }
+    void* func = NSAddressOfSymbol(sym);
+    if ( func == NULL ) {
+        FAIL("NSAddressOfSymbol failed");
+    }
 
     Dl_info info;
     if ( dladdr(func, &info) == 0 ) {
-        printf("[FAIL] dladdr(&p, xx) failed");
-               return 0;
+        FAIL("dladdr(&p, xx) fail");
     }
-    //printf("_fooInBundle found in %s\n", info.dli_fname);
+    LOG("_fooInBundle found in %s", info.dli_fname);
 
-       if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
-               printf("[FAIL] NSUnLinkModule failed\n");
-               return 0;
-       }
+    if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+        FAIL("NSUnLinkModule failed");
+    }
 
     if ( dladdr(func, &info) != 0 ) {
-        printf("[FAIL] dladdr(&p, xx) found but should not have\n");
-               return 0;
+        FAIL("dladdr(&p, xx) found but should not have");
     }
 
        if ( !NSDestroyObjectFileImage(ofi) ) {
-               printf("[FAIL] NSDestroyObjectFileImage failed\n");
-               return 0;
-       }
+        FAIL("NSDestroyObjectFileImage failed");
+    }
 
-    printf("[PASS] NSCreateObjectFileImageFromFile-basic\n");
-       return 0;
+    PASS("Success");
 }
 
index 54c676d7d081224080e25b97dd05cd8bfd14e6d2..d7e6f9822804ef86ea65fda0379ecca707749471 100644 (file)
 #include <mach-o/dyld.h>
 #include <vector>
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n");
+#include "test_support.h"
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     const char* path = argv[1];
 
     std::vector<NSObjectFileImage> ofis;
     for (unsigned i = 0; i != 32; ++i) {
-               NSObjectFileImage ofi;
-               if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
-                       printf("[FAIL] NSCreateObjectFileImageFromFile failed\n");
-                       return 0;
-               }
-               ofis.push_back(ofi);
-       }
-       
-       for (unsigned i = 0; i != 32; ++i) {
-               NSObjectFileImage ofi = ofis[i];
-               NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
-               if ( mod == NULL ) {
-                       printf("[FAIL] NSLinkModule failed\n");
-                       return 0;
-               }
-               
-               NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
-               if ( sym == NULL ) {
-                       printf("[FAIL] NSLookupSymbolInModule failed\n");
-                       return 0;
-               }
-
-               void* func = NSAddressOfSymbol(sym);
-               if ( func == NULL ) {
-                       printf("[FAIL] NSAddressOfSymbol failed\n");
-                       return 0;
-               }
-
-           Dl_info info;
-           if ( dladdr(func, &info) == 0 ) {
-               printf("[FAIL] dladdr(&p, xx) failed");
-                       return 0;
-           }
-           //printf("_fooInBundle found in %s\n", info.dli_fname);
-
-               if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
-                       printf("[FAIL] NSUnLinkModule failed\n");
-                       return 0;
-               }
-
-           if ( dladdr(func, &info) != 0 ) {
-               printf("[FAIL] dladdr(&p, xx) found but should not have\n");
-                       return 0;
-           }
-       }
-
-       for (unsigned i = 0; i != 32; ++i) {
-               NSObjectFileImage ofi = ofis[i];
-               if ( !NSDestroyObjectFileImage(ofi) ) {
-                       printf("[FAIL] NSDestroyObjectFileImage failed\n");
-                       return 0;
-               }
-       }
-
-    printf("[PASS] NSCreateObjectFileImageFromFile-basic\n");
-       return 0;
+        NSObjectFileImage ofi;
+        if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
+            FAIL("NSCreateObjectFileImageFromFile failed");
+        }
+        ofis.push_back(ofi);
+    }
+
+    for(unsigned i = 0; i != 32; ++i) {
+    NSObjectFileImage ofi = ofis[i];
+    NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
+        if ( mod == NULL ) {
+            FAIL("NSLinkModule failed");
+        }
+
+        NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
+        if ( sym == NULL ) {
+            FAIL("NSLookupSymbolInModule failed");
+        }
+
+        void* func = NSAddressOfSymbol(sym);
+        if ( func == NULL ) {
+            FAIL("NSAddressOfSymbol failed");
+        }
+
+        Dl_info info;
+        if ( dladdr(func, &info) == 0 ) {
+            FAIL("dladdr(&p, xx) fail");
+        }
+        LOG("_fooInBundle found in %s", info.dli_fname);
+
+        if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+            FAIL("NSUnLinkModule failed");
+        }
+
+        if ( dladdr(func, &info) != 0 ) {
+            FAIL("dladdr(&p, xx) found but should not have");
+        }
+    }
+
+    for (unsigned i = 0; i != 32; ++i) {
+        NSObjectFileImage ofi = ofis[i];
+        if ( !NSDestroyObjectFileImage(ofi) ) {
+            FAIL("NSDestroyObjectFileImage failed");
+        }
+    }
+
+    PASS("Success");
 }
 
index ca41cb75f728f533605b857a37f78e36b2d97b6c..b9944ffff414404fa827c9d26b970753fe6f5216 100644 (file)
 #include <dlfcn.h>
 #include <mach-o/dyld.h>
 
+#include "test_support.h"
 
 static void checkBundle(const char* path, bool unlinkBeforeDestroy)
 {
        int fd = open(path, O_RDONLY, 0);
        if ( fd == -1 ) {
-               printf("[FAIL] open(%s) failed", path);
-               exit(0);
+               FAIL("open(%s) failed", path);
        }
 
        struct stat stat_buf;
        if ( fstat(fd, &stat_buf) == -1) {
-               printf("[FAIL] fstat() failed\n");
-               exit(0);
+               FAIL("fstat() failed");
        }
 
        void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
        if ( loadAddress == ((void*)(-1)) ) {
-               printf("[FAIL] mmap() failed\n");
-               exit(0);
+               FAIL("mmap() failed");
        }
 
        close(fd);
 
        NSObjectFileImage ofi;
        if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) {
-               printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n");
-               exit(0);
+               FAIL("NSCreateObjectFileImageFromMemory failed");
        }
 
        NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
        if ( mod == NULL ) {
-               printf("[FAIL] NSLinkModule failed\n");
-               exit(0);
+               FAIL("NSLinkModule failed");
        }
        
    if ( !unlinkBeforeDestroy ) {
         // API lets you destroy ofi and NSModule lives on
         if ( !NSDestroyObjectFileImage(ofi) ) {
-            printf("[FAIL] NSDestroyObjectFileImage failed\n");
-            exit(0);
+            FAIL("NSDestroyObjectFileImage failed");
         }
     }
 
        NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
        if ( sym == NULL ) {
-               printf("[FAIL] NSLookupSymbolInModule failed\n");
-               exit(0);
+               FAIL("NSLookupSymbolInModule failed");
        }
 
        void* func = NSAddressOfSymbol(sym);
        if ( func == NULL ) {
-               printf("[FAIL] NSAddressOfSymbol failed\n");
-               exit(0);
+               FAIL("NSAddressOfSymbol failed");
        }
 
     Dl_info info;
     if ( dladdr(func, &info) == 0 ) {
-        printf("[FAIL] dladdr(&p, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&p, xx) failed");
     }
-    //printf("_fooInBundle found in %s\n", info.dli_fname);
+    LOG("_fooInBundle found in %s", info.dli_fname);
 
     if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
-            printf("[FAIL] NSUnLinkModule failed\n");
-            exit(0);
+            FAIL("NSUnLinkModule failed");
     }
 
     if ( dladdr(func, &info) != 0 ) {
-        printf("[FAIL] dladdr(&p, xx) found but should not have\n");
-        exit(0);
+        FAIL("dladdr(&p, xx) found but should not have");
     }
 
     if ( unlinkBeforeDestroy ) {
         if ( !NSDestroyObjectFileImage(ofi) ) {
-            printf("[FAIL] NSDestroyObjectFileImage failed\n");
-            exit(0);
+            FAIL("NSDestroyObjectFileImage failed");
         }
     }
 }
 
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] NSCreateObjectFileImageFromMemory-basic\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     checkBundle(argv[1], true);
     checkBundle(argv[1], false);
 
@@ -110,7 +96,6 @@ int main(int argc, const char* argv[])
     for (unsigned i = 0; i != 255; ++i)
       checkBundle(argv[1], false);
 
-    printf("[PASS] NSCreateObjectFileImageFromMemory-basic\n");
-    return 0;
+    PASS("Success");
 }
 
index 1adab5834c1c01549d5572d9625b5e79e6458a83..7cc1d46deddd2845129029d11aa95666ac451cc3 100644 (file)
 #include <dlfcn.h>
 #include <mach-o/dyld.h>
 
-extern struct mach_header __dso_handle;
+#include "test_support.h"
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] NSLookupSymbolInImage-basic\n");
+extern struct mach_header __dso_handle;
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // verify known symbol works
     NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
     if ( sym == NULL ) {
-         printf("[FAIL] NSLookupSymbolInImage-basic _main\n");
-         return 0;
+         FAIL("Did not find symnbol _main");
     }
 
     // verify mode where NSLookupSymbolInImage() returns NULL if symbol not found
     sym = NSLookupSymbolInImage(&__dso_handle, "_42hhg", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
     if ( sym != NULL ) {
-         printf("[FAIL] NSLookupSymbolInImage-basic _42hhg\n");
-         return 0;
+         FAIL("Did not find symnbol _42hhg");
     }
 
     // Note: NSLookupSymbolInImage is documented to abort if symbol not found and NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR not used,
     // but dyld 2 just returned NULL, so no need to test that.
 
-    printf("[PASS] NSLookupSymbolInImage-basic\n");
-       return 0;
+    PASS("Success");
 }
 
index 33f114b8cefa787e6f3494fa5da2ea7e2312c9d5..d9ebb7317a2a94c365c9ab7c8d40b94a2221272f 100644 (file)
@@ -11,6 +11,8 @@
 
 #import <Foundation/NSObject.h>
 
+#include "test_support.h"
+
 // All the libraries have a copy of NSString
 @interface NSString : NSObject
 @end
@@ -57,8 +59,6 @@ Class getMainNSString() {
   return (Class)&OBJC_CLASS_$_NSString;
 }
 
-extern int printf(const char*, ...);
-
 extern id objc_getClass(const char *name);
 
 // Get the NSString from liblinked1.dylib
@@ -72,20 +72,18 @@ static bool gotNSStringLinked = false;
 static bool gotNSStringLinked2 = false;
 static bool gotNSStringFoundation = false;
 
-bool testDuplicate(const char* className, Class nonCacheClass) {
+void testDuplicate(const char* className, Class nonCacheClass) {
   // Walk all the implementations of the class.  There should be 2.  One in the executable and one in the shared cache
   // The shared cache one should be returned first.
 
   // The objc runtime should have chosen the Foundation one as the canonical definition.
   Class objcRuntimeClassImpl = (Class)objc_getClass(className);
   if (objcRuntimeClassImpl == nil) {
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s not found via runtime\n", className);
-    return false;
+    FAIL("class %s not found via runtime", className);
   }
 
   if (objcRuntimeClassImpl == nonCacheClass) {
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s from runtime should not match main exexutable\n", className);
-    return false;
+    FAIL("class %s from runtime should not match main exexutable", className);
   }
 
   __block bool foundSharedCacheImpl = false;
@@ -98,60 +96,43 @@ bool testDuplicate(const char* className, Class nonCacheClass) {
     // We should walk these in the order Foundation, main exe
     if (!foundSharedCacheImpl) {
       if (classPtr != objcRuntimeClassImpl) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s should have come from Foundation\n", className);
-        *stop = true;
-        return;
+        FAIL("Optimized class %s should have come from Foundation", className);
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s isLoaded should have been set on Foundation\n", className);
-        *stop = true;
-        return;
+        FAIL("Optimized class %s isLoaded should have been set on Foundation", className);
       }
       foundSharedCacheImpl = true;
       return;
     }
     if (!foundMainExecutableImpl) {
       if (classPtr != nonCacheClass) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s should have come from main executable\n", className);
-        *stop = true;
-        return;
+        FAIL("Optimized class %s should have come from main executable", className);
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s isLoaded should have been set on main executable\n", className);
-        *stop = true;
-        return;
+        FAIL("Optimized class %s isLoaded should have been set on main executable", className);
       }
       foundMainExecutableImpl = true;
       return;
     }
 
     foundTooManyClasses = true;
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s found somewhere other than main executable and Foundation\n", className);
-    *stop = true;
-    return;
+    FAIL("class %s found somewhere other than main executable and Foundation", className);
   });
 
   if (!foundAnyClass) {
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s not found\n", className);
-    return false;
+    FAIL("class %s not found", className);
   }
 
   if (foundTooManyClasses) {
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s found too many times\n", className);
-    return false;
+    FAIL("class %s found too many times", className);
   }
 
   if (!foundSharedCacheImpl || !foundMainExecutableImpl) {
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s not found for shared cache or main executable\n", className);
-    return false;
+    FAIL("class %s not found for shared cache or main executable", className);
   }
-
-  return true;
 }
 
-int main() {
-  printf("[BEGIN] _dyld_for_each_objc_class-duplicates\n");
-
+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);
@@ -166,22 +147,18 @@ int main() {
       sawClass = true;
     });
     if (sawClass) {
-      printf("[FAIL] _dyld_for_each_objc_class-duplicates: dyld2 shouldn't see any classes\n");
-      return 0;
+      FAIL("dyld2 shouldn't see any classes");
     }
-    printf("[PASS] _dyld_for_each_objc_class-duplicates (dyld2 or no shared cache)\n");
-    return 0;
+    PASS("dyld2 or no shared cache)");
   }
 
   // Check that NSString comes from Foundation as the shared cache should win here.
   id runtimeNSString = objc_getClass("NSString");
   if ( (uint64_t)runtimeNSString < (uint64_t)sharedCacheStart ) {
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: NSString should have come from Foundation but instead was %p\n", runtimeNSString);
-    return 0;
+    FAIL("NSString should have come from Foundation but instead was %p", runtimeNSString);
   }
   if ( (uint64_t)runtimeNSString >= ((uint64_t)sharedCacheStart + sharedCacheLen) ) {
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: NSString should have come from Foundation but instead was %p\n", runtimeNSString);
-    return 0;
+    FAIL("NSString should have come from Foundation but instead was %p", runtimeNSString);
   }
 
   // Walk all the implementations of "NSString"
@@ -189,67 +166,49 @@ int main() {
     // We should walk these in the order Foundation, liblinked2, liblinked, main exe
     if (!gotNSStringFoundation) {
       if (classPtr != runtimeNSString) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from Foundation\n");
-        *stop = true;
-        return;
+        FAIL("Optimized NSString should have come from Foundation");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on Foundation\n");
-        *stop = true;
-        return;
+        FAIL("Optimized NSString isLoaded should have been set on Foundation");
       }
       gotNSStringFoundation = true;
       return;
     }
     if (!gotNSStringLinked2) {
       if (classPtr != getLinked2NSString()) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from liblinked2\n");
-        *stop = true;
-        return;
+        FAIL("Optimized NSString should have come from liblinked2");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on liblinked2\n");
-        *stop = true;
-        return;
+        FAIL("Optimized NSString isLoaded should have been set on liblinked2");
       }
       gotNSStringLinked2 = true;
       return;
     }
     if (!gotNSStringLinked) {
       if (classPtr != getLinked1NSString()) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from liblinked\n");
-        *stop = true;
-        return;
+        FAIL("Optimized NSString should have come from liblinked");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on liblinked\n");
-        *stop = true;
-        return;
+        FAIL("Optimized NSString isLoaded should have been set on liblinked");
       }
       gotNSStringLinked = true;
       return;
     }
     if (!gotNSStringMain) {
       if (classPtr != getMainNSString()) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from main exe\n");
-        *stop = true;
-        return;
+        FAIL("Optimized NSString should have come from main exe");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on main exe\n");
-        *stop = true;
-        return;
+        FAIL("Optimized NSString isLoaded should have been set on main exe");
       }
       gotNSStringMain = true;
       return;
     }
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: Unexpected Optimized NSString\n");
-    return;
+    FAIL("Unexpected Optimized NSString");
   });
 
   if ( !gotNSStringFoundation || !gotNSStringLinked2 || !gotNSStringLinked || !gotNSStringMain) {
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: Failed to find all duplicates of 'NSString'\n");
-    return 0;
+    FAIL("Failed to find all duplicates of 'NSString'");
   }
 
   // Visit again, and return Foundation's NSString
@@ -259,23 +218,13 @@ int main() {
     *stop = true;
   });
   if (NSStringImpl != runtimeNSString) {
-    printf("[FAIL] _dyld_for_each_objc_class-duplicates: _dyld_for_each_objc_class should have returned NSString from Foundation\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have returned NSString from Foundation");
   }
 
-  if (!testDuplicate("NSDictionary", (Class)&OBJC_CLASS_$_NSDictionary))
-    return 0;
-
-  if (!testDuplicate("NSError", (Class)&OBJC_CLASS_$_NSError))
-    return 0;
-
-  if (!testDuplicate("NSSet", (Class)&OBJC_CLASS_$_NSSet))
-    return 0;
-
-  if (!testDuplicate("NSArray", (Class)&OBJC_CLASS_$_NSArray))
-    return 0;
-
-  printf("[PASS] _dyld_for_each_objc_class-duplicates\n");
+  testDuplicate("NSDictionary", (Class)&OBJC_CLASS_$_NSDictionary);
+  testDuplicate("NSError", (Class)&OBJC_CLASS_$_NSError);
+  testDuplicate("NSSet", (Class)&OBJC_CLASS_$_NSSet);
+  testDuplicate("NSArray", (Class)&OBJC_CLASS_$_NSArray);
 
-  return 0;
+  PASS("Success");
 }
index 709050b635251889fc860397e19871076c927083..cd029cb4d716f8b9648841024417342aaf5e9bc4 100644 (file)
@@ -1,9 +1,11 @@
 // BUILD_ONLY: MacOSX
 
-// BUILD:  $CC missing.m -dynamiclib -o $TEMP_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc -Wl,-fixup_chains
+// 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 $TEMP_DIR/libmissing.dylib -Wl,-fixup_chains
-// BUILD:  $CC main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak-chained.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $TEMP_DIR/libmissing.dylib -Wl,-fixup_chains -lc++
+// 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: $SKIP_INSTALL $BUILD_DIR/libmissing.dylib
 
 // RUN:  ./_dyld_for_each_objc_class-missing-weak-chained.exe
 
@@ -18,6 +20,8 @@
 
 #import <Foundation/Foundation.h>
 
+#include "test_support.h"
+
 // All the libraries have a copy of DyldClass
 @interface DyldClass : NSObject
 @end
@@ -43,8 +47,6 @@ Class getMainDyldMainClass() {
   return (Class)&OBJC_CLASS_$_DyldMainClass;
 }
 
-extern "C" int printf(const char*, ...);
-
 extern "C" id objc_getClass(const char *name);
 
 // Get the DyldClass from liblinked1.dylib
@@ -64,9 +66,7 @@ extern "C" id getMissingDyldClass();
 static bool gotDyldClassMain = false;
 static bool gotDyldClassLinked1 = false;
 
-int main() {
-  printf("[BEGIN] _dyld_for_each_objc_class-missing-weak-chained\n");
-
+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);
@@ -79,49 +79,41 @@ int main() {
       sawClass = true;
     });
     if (sawClass) {
-      printf("[FAIL] _dyld_for_each_objc_class: dyld2 shouldn't see any classes\n");
-      return 0;
+      FAIL("dyld2 shouldn't see any classes");
     }
-    printf("[PASS] _dyld_for_each_objc_class (dyld2 or no shared cache)\n");
-    return 0;
+    PASS("Success");
   }
 
   // Make sure libmissing.dylib is actually missing
   if (&getMissingDyldClass != nil) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: libmissing needs to be missing\n");
-    return 0;
+    FAIL("libmissing needs to be missing");
   }
 
   // DyldClass in liblinked1 should exist as its superclass is just NSObject
   if (getLinked1DyldClass() == nil) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked1 DyldClass should exist\n");
-    return 0;
+    FAIL("liblinked1 DyldClass should exist");
   }
 
   // DyldLinkedClass in liblinked1 should exist as its superclass is just NSObject
   if (getLinked1DyldLinkedClass() == nil) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked1 DyldLinkedClass should exist\n");
-    return 0;
+    FAIL("liblinked1 DyldLinkedClass should exist");
   }
 
   // DyldLinkedClass in liblinked2 should exist as its superclass is just NSObject
   if (getLinked2DyldLinkedClass() == nil) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked2 DyldLinkedClass should exist\n");
-    return 0;
+    FAIL("liblinked2 DyldLinkedClass should exist");
   }
 
   // Check that DyldMainClass comes main.exe as that is its only definition
   id runtimeDyldMainClass = objc_getClass("DyldMainClass");
   if (runtimeDyldMainClass != getMainDyldMainClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldMainClass should have come from main.exe\n");
-    return 0;
+    FAIL("DyldMainClass should have come from main.exe");
   }
 
   // Check that DyldClass comes liblinked1 as it should be missing from liblinked2
   id runtimeDyldClass = objc_getClass("DyldClass");
   if (runtimeDyldClass != getLinked1DyldClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from liblinked1\n");
-    return 0;
+    FAIL("DyldClass should have come from liblinked1");
   }
 
   // Check that DyldLinkedClass comes from liblinked2
@@ -129,8 +121,7 @@ int main() {
 #if 0
   id runtimeDyldLinkedClass = objc_getClass("DyldLinkedClass");
   if (runtimeDyldLinkedClass != getLinked2DyldLinkedClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldLinkedClass should have come from liblinked2\n");
-    return 0;
+    FAIL("DyldLinkedClass should have come from liblinked2");
   }
 #endif
 
@@ -138,44 +129,33 @@ int main() {
     // We should walk these in the order liblinked, main exe
     if (!gotDyldClassLinked1) {
       if (classPtr != getLinked1DyldClass()) {
-        printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from liblinked1\n");
-        *stop = true;
-        return;
+        FAIL("DyldClass should have come from liblinked1");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass isLoaded should have been set on liblinked1\n");
-        *stop = true;
-        return;
+        FAIL("DyldClass isLoaded should have been set on liblinked1");
       }
       gotDyldClassLinked1 = true;
       return;
     }
     if (!gotDyldClassMain) {
       if (classPtr != getMainDyldClass()) {
-        printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from main exe\n");
-        *stop = true;
-        return;
+        FAIL("DyldClass should have come from main exe");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass isLoaded should have been set on main exe\n");
-        *stop = true;
-        return;
+        FAIL("DyldClass isLoaded should have been set on main exe");
       }
       gotDyldClassMain = true;
       return;
     }
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: Unexpected DyldClass\n");
-    return;
+    FAIL("Unexpected DyldClass");
   });
 
   if (!gotDyldClassLinked1) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have seen DyldClass in liblinked1\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have seen DyldClass in liblinked1");
   }
 
   if (!gotDyldClassMain) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have seen DyldClass in main.exe\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have seen DyldClass in main.exe");
   }
 
   // Visit again, and return liblinked1's DyldClass
@@ -186,8 +166,7 @@ int main() {
     *stop = true;
   });
   if (dyldClassImpl != getLinked1DyldClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have returned DyldClass from liblinked1\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have returned DyldClass from liblinked1");
   }
 
   // Visit again, and return liblinked1's DyldClass
@@ -201,11 +180,8 @@ int main() {
     *stop = true;
   });
   if (dyldClassImpl != getMainDyldClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have returned DyldClass from main.exe\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have returned DyldClass from main.exe");
   }
 
-  printf("[PASS] _dyld_for_each_objc_class-missing-weak-chained\n");
-
-  return 0;
+  PASS("Success");
 }
index ce4b38e9c39a6aa86dc904262f00db6b3209cf27..4752f008e96b36cd728a59c247618a93e337b303 100644 (file)
@@ -1,8 +1,10 @@
 
-// BUILD:  $CC missing.m -dynamiclib -o $TEMP_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc
+// BUILD:  $CC missing.m -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name $BUILD_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 $TEMP_DIR/libmissing.dylib
-// BUILD:  $CC main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $TEMP_DIR/libmissing.dylib -lc++
+// 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++
+
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libmissing.dylib
 
 // RUN:  ./_dyld_for_each_objc_class-missing-weak.exe
 
@@ -17,6 +19,8 @@
 
 #import <Foundation/Foundation.h>
 
+#include "test_support.h"
+
 // All the libraries have a copy of DyldClass
 @interface DyldClass : NSObject
 @end
@@ -42,8 +46,6 @@ Class getMainDyldMainClass() {
   return (Class)&OBJC_CLASS_$_DyldMainClass;
 }
 
-extern "C" int printf(const char*, ...);
-
 extern "C" id objc_getClass(const char *name);
 
 // Get the DyldClass from liblinked1.dylib
@@ -63,9 +65,7 @@ extern "C" id getMissingDyldClass();
 static bool gotDyldClassMain = false;
 static bool gotDyldClassLinked1 = false;
 
-int main() {
-  printf("[BEGIN] _dyld_for_each_objc_class-missing-weak\n");
-
+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);
@@ -78,49 +78,41 @@ int main() {
       sawClass = true;
     });
     if (sawClass) {
-      printf("[FAIL] _dyld_for_each_objc_class: dyld2 shouldn't see any classes\n");
-      return 0;
+      FAIL("dyld2 shouldn't see any classes");
     }
-    printf("[PASS] _dyld_for_each_objc_class (dyld2 or no shared cache)\n");
-    return 0;
+    PASS("dyld2 or no shared cache");
   }
 
   // Make sure libmissing.dylib is actually missing
   if (&getMissingDyldClass != nil) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: libmissing needs to be missing\n");
-    return 0;
+    FAIL("libmissing needs to be missing");
   }
 
   // DyldClass in liblinked1 should exist as its superclass is just NSObject
   if (getLinked1DyldClass() == nil) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: liblinked1 DyldClass should exist\n");
-    return 0;
+    FAIL("liblinked1 DyldClass should exist");
   }
 
   // DyldLinkedClass in liblinked1 should exist as its superclass is just NSObject
   if (getLinked1DyldLinkedClass() == nil) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: liblinked1 DyldLinkedClass should exist\n");
-    return 0;
+    FAIL("liblinked1 DyldLinkedClass should exist");
   }
 
   // DyldLinkedClass in liblinked2 should exist as its superclass is just NSObject
   if (getLinked2DyldLinkedClass() == nil) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: liblinked2 DyldLinkedClass should exist\n");
-    return 0;
+    FAIL("liblinked2 DyldLinkedClass should exist");
   }
 
   // Check that DyldMainClass comes main.exe as that is its only definition
   id runtimeDyldMainClass = objc_getClass("DyldMainClass");
   if (runtimeDyldMainClass != getMainDyldMainClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldMainClass should have come from main.exe\n");
-    return 0;
+    FAIL("DyldMainClass should have come from main.exe");
   }
 
   // Check that DyldClass comes liblinked1 as it should be missing from liblinked2
   id runtimeDyldClass = objc_getClass("DyldClass");
   if (runtimeDyldClass != getLinked1DyldClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldClass should have come from liblinked1\n");
-    return 0;
+    FAIL("DyldClass should have come from liblinked1");
   }
 
   // Check that DyldLinkedClass comes from liblinked2
@@ -128,8 +120,7 @@ int main() {
 #if 0
   id runtimeDyldLinkedClass = objc_getClass("DyldLinkedClass");
   if (runtimeDyldLinkedClass != getLinked2DyldLinkedClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldLinkedClass should have come from liblinked2\n");
-    return 0;
+    FAIL("DyldLinkedClass should have come from liblinked2");
   }
 #endif
 
@@ -137,44 +128,33 @@ int main() {
     // We should walk these in the order liblinked, main exe
     if (!gotDyldClassLinked1) {
       if (classPtr != getLinked1DyldClass()) {
-        printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldClass should have come from liblinked1\n");
-        *stop = true;
-        return;
+        FAIL("DyldClass should have come from liblinked1");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldClass isLoaded should have been set on liblinked1\n");
-        *stop = true;
-        return;
+        FAIL("DyldClass isLoaded should have been set on liblinked1");
       }
       gotDyldClassLinked1 = true;
       return;
     }
     if (!gotDyldClassMain) {
       if (classPtr != getMainDyldClass()) {
-        printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldClass should have come from main exe\n");
-        *stop = true;
-        return;
+        FAIL("DyldClass should have come from main exe");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldClass isLoaded should have been set on main exe\n");
-        *stop = true;
-        return;
+        FAIL("DyldClass isLoaded should have been set on main exe");
       }
       gotDyldClassMain = true;
       return;
     }
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: Unexpected DyldClass\n");
-    return;
+    FAIL("Unexpected DyldClass");
   });
 
   if (!gotDyldClassLinked1) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: _dyld_for_each_objc_class should have seen DyldClass in liblinked1\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have seen DyldClass in liblinked1");
   }
 
   if (!gotDyldClassMain) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: _dyld_for_each_objc_class should have seen DyldClass in main.exe\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have seen DyldClass in main.exe");
   }
 
   // Visit again, and return liblinked1's DyldClass
@@ -185,8 +165,7 @@ int main() {
     *stop = true;
   });
   if (dyldClassImpl != getLinked1DyldClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: _dyld_for_each_objc_class should have returned DyldClass from liblinked1\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have returned DyldClass from liblinked1");
   }
 
   // Visit again, and return liblinked1's DyldClass
@@ -200,11 +179,8 @@ int main() {
     *stop = true;
   });
   if (dyldClassImpl != getMainDyldClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class-missing-weak: _dyld_for_each_objc_class should have returned DyldClass from main.exe\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have returned DyldClass from main.exe");
   }
 
-  printf("[PASS] _dyld_for_each_objc_class-missing-weak\n");
-
-  return 0;
+  PASS("SUCCESS");
 }
index 517dfdee0a2916bc96fb5f230db440b58f5bd956..083e94d440da2cd90bd598c26dbea7449dbdb6cd 100644 (file)
@@ -14,6 +14,8 @@
 
 #import <Foundation/Foundation.h>
 
+#include "test_support.h"
+
 // All the libraries have a copy of DyldClass
 @interface DyldClass : NSObject
 @end
@@ -39,8 +41,6 @@ Class getMainDyldMainClass() {
   return (Class)&OBJC_CLASS_$_DyldMainClass;
 }
 
-extern int printf(const char*, ...);
-
 extern id objc_getClass(const char *name);
 
 // Get the DyldClass from liblinked1.dylib
@@ -56,9 +56,7 @@ static bool gotDyldClassMain = false;
 static bool gotDyldClassLinked = false;
 static bool gotDyldClassLinked2 = false;
 
-int main() {
-  printf("[BEGIN] _dyld_for_each_objc_class\n");
-
+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);
@@ -71,25 +69,21 @@ int main() {
       sawClass = true;
     });
     if (sawClass) {
-      printf("[FAIL] _dyld_for_each_objc_class: dyld2 shouldn't see any classes\n");
-      return 0;
+      FAIL("dyld2 shouldn't see any classes");
     }
-    printf("[PASS] _dyld_for_each_objc_class (dyld2 or no shared cache)\n");
-    return 0;
+    PASS("dyld2 or no shared cache)");
   }
 
   // Check that DyldClass comes from liblinked2 as it is last in load order
   id runtimeDyldClass = objc_getClass("DyldClass");
   if (runtimeDyldClass != getLinked2DyldClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class: DyldClass should have come from liblinked2\n");
-    return 0;
+    FAIL("DyldClass should have come from liblinked2");
   }
 
   // Check that DyldLinkedClass comes from liblinked2 as it is last in load order
   id runtimeDyldLinkedClass = objc_getClass("DyldLinkedClass");
   if (runtimeDyldLinkedClass != getLinked2DyldLinkedClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class: DyldLinkedClass should have come from liblinked2\n");
-    return 0;
+    FAIL("DyldLinkedClass should have come from liblinked2");
   }
 
   // Walk all the implementations of "DyldClass"
@@ -97,53 +91,39 @@ int main() {
     // We should walk these in the order liblinked2, liblinked, main exe
     if (!gotDyldClassLinked2) {
       if (classPtr != getLinked2DyldClass()) {
-        printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass should have come from liblinked2\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldClass should have come from liblinked2");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass isLoaded should have been set on liblinked2\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldClass isLoaded should have been set on liblinked2");
       }
       gotDyldClassLinked2 = true;
       return;
     }
     if (!gotDyldClassLinked) {
       if (classPtr != getLinked1DyldClass()) {
-        printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass should have come from liblinked\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldClass should have come from liblinked");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass isLoaded should have been set on liblinked\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldClass isLoaded should have been set on liblinked");
       }
       gotDyldClassLinked = true;
       return;
     }
     if (!gotDyldClassMain) {
       if (classPtr != getMainDyldClass()) {
-        printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass should have come from main exe\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldClass should have come from main exe");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass isLoaded should have been set on main exe\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldClass isLoaded should have been set on main exe");
       }
       gotDyldClassMain = true;
       return;
     }
-    printf("[FAIL] _dyld_for_each_objc_class: Unexpected Optimized DyldClass\n");
-    return;
+    FAIL("Unexpected Optimized DyldClass");
   });
 
   if ( !gotDyldClassLinked2 || !gotDyldClassLinked || !gotDyldClassMain) {
-    printf("[FAIL] _dyld_for_each_objc_class: Failed to find all duplicates of 'DyldClass'\n");
-    return 0;
+    FAIL("Failed to find all duplicates of 'DyldClass'");
   }
 
   // Visit again, and return liblinked2's DyldClass
@@ -153,8 +133,7 @@ int main() {
     *stop = true;
   });
   if (dyldClassImpl != getLinked2DyldClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class: _dyld_for_each_objc_class should have returned DyldClass from liblinked2\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have returned DyldClass from liblinked2");
   }
 
   // Visit DyldMainClass and make sure it makes the callback for just the result from main.exe
@@ -164,11 +143,8 @@ int main() {
     *stop = true;
   });
   if (dyldMainClassImpl != getMainDyldMainClass()) {
-    printf("[FAIL] _dyld_for_each_objc_class: _dyld_for_each_objc_class should have returned DyldMainClass from main.exe\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_class should have returned DyldMainClass from main.exe");
   }
 
-  printf("[PASS] _dyld_for_each_objc_class\n");
-
-  return 0;
+  PASS("Success");
 }
index eea4adfb5508e35ea0279b51b3e791cd10ab20c5..d80aea26d32471d9309a100d77f066abfe630f81 100644 (file)
@@ -17,6 +17,8 @@
 #include <string.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
+
 // All the libraries have a copy of DyldProtocol
 @protocol DyldProtocol
 @end
@@ -35,8 +37,6 @@ static void* useDyldMainProtocol() {
   return (void*)@protocol(DyldMainProtocol);
 }
 
-extern int printf(const char*, ...);
-
 extern id objc_getProtocol(const char *name);
 
 static bool gotDyldProtocolMain = false;
@@ -46,15 +46,13 @@ static bool gotDyldProtocolLinked2 = false;
 static bool isInImage(void* ptr, const char* name) {
   Dl_info info;
   if ( dladdr(ptr, &info) == 0 ) {
-    printf("[FAIL] _dyld_for_each_objc_protocol dladdr(protocol, xx) failed\n");
+    FAIL("dladdr(protocol, xx) failed");
     return false;
   }
   return strstr(info.dli_fname, name) != NULL;
 }
 
-int main() {
-  printf("[BEGIN] _dyld_for_each_objc_protocol\n");
-
+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);
@@ -67,25 +65,21 @@ int main() {
       sawProtocol = true;
     });
     if (sawProtocol) {
-      printf("[FAIL] _dyld_for_each_objc_protocol: dyld2 shouldn't see any protocols\n");
-      return 0;
+      FAIL("dyld2 shouldn't see any protocols");
     }
-    printf("[PASS] _dyld_for_each_objc_protocol (dyld2 or no shared cache)\n");
-    return 0;
+    PASS("dyld2 or no shared cache");
   }
 
   // Check that DyldProtocol comes from liblinked2 as it is last in load order
   id runtimeDyldProtocol = objc_getProtocol("DyldProtocol");
   if (!isInImage(runtimeDyldProtocol, "liblinked2")) {
-    printf("[FAIL] _dyld_for_each_objc_protocol: DyldProtocol should have come from liblinked2\n");
-    return 0;
+    FAIL("DyldProtocol should have come from liblinked2");
   }
 
   // Check that DyldLinkedProtocol comes from liblinked2 as it is last in load order
   id runtimeDyldLinkedProtocol = objc_getProtocol("DyldLinkedProtocol");
   if (!isInImage(runtimeDyldLinkedProtocol, "liblinked2")) {
-    printf("[FAIL] _dyld_for_each_objc_protocol: DyldLinkedProtocol should have come from liblinked2\n");
-    return 0;
+    FAIL("DyldLinkedProtocol should have come from liblinked2");
   }
 
   // Walk all the implementations of "DyldProtocol"
@@ -93,47 +87,35 @@ int main() {
     // We should walk these in the order liblinked2, liblinked, main exe
     if (!gotDyldProtocolLinked2) {
       if (!isInImage(protocolPtr, "liblinked2")) {
-        printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol should have come from liblinked2\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldProtocol should have come from liblinked2");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol isLoaded should have been set on liblinked2\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldProtocol isLoaded should have been set on liblinked2");
       }
       gotDyldProtocolLinked2 = true;
       return;
     }
     if (!gotDyldProtocolLinked) {
       if (!isInImage(protocolPtr, "liblinked1")) {
-        printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol should have come from liblinked\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldProtocol should have come from liblinked");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol isLoaded should have been set on liblinked\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldProtocol isLoaded should have been set on liblinked");
       }
       gotDyldProtocolLinked = true;
       return;
     }
     if (!gotDyldProtocolMain) {
       if (!isInImage(protocolPtr, "_dyld_for_each_objc_protocol.exe")) {
-        printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol should have come from main exe\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldProtocol should have come from main exe");
       }
       if (!isLoaded) {
-        printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol isLoaded should have been set on main exe\n");
-        *stop = true;
-        return;
+        FAIL("Optimized DyldProtocol isLoaded should have been set on main exe");
       }
       gotDyldProtocolMain = true;
       return;
     }
-    printf("[FAIL] _dyld_for_each_objc_protocol: Unexpected Optimized DyldProtocol\n");
+    FAIL("Unexpected Optimized DyldProtocol");
     return;
   });
 
@@ -144,8 +126,7 @@ int main() {
     *stop = true;
   });
   if (!isInImage(DyldProtocolImpl, "liblinked2")) {
-    printf("[FAIL] _dyld_for_each_objc_protocol: _dyld_for_each_objc_protocol should have returned DyldProtocol from liblinked2\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_protocol should have returned DyldProtocol from liblinked2");
   }
 
   // Visit DyldMainProtocol and make sure it makes the callback for just the result from main.exe
@@ -155,11 +136,8 @@ int main() {
     *stop = true;
   });
   if (!isInImage(DyldMainProtocolImpl, "_dyld_for_each_objc_protocol.exe")) {
-    printf("[FAIL] _dyld_for_each_objc_protocol: _dyld_for_each_objc_protocol should have returned DyldMainProtocol from main.exe\n");
-    return 0;
+    FAIL("_dyld_for_each_objc_protocol should have returned DyldMainProtocol from main.exe");
   }
 
-  printf("[PASS] _dyld_for_each_objc_protocol\n");
-
-  return 0;
+  PASS("Success");
 }
index e4f1b9d2e5f6f005524e2b33de9fbdfee5b6a9dd..ec3e198cb6de401822de43dcc52a4b539717720a 100644 (file)
 #include <mach-o/dyld.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] _dyld_get_image_slide-test\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     int count = _dyld_image_count();
     for (int i=0; i < count; ++i) {
         const struct mach_header* mh    = _dyld_get_image_header(i);
@@ -22,8 +20,7 @@ int main()
         intptr_t                  slide = _dyld_get_image_slide(mh);
         intptr_t                  vmaddrSlide = _dyld_get_image_vmaddr_slide(i);
         if ( slide != vmaddrSlide ) {
-            printf("[FAIL] _dyld_get_image_slide-test: %lld != %lld in %s\n",
-                   (uint64_t)slide, (uint64_t)vmaddrSlide, name);
+            FAIL("%lld != %lld in %s", (uint64_t)slide, (uint64_t)vmaddrSlide, name);
             return 0;
         }
     }
@@ -32,17 +29,14 @@ int main()
     uintptr_t notMagic = 0;
     intptr_t slide = _dyld_get_image_slide((const struct mach_header*)&notMagic);
     if (slide != 0) {
-        printf("[FAIL] _dyld_get_image_slide-test: slide value %lld for bad magic\n",
-               (uint64_t)slide);
+        FAIL("slide value %lld for bad magic", (uint64_t)slide);
     }
 
     intptr_t vmaddrSlide = _dyld_get_image_vmaddr_slide(count + 1);
     if (vmaddrSlide != 0) {
-        printf("[FAIL] _dyld_get_image_slide-test: vmaddr slide value %lld for index %d\n",
-               (uint64_t)vmaddrSlide, count + 1);
+        FAIL("vmaddr slide value %lld for index %d", (uint64_t)vmaddrSlide, count + 1);
     }
 
-    printf("[PASS] _dyld_get_image_slide-test\n");
-       return 0;
+    PASS("Success");
 }
 
index 2d19a993c6d7fb26d30b71f61d7e1f6762616154..5a7a735586d7f2c024e29d9f487330d1f0202770 100644 (file)
@@ -8,6 +8,8 @@
 
 #import <Foundation/Foundation.h>
 
+#include "test_support.h"
+
 @interface DyldClass : NSObject
 @end
 
 }
 @end
 
-extern int printf(const char*, ...);
-
 extern id objc_getClass(const char *name);
 
-int main() {
-       printf("[BEGIN] _dyld_get_objc_selector-chained\n");
-
-       // dyldClassFoo
-       const char* sel = _dyld_get_objc_selector("dyldClassFoo");
-       if (sel) {
-               if ((SEL)sel != @selector(dyldClassFoo)) {
-                       printf("[FAIL] _dyld_get_objc_selector-chained: dyldClassFoo is wrong\n");
-                       return 0;
-               }
-       }
-
-       // dyldMainClassFoo
-       sel = _dyld_get_objc_selector("dyldMainClassFoo");
-       if (sel) {
-               if ((SEL)sel != @selector(dyldMainClassFoo)) {
-                       printf("[FAIL] _dyld_get_objc_selector-chained: dyldMainClassFoo is wrong\n");
-                       return 0;
-               }
-       }
-
-       // dyldMainClassFoo2
-       sel = _dyld_get_objc_selector("dyldMainClassFoo2");
-       if (sel) {
-               if ((SEL)sel != @selector(dyldMainClassFoo2)) {
-                       printf("[FAIL] _dyld_get_objc_selector-chained: dyldMainClassFoo2 is wrong\n");
-                       return 0;
-               }
-       }
-
-       printf("[PASS] _dyld_get_objc_selector-chained\n");
-
-       return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    // dyldClassFoo
+    const char* sel = _dyld_get_objc_selector("dyldClassFoo");
+    if (sel) {
+        if ((SEL)sel != @selector(dyldClassFoo)) {
+            FAIL("dyldClassFoo is wrong");
+        }
+    }
+
+    // dyldMainClassFoo
+    sel = _dyld_get_objc_selector("dyldMainClassFoo");
+    if (sel) {
+        if ((SEL)sel != @selector(dyldMainClassFoo)) {
+            FAIL("dyldMainClassFoo is wrong");
+        }
+    }
+
+    // dyldMainClassFoo2
+    sel = _dyld_get_objc_selector("dyldMainClassFoo2");
+    if (sel) {
+        if ((SEL)sel != @selector(dyldMainClassFoo2)) {
+            FAIL("dyldMainClassFoo2 is wrong");
+        }
+    }
+
+    PASS("Success");
 }
index d11e4421f3b1d1ec4b1cc59fb39b423506a241fe..e698fbfcc005b22114534f01b218d6801e5408c7 100644 (file)
@@ -9,11 +9,9 @@
 #include <mach-o/dyld.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] _dyld_get_objc_selector-shared-cache\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     size_t cacheLen;
     uintptr_t cacheStart = (uintptr_t)_dyld_get_shared_cache_range(&cacheLen);
 
@@ -22,24 +20,20 @@ int main()
     if ( cacheStart != 0 ) {
         // We have a shared cache, so the selector should be there
         if ( selName == NULL ) {
-            printf("[FAIL] _dyld_get_objc_selector() returned null for selector in shared cache\n");
-            return 0;
+            FAIL("_dyld_get_objc_selector() returned null for selector in shared cache");
         }
 
         if ( ((uintptr_t)selName < cacheStart) || ((uintptr_t)selName >= (cacheStart + cacheLen)) ) {
-            printf("[FAIL] _dyld_get_objc_selector() pointer outside of shared cache range\n");
-            return 0;
+            FAIL("_dyld_get_objc_selector() pointer outside of shared cache range");
         }
     } else {
         // No shared cache, so the selector should not be found.
         // FIXME: This assumption may be false once the selectors are in the closure.
         if ( selName != NULL ) {
-            printf("[FAIL] _dyld_get_objc_selector() returned non-null for selector without shared cache\n");
-            return 0;
+            FAIL("_dyld_get_objc_selector() returned non-null for selector without shared cache");
         }
     }
 
-    printf("[PASS] _dyld_get_objc_selector-shared-cache\n");
-    return 0;
+    PASS("Success");
 }
 
index 9845721ef23e5eb1179145df395677e76c8d4b02..8375d3de1d681198f0f09d219a7cb3ca72ec2de0 100644 (file)
@@ -7,65 +7,52 @@
 
 #import <Foundation/Foundation.h>
 
+#include "test_support.h"
+
 @interface DyldClass : NSObject
 @end
 
 @implementation DyldClass
--(void) dyldClassFoo {
-       
-}
-+(void) dyldClassFoo {
-       
-}
+-(void) dyldClassFoo {}
++(void) dyldClassFoo {}
 @end
 
 @interface DyldMainClass : NSObject
 @end
 
 @implementation DyldMainClass
--(void) dyldMainClassFoo {
-       
-}
--(void) dyldMainClassFoo2 {
-       
-}
+-(void) dyldMainClassFoo {}
+-(void) dyldMainClassFoo2 {}
 @end
 
-extern int printf(const char*, ...);
-
 extern id objc_getClass(const char *name);
 
-int main() {
-       printf("[BEGIN] _dyld_get_objc_selector\n");
-
-       // dyldClassFoo
-       const char* sel = _dyld_get_objc_selector("dyldClassFoo");
-       if (sel) {
-               if ((SEL)sel != @selector(dyldClassFoo)) {
-                       printf("[FAIL] _dyld_get_objc_selector: dyldClassFoo is wrong\n");
-                       return 0;
-               }
-       }
-
-       // dyldMainClassFoo
-       sel = _dyld_get_objc_selector("dyldMainClassFoo");
-       if (sel) {
-               if ((SEL)sel != @selector(dyldMainClassFoo)) {
-                       printf("[FAIL] _dyld_get_objc_selector: dyldMainClassFoo is wrong\n");
-                       return 0;
-               }
-       }
-
-       // dyldMainClassFoo2
-       sel = _dyld_get_objc_selector("dyldMainClassFoo2");
-       if (sel) {
-               if ((SEL)sel != @selector(dyldMainClassFoo2)) {
-                       printf("[FAIL] _dyld_get_objc_selector: dyldMainClassFoo2 is wrong\n");
-                       return 0;
-               }
-       }
-
-       printf("[PASS] _dyld_get_objc_selector\n");
-
-       return 0;
-}
\ No newline at end of file
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    // dyldClassFoo
+    const char* sel = _dyld_get_objc_selector("dyldClassFoo");
+    if (sel) {
+        if ((SEL)sel != @selector(dyldClassFoo)) {
+            FAIL("dyldClassFoo is wrong");
+        }
+    }
+    
+    // dyldMainClassFoo
+    sel = _dyld_get_objc_selector("dyldMainClassFoo");
+    if (sel) {
+        if ((SEL)sel != @selector(dyldMainClassFoo)) {
+            FAIL("dyldMainClassFoo is wrong");
+        }
+    }
+
+    // dyldMainClassFoo2
+    sel = _dyld_get_objc_selector("dyldMainClassFoo2");
+    if (sel) {
+        if ((SEL)sel != @selector(dyldMainClassFoo2)) {
+            FAIL("dyldMainClassFoo2 is wrong");
+        }
+    }
+
+    PASS("_dyld_get_objc_selector");
+
+    return 0;
+}
index c44d1f29771506f8629a5efe8b5a6454cbc3fbfc..5ffcf9ab60f43ae6255a67ceb53c76c2341cf133 100644 (file)
@@ -10,6 +10,8 @@
 #include <uuid/uuid.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
+
 extern void* __dso_handle;
 
 extern int foo1();
@@ -36,9 +38,7 @@ int mydata = 5;
 int myarray[10];
 
 
-int main()
-{
-    printf("[BEGIN] _dyld_images_for_addresses\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     int mylocal;
 
     const void* addresses[12];
@@ -61,51 +61,50 @@ int main()
     for (int i=0; i < 12; ++i) {
         uuid_string_t str;
         uuid_unparse_upper(infos[i].uuid, str);
-        printf("0x%09llX 0x%08llX %s\n", (long long)infos[i].image, infos[i].offsetInImage, str);
+        LOG("0x%09llX 0x%08llX %s", (long long)infos[i].image, infos[i].offsetInImage, str);
     }
 
     if ( infos[0].image != infos[1].image )
-        printf("[FAIL] _dyld_images_for_addresses 0 vs 1\n");
+        FAIL("0 vs 1");
     else if ( infos[0].image != infos[2].image )
-        printf("[FAIL] _dyld_images_for_addresses 0 vs 2\n");
+        FAIL("0 vs 2");
     else if ( infos[0].image != infos[3].image )
-        printf("[FAIL] _dyld_images_for_addresses 0 vs 3\n");
+        FAIL("0 vs 3");
     else if ( infos[0].image != infos[4].image )
-        printf("[FAIL] _dyld_images_for_addresses 0 vs 4\n");
+        FAIL("0 vs 4");
     else if ( infos[0].image != infos[5].image )
-        printf("[FAIL] _dyld_images_for_addresses 0 vs 5\n");
+        FAIL("0 vs 5");
     else if ( infos[6].image != NULL )
-        printf("[FAIL] _dyld_images_for_addresses 6 vs null \n");
+        FAIL("6 vs null ");
     else if ( infos[7].image != infos[8].image )
-        printf("[FAIL] _dyld_images_for_addresses 7 vs 8\n");
+        FAIL("7 vs 8");
     else if ( infos[7].image != infos[9].image )
-        printf("[FAIL] _dyld_images_for_addresses 7 vs 9\n");
+        FAIL("7 vs 9");
     else if ( infos[0].image == infos[7].image )
-        printf("[FAIL] _dyld_images_for_addresses 0 vs 7\n");
+        FAIL("0 vs 7");
     else if ( infos[10].image != infos[11].image )
-        printf("[FAIL] _dyld_images_for_addresses 10 vs 11\n");
+        FAIL("10 vs 11");
     else if ( uuid_compare(infos[0].uuid, infos[1].uuid) != 0  )
-        printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 1\n");
+        FAIL("uuid 0 vs 1");
     else if ( uuid_compare(infos[0].uuid, infos[2].uuid) != 0 )
-        printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 2\n");
+        FAIL("uuid 0 vs 2");
     else if ( uuid_compare(infos[0].uuid, infos[3].uuid) != 0 )
-        printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 3\n");
+        FAIL("uuid 0 vs 3");
     else if ( uuid_compare(infos[0].uuid, infos[4].uuid) != 0 )
-        printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 4\n");
+        FAIL("uuid 0 vs 4");
     else if ( uuid_compare(infos[0].uuid, infos[5].uuid) != 0 )
-        printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 5\n");
+        FAIL("uuid 0 vs 5");
     else if ( uuid_is_null(infos[6].uuid) == 0 )
-        printf("[FAIL] _dyld_images_for_addresses uuid 6 vs null\n");
+        FAIL("uuid 6 vs null");
     else if ( uuid_compare(infos[7].uuid, infos[8].uuid) != 0 )
-        printf("[FAIL] _dyld_images_for_addresses uuid 7 vs 8\n");
+        FAIL("uuid 7 vs 8");
     else if ( uuid_compare(infos[7].uuid, infos[9].uuid) != 0 )
-        printf("[FAIL] _dyld_images_for_addresses uuid 7 vs 9\n");
+        FAIL("uuid 7 vs 9");
     else if ( uuid_compare(infos[0].uuid, infos[7].uuid) == 0 )
-        printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 7\n");
+        FAIL("uuid 0 vs 7");
     else if ( uuid_compare(infos[10].uuid, infos[11].uuid) != 0 )
-        printf("[FAIL] _dyld_images_for_addresses uuid 10 vs 11\n");
+        FAIL("uuid 10 vs 11");
     else
-        printf("[PASS] _dyld_images_for_addresses\n");
-    return 0;
+        PASS("Success");
 }
 
index e35435fa91a61355d2254b3801df5cb53c28339c..85c3be6e9328757cd3c041ee7e56e0dc7b681b07 100644 (file)
@@ -11,6 +11,7 @@
 #include <pthread.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
 static void* work(void* mh)
 {
@@ -27,8 +28,7 @@ static void notify(const struct mach_header* mh, intptr_t vmaddr_slide)
     // for each image notified that is not in the dyld cache, spin up a thread which calls _dyld_is_memory_immutable()
     pthread_t workerThread;
     if ( pthread_create(&workerThread, NULL, &work, (void*)mh) != 0 ) {
-        printf("[FAIL]  _dyld_is_memory_immutable-lock, pthread_create\n");
-        exit(0);
+        FAIL("pthread_create");
     }
     void* dummy;
     pthread_join(workerThread, &dummy);
@@ -36,20 +36,14 @@ static void notify(const struct mach_header* mh, intptr_t vmaddr_slide)
 
 
 
-int main()
-{
-    printf("[BEGIN] _dyld_is_memory_immutable-lock\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     _dyld_register_func_for_add_image(&notify);
 
     void* h = dlopen(RUN_DIR "/lock.bundle", 0);
     if ( h == NULL ) {
-        printf("dlopen(lock.bundle) failed with error: %s\n", dlerror());
-        printf("[FAIL]  _dyld_is_memory_immutable-lock, lock.bundle not loaded\n");
-        return 0;
+        FAIL("lock.bundle not loaded, dlopen(lock.bundle) failed with error: %s", dlerror());
     }
 
-    printf("[PASS]  _dyld_is_memory_immutable-lock\n");
-       return 0;
+    PASS("Success");
 }
 
index a162cfa5b5f144b03e1c9160175ae909ded59807..94e4a0a4bfceb6aec56ec13a45d7c986ca5cd30d 100644 (file)
@@ -16,6 +16,8 @@
     #include <ptrauth.h>
 #endif
 
+#include "test_support.h"
+
 static const void* stripPointer(const void* ptr)
 {
 #if __has_feature(ptrauth_calls)
@@ -36,59 +38,44 @@ const char* myStr = "myStr";
 int myInt;
 
 
-int main()
-{
-    printf("[BEGIN] _dyld_is_memory_immutable\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if ( !_dyld_is_memory_immutable(myStr, 6) ) {
-               printf("[FAIL] _dyld_is_memory_immutable() returned false for string in main executable\n");
-        return 0;
+        FAIL("returned false for string in main executable");
     }
 
     if ( _dyld_is_memory_immutable(strdup("hello"), 6) ) {
-               printf("[FAIL] _dyld_is_memory_immutable() returned true for result from strdup()\n");
-        return 0;
+        FAIL("returned true for result from strdup()");
     }
 
     if ( _dyld_is_memory_immutable(&myInt, 4) ) {
-               printf("[FAIL] _dyld_is_memory_immutable() returned true for global variabe in main executable\n");
-        return 0;
+        FAIL("returned true for global variabe in main executable");
     }
 
     if ( !_dyld_is_memory_immutable(foo(), 4) ) {
-               printf("[FAIL] _dyld_is_memory_immutable() returned false for string in statically linked dylib\n");
-        return 0;
+        FAIL("returned false for string in statically linked dylib");
     }
 
     if ( !_dyld_is_memory_immutable(stripPointer((void*)&strcpy), 4) ) {
-               printf("[FAIL] _dyld_is_memory_immutable() returned false for strcpy function in dyld shared cache\n");
-        return 0;
+        FAIL("returned false for strcpy function in dyld shared cache");
     }
 
     if ( _dyld_is_memory_immutable(&_cpu_capabilities, 4) ) {
-               printf("[FAIL] _dyld_is_memory_immutable() returned true for global variable in shared cache\n");
-        return 0;
+        FAIL("returned true for global variable in shared cache");
     }
 
-       void* handle = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST);
+    void* handle = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST);
     if ( handle == NULL ) {
-               printf("[FAIL] dlopen(libbar.dylib) failed");
-        return 0;
-    }
+        FAIL("dlopen(libbar.dylib) failed");    }
 
     BarProc proc = dlsym(handle, "bar");
     if ( proc == NULL ) {
-               printf("[FAIL] dlsym(bar) failed\n");
-        return 0;
+        FAIL("dlsym(bar) failed");
     }
     const char* barStr = (*proc)();
     if ( _dyld_is_memory_immutable(barStr, 4) ) {
-               printf("[FAIL] _dyld_is_memory_immutable() returned true for string in unloadable dylib\n");
-        return 0;
+        FAIL("returned true for string in unloadable dylib");
     }
 
-
-    printf("[PASS] _dyld_is_memory_immutable\n");
-    return 0;
+    PASS("Success");
 }
 
index e3b00af15ced0c82c7c71996df218a9fc46ec25e..29b6521584a215696f6f280bee27a88447fc203b 100644 (file)
@@ -1,13 +1,12 @@
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
+
 __attribute__((constructor))
-void bar() {
+void bar(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     void* handle = dlopen(RUN_DIR "/libbaz.dylib", RTLD_FIRST);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libbaz.dylib", dlerror());
-        exit(0);
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libbaz.dylib", dlerror());
     }
 }
index c56126cd6563aceb35ca44558251b50658dfc676..0cc7e5c7a83ea1cb4bf86f2fc28e5afc715a949f 100644 (file)
@@ -16,6 +16,8 @@
 
 #include <unordered_set>
 
+#include "test_support.h"
+
 extern "C" void foo();
 
 extern mach_header __dso_handle;
@@ -24,13 +26,13 @@ static std::unordered_set<const mach_header*> sCurrentImages;
 
 static void notify(unsigned count, const mach_header* mhs[], const char* paths[])
 {
-    fprintf(stderr, "notification:\n");
+    LOG("notification:");
     for (unsigned i=0; i < count; ++i) {
         const mach_header* mh   = mhs[i];
         const char*        path = paths[i];
-        fprintf(stderr, "  %3d  mh=%p, path=%s\n", i, mh, path);
+        LOG("  %3d  mh=%p, path=%s", i, mh, path);
         if ( sCurrentImages.count(mh) != 0 ) {
-            printf("[FAIL] _dyld_register_for_image_loads: notified twice about %p\n", mh);
+            FAIL("notified twice about %p", mh);
             exit(0);
         }
         sCurrentImages.insert(mh);
@@ -38,49 +40,39 @@ static void notify(unsigned count, const mach_header* mhs[], const char* paths[]
 }
 
 
-int main()
-{
-    printf("[BEGIN] _dyld_register_for_bulk_image_loads\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     _dyld_register_for_bulk_image_loads(&notify);
 
     // verify we were notified about already loaded images
     if ( sCurrentImages.count(&__dso_handle) == 0 ) {
-               printf("[FAIL] _dyld_register_for_bulk_image_loads() did not notify us about main executable\n");
-               exit(0);
+        FAIL("did not notify us about main executable");
     }
     const mach_header* libSysMH = dyld_image_header_containing_address((void*)&printf);
     if ( sCurrentImages.count(libSysMH) == 0 ) {
-               printf("[FAIL] _dyld_register_for_bulk_image_loads() did not notify us about libsystem_c.dylib\n");
-               exit(0);
+        FAIL("did not notify us about libsystem_c.dylib");
     }
     const mach_header* libFoo = dyld_image_header_containing_address((void*)&foo);
     if ( sCurrentImages.count(libFoo) == 0 ) {
-        printf("[FAIL] _dyld_register_for_bulk_image_loads() did not notify us about libfoo.dylib\n");
-        exit(0);
+        FAIL("did not notify us about libfoo.dylib");
     }
 
     // verify we were notified about load of libfoo2.dylib and libz.dylib
-       void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST);
-       if ( handle2 == NULL ) {
-               printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo.dylib", dlerror());
-               exit(0);
-       }
+    void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST);
+    if ( handle2 == NULL ) {
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror());
+    }
     const void* libfoo2Foo = dlsym(handle2, "foo");
     const mach_header* libfoo2MH = dyld_image_header_containing_address(libfoo2Foo);
     if ( sCurrentImages.count(libfoo2MH) == 0 ) {
-               printf("[FAIL] _dyld_register_for_bulk_image_loads() did not notify us about libfoo2.dylib\n");
-               exit(0);
+        FAIL("did not notify us about libfoo2.dylib");
     }
     const void* inflateSym = dlsym(RTLD_DEFAULT, "inflate");
     if ( inflateSym == NULL ) {
-        printf("[FAIL] _dyld_register_for_bulk_image_loads() did not load libz.dylib\n");
-        exit(0);
+       FAIL("did not load libz.dylib");
     }
     const mach_header* libzMH = dyld_image_header_containing_address(inflateSym);
     if ( sCurrentImages.count(libzMH) == 0 ) {
-        printf("[FAIL] _dyld_register_for_bulk_image_loads() did not notify us about libz.dylib\n");
-        exit(0);
+        FAIL("did not notify us about libz.dylib");
     }
     
     // Upward linking with dlopen may confuse the notifier as we may try to notify twice on the upwardly linked image
@@ -91,11 +83,9 @@ int main()
     // We should not get a duplicate notification on libup
     void* handleBar = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST);
     if ( handleBar == NULL ) {
-        printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libbar.dylib", dlerror());
-        exit(0);
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libbar.dylib", dlerror());
     }
 
-    printf("[PASS] _dyld_register_for_bulk_image_loads\n");
-    return 0;
+    PASS("SUuccess");
 }
 
index e3b00af15ced0c82c7c71996df218a9fc46ec25e..5613fa7286039187a9e377bdaa77cada39e6c7ed 100644 (file)
@@ -3,11 +3,13 @@
 #include <stdlib.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
+
 __attribute__((constructor))
-void bar() {
+void bar(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     void* handle = dlopen(RUN_DIR "/libbaz.dylib", RTLD_FIRST);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libbaz.dylib", dlerror());
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libbaz.dylib", dlerror());
         exit(0);
     }
 }
index c4ec3b1125b7f8f4eb9cb6100f436a868ffc7ad4..ed5a9c619720d7833efaa7aee1024ae9b738e6c9 100644 (file)
@@ -17,6 +17,8 @@
 
 #include <unordered_set>
 
+#include "test_support.h"
+
 extern "C" void foo();
 
 extern mach_header __dso_handle;
@@ -26,73 +28,61 @@ static bool expectedUnloadableState = false;
 
 static void notify(const mach_header* mh, const char* path, bool unloadable)
 {
-    fprintf(stderr, "mh=%p, path=%s, unloadable=%d\n", mh, path, unloadable);
+    LOG("mh=%p, path=%s, unloadable=%d", mh, path, unloadable);
     if ( sCurrentImages.count(mh) != 0 ) {
-        printf("[FAIL] _dyld_register_for_image_loads: notified twice about %p\n", mh);
-        exit(0);
+        FAIL("notified twice about %p", mh);
     }
     sCurrentImages.insert(mh);
 
     const char* leaf = strrchr(path, '/');
     if ( unloadable != expectedUnloadableState ) {
-        printf("[FAIL] _dyld_register_for_image_loads: image incorrectly marked unloadable(%s) but expected unloadable(%s) %p %s\n",
+        FAIL("image incorrectly marked unloadable(%s) but expected unloadable(%s) %p %s",
                unloadable ? "true" : "true", expectedUnloadableState ? "true" : "false", mh, path);
-        exit(0);
     }
 }
 
 
-int main()
-{
-    printf("[BEGIN] _dyld_register_for_image_loads\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // Initially all images must not be unloadable as they are directly linked to the main executable
     expectedUnloadableState = false;
     _dyld_register_for_image_loads(&notify);
 
     // verify we were notified about already loaded images
     if ( sCurrentImages.count(&__dso_handle) == 0 ) {
-               printf("[FAIL] _dyld_register_for_image_loads() did not notify us about main executable\n");
-               exit(0);
+        FAIL("did not notify us about main executable");
     }
     const mach_header* libSysMH = dyld_image_header_containing_address((void*)&printf);
     if ( sCurrentImages.count(libSysMH) == 0 ) {
-               printf("[FAIL] _dyld_register_for_image_loads() did not notify us about libsystem_c.dylib\n");
-               exit(0);
+        FAIL("did not notify us about libsystem_c.dylib");
     }
     const mach_header* libFoo = dyld_image_header_containing_address((void*)&foo);
     if ( sCurrentImages.count(libFoo) == 0 ) {
-        printf("[FAIL] _dyld_register_for_image_loads() did not notify us about libfoo.dylib\n");
-        exit(0);
+        FAIL("did not notify us about libfoo.dylib");
     }
     
     // These dlopen's can be unloaded
     expectedUnloadableState = true;
 
     // verify we were notified about load of libfoo2.dylib
-       void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST);
-       if ( handle2 == NULL ) {
-               printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo.dylib", dlerror());
-               exit(0);
-       }
+    void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST);
+    if ( handle2 == NULL ) {
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror());
+    }
     const void* libfoo2Foo = dlsym(handle2, "foo");
     const mach_header* libfoo2MH = dyld_image_header_containing_address(libfoo2Foo);
     if ( sCurrentImages.count(libfoo2MH) == 0 ) {
-               printf("[FAIL] _dyld_register_for_image_loads() did not notify us about libfoo2.dylib\n");
-               exit(0);
+        FAIL("did not notify us about libfoo2.dylib");
     }
 
     // verify we were notified about load of foo.bundle
     void* handleB = dlopen(RUN_DIR "/foo.bundle", RTLD_FIRST);
     if ( handleB == NULL ) {
-        printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/foo.bundle", dlerror());
-        exit(0);
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/foo.bundle", dlerror());
     }
     const void* libfooBFoo = dlsym(handle2, "foo");
     const mach_header* libfooB = dyld_image_header_containing_address(libfooBFoo);
     if ( sCurrentImages.count(libfooB) == 0 ) {
-        printf("[FAIL] _dyld_register_for_image_loads() did not notify us about foo.bundle\n");
-        exit(0);
+        FAIL("_dyld_register_for_image_loads() did not notify us about foo.bundle");
     }
 
     // Upward linking with dlopen may confuse the notifier as we may try to notify twice on the upwardly linked image
@@ -103,11 +93,9 @@ int main()
     // We should not get a duplicate notification on libup
     void* handleBar = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST);
     if ( handleBar == NULL ) {
-        printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libbar.dylib", dlerror());
-        exit(0);
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libbar.dylib", dlerror());
     }
 
-    printf("[PASS] _dyld_register_for_image_loads\n");
-    return 0;
+    PASS("Success");
 }
 
index 905a1533ef7a66ed61712d4d4d02442ec9cac542..eb1a05e54dd4183956778d836236ab29418b0af1 100644 (file)
 
 #include <unordered_set>
 
+#include "test_support.h"
+
 extern mach_header __dso_handle;
 
 static std::unordered_set<const mach_header*> sCurrentImages;
 
 static void notify(const mach_header* mh, intptr_t vmaddr_slide)
 {
-    //fprintf(stderr, "mh=%p\n", mh);
+    LOG("mh=%p", mh);
     if ( sCurrentImages.count(mh) != 0 ) {
-        printf("[FAIL] _dyld_register_func_for_add_image: notified twice about %p\n", mh);
-        exit(0);
+        FAIL("notified twice about %p", mh);
     }
     sCurrentImages.insert(mh);
 }
 
 
-int main()
-{
-    printf("[BEGIN] _dyld_register_func_for_add_image\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     _dyld_register_func_for_add_image(&notify);
 
     // verify we were notified about already loaded images
     if ( sCurrentImages.count(&__dso_handle) == 0 ) {
-               printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about main executable");
-               exit(0);
+        FAIL("did not notify us about main executable");
     }
     const mach_header* libSysMH = dyld_image_header_containing_address((void*)&printf);
     if ( sCurrentImages.count(libSysMH) == 0 ) {
-               printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about libsystem_c.dylib");
-               exit(0);
+        FAIL("did not notify us about libsystem_c.dylib");    }
+
+    void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST);
+    if ( handle == NULL ) {
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror());
     }
 
-       void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST);
-       if ( handle == NULL ) {
-               printf("[FAIL] dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror());
-               exit(0);
-       }
-       
-       void* sym = dlsym(handle, "foo");
-       if ( sym == NULL ) {
-               printf("[FAIL] dlsym(handle, \"foo\") failed");
-               exit(0);
-       }
+    void* sym = dlsym(handle, "foo");
+    if ( sym == NULL ) {
+        FAIL("dlsym(handle, \"foo\") failed");
+    }
 
     // verify we were notified about load of libfoo.dylib
     const mach_header* libfooMH = dyld_image_header_containing_address(sym);
     if ( sCurrentImages.count(libfooMH) == 0 ) {
-               printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about libfoo.dylib");
-               exit(0);
+               FAIL("_dyld_register_func_for_add_image() did not notify us about libfoo.dylib");
     }
 
-
        int result = dlclose(handle);
        if ( result != 0 ) {
-        printf("[FAIL] dlclose(handle) returned %d", result);
-        exit(0);
+        FAIL("dlclose(handle) returned %d", result);
        }
 
-
-    printf("[PASS] _dyld_register_func_for_add_image\n");
-    return 0;
+    PASS("_dyld_register_func_for_add_image");
 }
 
index 8672bb83e4fb656df2c78c77afb4f3f6a9a2784e..838dda80c6b8fd323c42b5133d901bc7faa53871 100644 (file)
@@ -9,16 +9,12 @@
 #include <mach-o/dyld_priv.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] _dyld_shared_cache_is_locally_built\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // We can't reliably test the result of this function, but it shouldn't crash
     _dyld_shared_cache_is_locally_built();
 
-    printf("[PASS] _dyld_shared_cache_is_locally_built\n");
-
-    return 0;
+    PASS("SUCCESS");
 }
 
index 5b999d2ecf0e74c4902e12cc7e2bb87aa2fd058b..35d77cf72fcdf3c1da4c7b0c50840fea8ac5d179 100644 (file)
 #include <mach/mach.h>
 #include <mach/mach_host.h>
 
+#include "test_support.h"
+
 void tryPath(const char* prog, const char* path)
 {
     void* handle = dlopen(path, RTLD_LAZY);
 #if HARDENED
     if ( handle != NULL ) {
-        printf("[FAIL]  %s dlopen(%s) unexpectedly succeeded\n", prog, path);
+        FAIL("dlopen(%s) unexpectedly succeeded", path);
         exit(0);
     }
 #else
     if ( handle == NULL ) {
-        printf("[FAIL]  %s dlopen(%s) - %s\n", prog, path, dlerror());
+        FAIL("dlopen(%s) - %s", path, dlerror());
         exit(0);
     }
 #endif
 
 }
 
-int main(int arg, const char* argv[])
-{
-    printf("[BEGIN] %s\n", argv[0]);
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // verify leaf name leads to dylib in /usr/lib/
     void* handle = dlopen("libc.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  %s dlopen - %s\n", argv[0], dlerror());
-        return 0;
+        FAIL("dlopen - %s", dlerror());
     }
 
     // verify file system relative paths: hardened should fail
     tryPath(argv[0], "libmy.dylib");
     tryPath(argv[0], "./libmy.dylib");
     tryPath(argv[0], "../amfi-hardened-dlopen-leaf/libmy.dylib");
-
-    printf("[PASS]  %s\n", argv[0]);
-
-       return 0;
+    PASS("Succcess");
 }
 
 
index a05963713dc7a2b0e6fd6376b49394eb9103ea92..f89fe343d7c1e80d6ba01e3ae9030729b398c7c9 100644 (file)
@@ -7,6 +7,8 @@
 
 #include <stdio.h>
 
+#include "test_support.h"
+
 // Note this is weak so that we have a bind
 __attribute__((weak))
 void* p = 0;
@@ -20,21 +22,15 @@ void* pMinus = (void*)((uintptr_t)&p - offset);
 extern int objc_msgSend;
 void* msgSendMinus = (void*)((uintptr_t)&objc_msgSend - offset);
 
-int main()
-{
-    printf("[BEGIN] bind-addend\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if ( pMinus != (void*)((uintptr_t)&p - offset) ) {
-        printf("[FAIL]  bind-addend: %p != %p\n", pMinus, (void*)((uintptr_t)&p - offset));
-        return 0;
+        FAIL("bind-addend: %p != %p", pMinus, (void*)((uintptr_t)&p - offset));
     }
 
     if ( msgSendMinus != (void*)((uintptr_t)&objc_msgSend - offset) ) {
-        printf("[FAIL]  bind-addend: %p != %p\n", msgSendMinus, (void*)((uintptr_t)&objc_msgSend - offset));
-        return 0;
+        FAIL("bind-addend: %p != %p", msgSendMinus, (void*)((uintptr_t)&objc_msgSend - offset));
     }
 
-    printf("[PASS]  bind-addend\n");
-    return 0;
+    PASS("bind-addend");
 }
 
index 015bee1dd115d6d43843a28410b22a30f068ab26..78c83ceb17c7d1a7dfb13056b0feeae50869718f 100644 (file)
@@ -9,6 +9,8 @@
 #include <dlfcn.h> 
 #include <assert.h>
 
+#include "test_support.h"
+
 extern char tzname[];  // a char array in libSystem.dylib
 
 
@@ -91,9 +93,7 @@ void verifyLongChains()
 }
 #endif
 
-int main()
-{
-    printf("[BEGIN] bind-rebase\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     verifyRebases();
     verifyBinds();
 
@@ -101,7 +101,6 @@ int main()
     verifyLongChains();
 #endif
 
-    printf("[PASS]  bind-rebase\n");
-    return 0;
+    PASS("Success");
 }
 
index 698dd0a91e30e8ad54ec0184af53a32d152bee7d..35b0e779cf233368272c796c5c2c9bc8d6ed79ce 100644 (file)
@@ -14,8 +14,8 @@
 #include "foo.h"
 #include "uses.h"
 
-int main() {
-       printf("[BEGIN] chained-fixups-many-binds\n");
-       printf("[PASS] chained-fixups-many-binds\n");
-       return 0;
-}
\ No newline at end of file
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    PASS("Success");
+}
diff --git a/testing/test-cases/crt-old-mac10.5-vars-libSystem.dtest/main.c b/testing/test-cases/crt-old-mac10.5-vars-libSystem.dtest/main.c
new file mode 100644 (file)
index 0000000..2d36cf9
--- /dev/null
@@ -0,0 +1,88 @@
+// BUILD_ONLY:      MacOSX
+// BUILD_MIN_OS:    10.5
+
+// BUILD:  $CC main.c            -o $BUILD_DIR/crt-vars10.5-libSystem.exe
+
+// RUN:  ./crt-vars10.5-libSystem.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <crt_externs.h>
+#include <mach-o/ldsyms.h>
+
+#include "test_support.h"
+
+// This struct is passed as fifth parameter to libSystem.dylib's initializer so it record
+// the address of crt global variables.
+struct ProgramVars
+{
+    const void*        mh;
+    int*               NXArgcPtr;
+    char***            NXArgvPtr;
+    char***            environPtr;
+    char**             __prognamePtr;
+};
+
+
+// global variables defeined in crt1.o
+extern char**  NXArgv;
+extern int     NXArgc;
+extern char**  environ;
+extern char*   __progname;
+
+
+static const struct ProgramVars* sVars;
+
+void __attribute__((constructor))
+myInit(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars)
+{
+    sVars = vars;
+}
+
+
+int main(int argc, const char* argv[])
+{
+    if ( _NSGetArgv() != &NXArgv ) {
+        FAIL("crt-libSystem: _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]);
+    }
+
+    if ( _NSGetArgc() != &NXArgc ) {
+        FAIL("crt-libSystem: _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]);
+    }
+
+    if ( _NSGetEnviron() != &environ ) {
+        FAIL("crt-libSystem: _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]);
+    }
+
+    if ( _NSGetProgname() != &__progname ) {
+        FAIL("crt-libSystem: _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]);
+    }
+
+    if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) {
+        FAIL("crt-libSystem: _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]);
+    }
+
+    if ( sVars->NXArgvPtr != &NXArgv ) {
+        FAIL("crt-libSystem: sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]);
+    }
+
+    if ( sVars->NXArgcPtr != &NXArgc ) {
+        FAIL("crt-libSystem: sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]);
+    }
+
+    if ( sVars->environPtr != &environ ) {
+        FAIL("crt-libSystem: sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]);
+    }
+
+    if ( sVars->__prognamePtr != &__progname ) {
+        FAIL("crt-libSystem: sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]);
+    }
+
+    if ( sVars->mh != &_mh_execute_header ) {
+        FAIL("crt-libSystem: sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]);
+    }
+
+   PASS("Success");
+}
+
diff --git a/testing/test-cases/crt-old-mac10.6-vars-libSystem.dtest/main.c b/testing/test-cases/crt-old-mac10.6-vars-libSystem.dtest/main.c
new file mode 100644 (file)
index 0000000..ee3d8b3
--- /dev/null
@@ -0,0 +1,88 @@
+// BUILD_ONLY:      MacOSX
+// BUILD_MIN_OS:    10.6
+
+// BUILD:  $CC main.c            -o $BUILD_DIR/crt-vars10.6-libSystem.exe
+
+// RUN:  ./crt-vars10.6-libSystem.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <crt_externs.h>
+#include <mach-o/ldsyms.h>
+
+#include "test_support.h"
+
+// This struct is passed as fifth parameter to libSystem.dylib's initializer so it record
+// the address of crt global variables.
+struct ProgramVars
+{
+    const void*        mh;
+    int*               NXArgcPtr;
+    char***            NXArgvPtr;
+    char***            environPtr;
+    char**             __prognamePtr;
+};
+
+
+// global variables defeined in crt1.o
+extern char**  NXArgv;
+extern int     NXArgc;
+extern char**  environ;
+extern char*   __progname;
+
+
+static const struct ProgramVars* sVars;
+
+void __attribute__((constructor))
+myInit(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars)
+{
+    sVars = vars;
+}
+
+
+int main(int argc, const char* argv[])
+{
+    if ( _NSGetArgv() != &NXArgv ) {
+        FAIL("crt-libSystem: _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]);
+    }
+
+    if ( _NSGetArgc() != &NXArgc ) {
+        FAIL("crt-libSystem: _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]);
+    }
+
+    if ( _NSGetEnviron() != &environ ) {
+        FAIL("crt-libSystem: _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]);
+    }
+
+    if ( _NSGetProgname() != &__progname ) {
+        FAIL("crt-libSystem: _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]);
+    }
+
+    if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) {
+        FAIL("crt-libSystem: _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]);
+    }
+
+    if ( sVars->NXArgvPtr != &NXArgv ) {
+        FAIL("crt-libSystem: sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]);
+    }
+
+    if ( sVars->NXArgcPtr != &NXArgc ) {
+        FAIL("crt-libSystem: sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]);
+    }
+
+    if ( sVars->environPtr != &environ ) {
+        FAIL("crt-libSystem: sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]);
+    }
+
+    if ( sVars->__prognamePtr != &__progname ) {
+        FAIL("crt-libSystem: sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]);
+    }
+
+    if ( sVars->mh != &_mh_execute_header ) {
+        FAIL("crt-libSystem: sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]);
+    }
+
+    PASS("Success");
+}
+
index 6eb9381136631829a261c25b3d1870334075d610..9cf47756d6ec4e043868d013a54a8cb7532a7f2d 100644 (file)
@@ -9,6 +9,8 @@
 #include <crt_externs.h>
 #include <mach-o/ldsyms.h>
 
+#include "test_support.h"
+
 // This struct is passed as fifth parameter to libSystem.dylib's initializer so it record
 // the address of crt global variables.
 struct ProgramVars
@@ -36,65 +38,46 @@ myInit(int argc, const char* argv[], const char* envp[], const char* apple[], co
     sVars = vars;
 }
 
-
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] crt-vars-libSystem\n");
-    bool success = true;
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if ( _NSGetArgv() != &NXArgv ) {
-        printf("[FAIL] crt-libSystem: _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]);
-        success = false;
+        FAIL("_NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]);
     }
 
     if ( _NSGetArgc() != &NXArgc ) {
-        printf("[FAIL] crt-libSystem: _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]);
-        success = false;
+        FAIL("_NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]);
     }
 
     if ( _NSGetEnviron() != &environ ) {
-        printf("[FAIL] crt-libSystem: _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]);
-        success = false;
+        FAIL("_NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]);
     }
 
     if ( _NSGetProgname() != &__progname ) {
-        printf("[FAIL] crt-libSystem: _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]);
-        success = false;
+        FAIL("_NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]);
     }
 
     if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) {
-        printf("[FAIL] crt-libSystem: _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]);
-        success = false;
+        FAIL("_NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]);
     }
 
     if ( sVars->NXArgvPtr != &NXArgv ) {
-        printf("[FAIL] crt-libSystem: sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]);
-        success = false;
+        FAIL("sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]);
     }
 
     if ( sVars->NXArgcPtr != &NXArgc ) {
-        printf("[FAIL] crt-libSystem: sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]);
-        success = false;
+        FAIL("sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]);
     }
 
     if ( sVars->environPtr != &environ ) {
-        printf("[FAIL] crt-libSystem: sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]);
-        success = false;
+        FAIL("sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]);
     }
 
     if ( sVars->__prognamePtr != &__progname ) {
-        printf("[FAIL] crt-libSystem: sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]);
-        success = false;
+        FAIL("sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]);
     }
 
     if ( sVars->mh != &_mh_execute_header ) {
-        printf("[FAIL] crt-libSystem: sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]);
-        success = false;
+        FAIL("sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]);
     }
-
-    if ( success )
-        printf("[PASS] crt-vars-libSystem\n");
-
-    return 0;
+    PASS("Success");
 }
 
index 8f2042ffb317c62f221d2ce160ff00db51de3c1f..b111390095ef6ad426cfcd671985a18b9a4ea09b 100644 (file)
 
 #include <stdio.h>
 
+#include "test_support.h"
+
 extern int foo;
 
 
-int main()
-{
-    printf("[BEGIN] cwd-relative-load\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if ( foo == 42 )
-        printf("[PASS] cwd-relative-load\n");
+        PASS("cwd-relative-load");
     else
-        printf("[FAIL] cwd-relative-load, wrong value\n");
-
-       return 0;
+        FAIL("cwd-relative-load, wrong value");
 }
 
 
index 672595da577898ee298de9b59f9c2507783e43ea..baef3f6ffcbd3d8e52915679ee372ef195285914 100644 (file)
@@ -1,6 +1,6 @@
 
 // BUILD:  $CC main-no-syms.c            -o $BUILD_DIR/dladdr-stripped.exe
-// BUILD:  strip $BUILD_DIR/dladdr-stripped.exe
+// BUILD:  $STRIP $BUILD_DIR/dladdr-stripped.exe
 
 // RUN:  ./dladdr-stripped.exe
 
 #include <dlfcn.h>
 #include <mach-o/dyld_priv.h>
 
-
+#include "test_support.h"
 
 ///
 /// verify dladdr() returns NULL for a symbol name in a fully stripped 
 /// main executable (and not _mh_execute_header+nnn).
 ///
 
-int main()
-{
-    printf("[BEGIN] dladdr-stripped\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     Dl_info info;
     if ( dladdr(&main, &info) == 0 ) {
-        printf("[FAIL] dladdr(&main, xx) failed\n");
-        return 0;
+        FAIL("dladdr(&main, xx) failed");
     }
 
     if ( info.dli_sname != NULL ){
-        printf("[FAIL] dladdr() returned: \"%s\" instead of NULL\n", info.dli_sname);
-        return 0;
+        FAIL("%s\" instead of NULL", info.dli_sname);
     }
 
-    printf("[PASS] dladdr-stripped\n");
-    return 0;
+    PASS("Succes");
 }
index 5f2e67ce2d62292cedc37edbeb9b9ff0469e1ed3..4ee8adacd56f3a80fa0c12cfd384b2fcb7cd33fd 100644 (file)
@@ -9,6 +9,8 @@
 #include <dlfcn.h> 
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
+
 extern char** environ;
 
 #if __has_feature(ptrauth_calls)
@@ -45,20 +47,16 @@ static void verifybar()
 {
     Dl_info info;
     if ( dladdr(&bar, &info) == 0 ) {
-        printf("[FAIL] dladdr(&bar, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&bar, xx) failed");
     }
     if ( strcmp(info.dli_sname, "bar") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname);
     }
     if ( info.dli_saddr != stripPointer(&bar) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &bar\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &bar");
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&bar) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &bar");
     }
 }
 
@@ -67,20 +65,17 @@ static void verifyfoo()
 {
     Dl_info info;
     if ( dladdr(&foo, &info) == 0 ) {
-        printf("[FAIL] dladdr(&foo, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&foo, xx) failed");
     }
     if ( strcmp(info.dli_sname, "foo") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"foo\"", info.dli_sname);
         exit(0);
     }
     if ( info.dli_saddr != stripPointer(&foo) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &foo\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &foo");
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&foo) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &foo");
     }
 }
 
@@ -89,20 +84,16 @@ static void verifyhide()
 {
     Dl_info info;
     if ( dladdr(&hide, &info) == 0 ) {
-        printf("[FAIL] dladdr(&hide, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&hide, xx) failed");
     }
     if ( strcmp(info.dli_sname, "hide") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"hide\"", info.dli_sname);
     }
     if ( info.dli_saddr != stripPointer(&hide) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &hide\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &hide");
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&hide) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &hide");
     }
 }
 
@@ -111,20 +102,17 @@ static void verifymalloc()
 {
     Dl_info info;
     if ( dladdr(&malloc, &info) == 0 ) {
-        printf("[FAIL] dladdr(&malloc, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&malloc, xx) failed");
     }
     if ( strcmp(info.dli_sname, "malloc") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"malloc\"", info.dli_sname);
         exit(0);
     }
     if ( info.dli_saddr != stripPointer(&malloc) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &malloc\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &malloc");
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &malloc");
     }
 }
 
@@ -133,20 +121,16 @@ static void verifyenviron()
 {
     Dl_info info;
     if ( dladdr(&environ, &info) == 0 ) {
-        printf("[FAIL] dladdr(&environ, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&environ, xx) failed");
     }
     if ( strcmp(info.dli_sname, "environ") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"environ\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"environ\"", info.dli_sname);
     }
     if ( info.dli_saddr != &environ ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &environ\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &environ");
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&environ) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &environ\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &environ");
     }
 }
 
@@ -156,20 +140,16 @@ static void verifymydata()
 {
     Dl_info info;
     if ( dladdr(&mydata, &info) == 0 ) {
-        printf("[FAIL] dladdr(&mydata, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&mydata, xx) failed");
     }
     if ( strcmp(info.dli_sname, "mydata") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"mydata\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"mydata\"", info.dli_sname);
     }
     if ( info.dli_saddr != &mydata ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &mydata\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &mydata");
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&mydata) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &mydata\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &mydata");
     }
 }
 
@@ -179,18 +159,14 @@ static void verifyNULL()
 {
     Dl_info info;
     if ( dladdr(&malloc, NULL) != 0 ) {
-        printf("[FAIL] dladdr(&malloc, NULL) did not fail\n");
-        exit(0);
+        FAIL("dladdr(&malloc, NULL) did not fail");
     }
     if ( dladdr(NULL, NULL) != 0 ) {
-        printf("[FAIL] dladdr(NULL, NULL) did not fail\n");
-        exit(0);
+        FAIL("dladdr(NULL, NULL) did not fail");
     }
 }
 
-int main()
-{
-    printf("[BEGIN] dladdr-basic\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     verifybar();
     verifyhide();
     verifyfoo();
@@ -199,7 +175,5 @@ int main()
     verifymydata();
     verifyNULL();
 
-    printf("[PASS] dladdr-basic\n");
-    return 0;
-}
+    PASS("Success");}
 
index c62f8c639bf6bba6e3ce7983e4b2c7e94b751b0e..1135218728b60a5cfba68e57fa47d77b1d11aa87 100644 (file)
@@ -9,6 +9,8 @@
     #include <ptrauth.h>
 #endif
 
+#include "test_support.h"
+
 extern void* __dso_handle;
 
 
@@ -42,20 +44,16 @@ static void verifybar()
 {
     Dl_info info;
     if ( dladdr(&dylib_bar, &info) == 0 ) {
-        printf("[FAIL] dladdr(&dylib_bar, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&dylib_bar, xx) failed");
     }
     if ( strcmp(info.dli_sname, "dylib_bar") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_bar\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"dylib_bar\"", info.dli_sname);
     }
     if ( info.dli_saddr != stripPointer(&dylib_bar) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &dylib_bar\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &dylib_bar");
     }
     if ( info.dli_fbase != &__dso_handle ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_bar\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &dylib_bar");
     }
 }
 
@@ -64,20 +62,16 @@ static void verifyfoo()
 {
     Dl_info info;
     if ( dladdr(&dylib_foo, &info) == 0 ) {
-        printf("[FAIL] dladdr(&dylib_foo, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&dylib_foo, xx) failed");
     }
     if ( strcmp(info.dli_sname, "dylib_foo") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_foo\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"dylib_foo\"", info.dli_sname);
     }
     if ( info.dli_saddr != stripPointer(&dylib_foo) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &dylib_foo\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &dylib_foo");
     }
     if ( info.dli_fbase != &__dso_handle ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_foo\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &dylib_foo");
     }
 }
 
@@ -86,20 +80,16 @@ static void verifyhide()
 { 
     Dl_info info;
     if ( dladdr(&dylib_hide, &info) == 0 ) {
-        printf("[FAIL] dladdr(&dylib_hide, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&dylib_hide, xx) failed");
     }
     if ( strcmp(info.dli_sname, "dylib_hide") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_hide\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"dylib_hide\"", info.dli_sname);
     }
     if ( info.dli_saddr != stripPointer(&dylib_hide) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &dylib_hide\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &dylib_hide");
     }
     if ( info.dli_fbase != &__dso_handle ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_hide\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &dylib_hide");
     }
 }
 
@@ -108,20 +98,16 @@ static void verifyDSOHandle()
 {
     Dl_info info;
     if ( dladdr(&__dso_handle, &info) == 0 ) {
-        printf("[FAIL] dladdr(&__dso_handle, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&__dso_handle, xx) failed");
     }
     if ( strcmp(info.dli_sname, "__dso_handle") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"__dso_handle\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"__dso_handle\"", info.dli_sname);
     }
     if ( info.dli_saddr != stripPointer(&__dso_handle) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &__dso_handle\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &__dso_handle");
     }
     if ( info.dli_fbase != &__dso_handle ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &__dso_handle\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &__dso_handle");
     }
 }
 
index a4401abf3d38e306da8c2891f925a999a9d1c3ca..6a56135eb4b41929f2c5ecefdeaeb35d59d7611d 100644 (file)
@@ -13,6 +13,8 @@
     #include <ptrauth.h>
 #endif
 
+#include "test_support.h"
+
 extern void* __dso_handle;
 
 extern void verifyDylib();
@@ -48,20 +50,16 @@ static void verifybar()
 {
     Dl_info info;
     if ( dladdr(&bar, &info) == 0 ) {
-        printf("[FAIL] dladdr(&bar, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&bar, xx) failed");
     }
     if ( strcmp(info.dli_sname, "bar") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname);
     }
     if ( info.dli_saddr != stripPointer(&bar) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &bar\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &bar");
     }
     if ( info.dli_fbase != &__dso_handle ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &bar");
     }
 }
 
@@ -70,20 +68,16 @@ static void verifyfoo()
 {
     Dl_info info;
     if ( dladdr(&foo, &info) == 0 ) {
-        printf("[FAIL] dladdr(&foo, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&foo, xx) failed");
     }
     if ( strcmp(info.dli_sname, "foo") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"foo\"", info.dli_sname);
     }
     if ( info.dli_saddr != stripPointer(&foo) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &foo\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &foo");
     }
     if ( info.dli_fbase != &__dso_handle ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &foo");
     }
 }
 
@@ -92,20 +86,16 @@ static void verifyhide()
 {
     Dl_info info;
     if ( dladdr(&hide, &info) == 0 ) {
-        printf("[FAIL] dladdr(&hide, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&hide, xx) failed");
     }
     if ( strcmp(info.dli_sname, "hide") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"hide\"", info.dli_sname);
     }
     if ( info.dli_saddr != stripPointer(&hide) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &hide\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &hide");
     }
     if ( info.dli_fbase != &__dso_handle ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &hide");
     }
 }
 
@@ -114,27 +104,21 @@ static void verifymalloc()
 {
     Dl_info info;
     if ( dladdr(&malloc, &info) == 0 ) {
-        printf("[FAIL] dladdr(&malloc, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&malloc, xx) failed");
     }
     if ( strcmp(info.dli_sname, "malloc") != 0 ) {
-        printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname);
-        exit(0);
+        FAIL("dladdr()->dli_sname is \"%s\" instead of \"malloc\"", info.dli_sname);
     }
     if ( info.dli_saddr != stripPointer(&malloc) ) {
-        printf("[FAIL] dladdr()->dli_saddr is not &malloc\n");
-        exit(0);
+        FAIL("dladdr()->dli_saddr is not &malloc");
     }
     if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) {
-        printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc\n");
-        exit(0);
+        FAIL("dladdr()->dli_fbase is not image that contains &malloc");
     }
 }
 
 
-int main()
-{
-    printf("[BEGIN] dladdr-dylib\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     verifybar();
     verifyhide();
     verifyfoo();
@@ -142,7 +126,6 @@ int main()
 
     verifyDylib();
 
-    printf("[PASS] dladdr-dylib\n");
-    return 0;
+    PASS("Success");
 }
 
index dd19bbfd23651a4bfe3dd52f2841aae9e629238d..da4a01aff48f6d137b1787530887d99a02b37c39 100644 (file)
@@ -9,6 +9,7 @@
 #include <dlfcn.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
 // verify dlclose() runs static terminator
 
@@ -21,22 +22,17 @@ static void termNotifyFunc()
     termDidRun = true;
 }
 
-int main()
-{
-    printf("[BEGIN] dlclose-static-terminator\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // load dylib
     void* handle = dlopen(RUN_DIR "/libterm.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  dlclose-static-terminator: libterm.dylib could not be loaded, %s\n", dlerror());
-        return 0;
+        FAIL("libterm.dylib could not be loaded, %s", dlerror());
     }
 
     // stuff pointer to my notifier
     NotifyProc* pointerAddress = (NotifyProc*)dlsym(handle, "gNotifer");
     if ( pointerAddress == NULL ) {
-        printf("[FAIL]  dlclose-static-terminator: gNotifer not found in libterm.dylib\n");
-        return 0;
+        FAIL("gNotifer not found in libterm.dylib");
     }
     *pointerAddress = &termNotifyFunc;
 
@@ -44,10 +40,8 @@ int main()
     dlclose(handle);
 
     if ( termDidRun )
-        printf("[PASS]  dlclose-static-terminator\n");
+        PASS("Success");
     else
-        printf("[FAIL]  dlclose-static-terminator: terminator not run\n");
-
-       return 0;
+        FAIL("terminator not run");
 }
 
index 431c63c9bcb5995b945303bf3991607e95c2602c..53d222fa551879f6395ed5bb1c3bde05d14e1f6a 100644 (file)
@@ -1,5 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2
 // BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/door1/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=3
 // BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/door2/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=17
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=10  $BUILD_DIR/door1/libbar.dylib
 #include <dlfcn.h>
 #include <stdlib.h>
 
+#include "test_support.h"
+
 // Program dlopen()s libfoo.dylib which was linked against libbar.dylib
 // Neither have valid paths and must be found via DYLD_LIBRARY_PATH
 // This test direct and indirect loading.
 
-int main(int argc, const char* argv[])
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     const char* env = getenv("DYLD_LIBRARY_PATH");
     if ( env == NULL ) {
-        printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n");
-        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, env not set\n");
-               return 0;
+        FAIL("env not set");
     }
     const char* valueStr = argv[1];
     if ( valueStr == NULL ) {
-        printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n");
-        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, arg1 value not set\n");
-               return 0;
+        FAIL("arg1 value not set");
     }
     char* end;
     long value = strtol(valueStr, &end, 0);
 
-    printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH %s\n", env);
 
-       void* handle = dlopen("/bogus/libfoo.dylib", RTLD_LAZY);
-       if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
-               return 0;
-       }
-       
+    void* handle = dlopen("/bogus/libfoo.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        FAIL("dlerror(\"/bogus/libfoo.dylib\"): %s", dlerror());
+    }
+
     typedef int (*FooProc)();
 
-       FooProc sym = (FooProc)dlsym(handle, "foo");
-       if ( sym == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
-               return 0;
-       }
+    FooProc sym = (FooProc)dlsym(handle, "foo");
+    if ( sym == NULL ) {
+        FAIL("dlerror(): %s", dlerror());
+    }
 
     int result = (*sym)();
     if ( result != value ) {
-        printf("result=%d, expected %ld (str=%s)\n", result, value, valueStr);
-        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
-               return 0;
-       }
+        FAIL("result=%d, expected %ld (str=%s)", result, value, valueStr);
+    }
 
        int r = dlclose(handle);
        if ( r != 0 ) {
-        printf("dlclose() returned %d\n", r);
-        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
-               return 0;
-       }
-
-       void* handle2 = dlopen("/junk/libfoo.dylib", RTLD_LAZY);
-       if ( handle2 == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
-               return 0;
-       }
-
-
+        FAIL("dlclose() returned %d", r);
+    }
 
-    printf("[PASS] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+    void* handle2 = dlopen("/junk/libfoo.dylib", RTLD_LAZY);
+    if ( handle2 == NULL ) {
+        FAIL("dlerror(\"/junk/libfoo.dylib\"): %s", dlerror());
+    }
 
-       return 0;
+    PASS("Success");
 }
 
index 8c91cb585060bd05703feeb9a6da9bcd3fcc4bc9..a4d11b59a0aa0f34ff2e4c1efb8f491221f6425c 100644 (file)
@@ -11,6 +11,7 @@
 #include <string.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
 ///
 /// This tests the interaction of RTLD_LOCAL and weak-def coalescing.
@@ -32,10 +33,7 @@ typedef int (*IntProc)(void);
 
 int __attribute__((weak)) coalA = 0;
 
-int main()
-{
-    printf("[BEGIN] dlopen-RTLD_LOCAL-coalesce\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     ///
     /// Load three foo dylibs in order
     ///
@@ -43,19 +41,15 @@ int main()
     void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_LOCAL);
     void* handle3 = dlopen(RUN_DIR "/libfoo3.dylib", RTLD_GLOBAL);
     if ( handle1 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlopen(libfoo1.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(\"libfoo1.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror());
     }
     if ( handle2 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlopen(libfoo2.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(\"libfoo2.dylib\", RTLD_LOCAL) failed but it should have worked: %s", dlerror());
     }
     if ( handle3 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlopen(libfoo3.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(\"libfoo3.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror());
     }
 
-    
     ///
     /// Get accessor functions
     ///
@@ -70,8 +64,7 @@ int main()
     if ( !foo1_coalA || !foo1_coalB ||
          !foo2_coalA || !foo2_coalB || !foo2_coalC ||
          !foo3_coalA || !foo3_coalB || !foo3_coalC ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlsym() failed\n");
-        return 0;
+        FAIL("dlsym() failed");
     }
 
     ///
@@ -85,38 +78,30 @@ int main()
     int foo3A = (*foo3_coalA)();
     int foo3B = (*foo3_coalB)();
     int foo3C = (*foo3_coalC)();
-    printf("coalA in main:    %d\n", coalA);
-    printf("coalA in libfoo1: %d\n", foo1A);
-    printf("coalA in libfoo2: %d\n", foo2A);
-    printf("coalA in libfoo3: %d\n", foo3A);
-
-    printf("coalB in libfoo1: %d\n", foo1B);
-    printf("coalB in libfoo2: %d\n", foo2B);
-    printf("coalB in libfoo3: %d\n", foo3B);
-
-    printf("coalC in libfoo2: %d\n", foo2C);
-    printf("coalC in libfoo3: %d\n", foo3C);
+    LOG("coalA in main:    %d", coalA);
+    LOG("coalA in libfoo1: %d", foo1A);
+    LOG("coalA in libfoo2: %d", foo2A);
+    LOG("coalA in libfoo3: %d", foo3A);
 
+    LOG("coalB in libfoo1: %d", foo1B);
+    LOG("coalB in libfoo2: %d", foo2B);
+    LOG("coalB in libfoo3: %d", foo3B);
 
+    LOG("coalC in libfoo2: %d", foo2C);
+    LOG("coalC in libfoo3: %d", foo3C);
 
     ///
     /// Verify coalescing was done properly (foo2 was skipped because of RTLD_LOCAL)
     ///
     if ( (foo1A != 0) || (foo2A != 0) || (foo3A != 0) || (coalA != 0) ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalA was not coalesced properly\n");
-        return 0;
+        FAIL("coalA was not coalesced properly");
     }
     if ( (foo1B != 1) || (foo2B != 1) || (foo3B != 1) ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalB was not coalesced properly\n");
-        return 0;
+        FAIL("coalB was not coalesced properly");
     }
     if ( (foo2C != 2) || (foo3C != 3) ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalC was not coalesced properly\n");
-        return 0;
+        FAIL("coalC was not coalesced properly");
     }
 
-
-
-    printf("[PASS] dlopen-RTLD_LOCAL-coalesce\n");
-    return 0;
+    PASS("Success");
 }
index 79c113ae8540a57eacdc217f0ae4709057f6d1e9..b358dc9f09369d95828ffef84661702f4059b3cf 100644 (file)
 #include <string.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] dlopen-RTLD_LOCAL-hides\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     ///
     /// This tests that RTLD_LOCAL prevents RTLD_DEFAULT from finding symbols, but can be found via handle
     ///
     void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LOCAL);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libfoo.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(\"libfoo.dylib\", RTLD_LOCAL) failed but it should have worked: %s", dlerror());
     }
     void* sym = dlsym(handle, "foo");
     if ( sym == NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", dlerror());
     }
     void* sym2 = dlsym(RTLD_DEFAULT, "foo");
     if ( sym2 != NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"foo\") succeeded but it should have failed\n");
-        return 0;
+        FAIL("dlsym(RTLD_DEFAULT, \"foo\") succeeded but it should have failed");
     }
 
-
     ///
     /// This tests that RTLD_GLOBAL after RTLD_LOCAL allows RTLD_DEFAULT to find symbols
     ///
     void* handle2 = dlopen(RUN_DIR "/libfoo.dylib", RTLD_GLOBAL);
     if ( handle2 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libfoo.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(\"libfoo.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror());
     }
     void* sym3 = dlsym(RTLD_DEFAULT, "foo");
     if ( sym3 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"foo\") failed after upgrading to RTLD_GLOBAL\n");
-        return 0;
+        FAIL("dlsym(RTLD_DEFAULT, \"foo\") failed after upgrading to RTLD_GLOBAL");
     }
 
-
     ///
     /// This tests that RTLD_LOCAL after RTLD_GLOBAL does not block RTLD_DEFAULT from finding symbols
     ///
     void* handle3 = dlopen(RUN_DIR "/libbar.dylib", RTLD_GLOBAL);
     if ( handle3 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libbar.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(\"libbar.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror());
     }
     void* handle4 = dlopen(RUN_DIR "/libbar.dylib", RTLD_LOCAL);
     if ( handle4 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libbar.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(\"libbar.dylib\", RTLD_LOCAL) failed but it should have worked: %s", dlerror());
     }
     void* sym4 = dlsym(RTLD_DEFAULT, "bar");
     if ( sym4 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"bar\") failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlsym(RTLD_DEFAULT, \"bar\") failed but it should have worked: %s", dlerror());
     }
 
-    printf("[PASS] dlopen-RTLD_LOCAL-hides\n");
-    return 0;
+    PASS("Success");
 }
index 3fff11733ab48c57ce4081a7d6283610803f2ac2..abb8a3a0592d42a967588f3bceae020497ec3272 100644 (file)
 #include <string.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] dlopen-RTLD_NODELETE\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     ///
     /// This tests that RTLD_NODELETE on first dlopen() blocks dlclose() from unloading image
     ///
     void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NODELETE);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen-RTLD_NODELETE: dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s", dlerror());
     }
     int* fooSym = (int*)dlsym(handle, "foo");
     if ( fooSym == NULL ) {
-        printf("[FAIL] dlopen-RTLD_NODELETE: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", dlerror());
     }
     int fooValue = *fooSym;
     dlclose(handle);
     Dl_info info;
     if ( dladdr(fooSym, &info) != 0 ) {
-        printf("[FAIL] dlopen-RTLD_NODELETE: dladdr(fooSym, xx) succeeded as if libfoo.dylib was not unloaded\n");
-        return 0;
+        FAIL("dladdr(fooSym, xx) succeeded as if libfoo.dylib was not unloaded");
     }
     // dereference foo pointer.  If RTLD_NODELETE worked, this will not crash
     if ( *fooSym != fooValue ) {
-        printf("[FAIL] dlopen-RTLD_NODELETE: value at fooSym changed\n");
-        return 0;
+        FAIL("value at fooSym changed");
     }
 
     ///
@@ -46,33 +40,26 @@ int main()
     ///
     void* handle2 = dlopen(RUN_DIR "/libbar.dylib", RTLD_GLOBAL);
     if ( handle2 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_NODELETE: dlopen(libfoo.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(libfoo.dylib, RTLD_GLOBAL) failed but it should have worked: %s", dlerror());
     }
     int* barSym = (int*)dlsym(handle2, "bar");
     if ( barSym == NULL ) {
-        printf("[FAIL] dlopen-RTLD_NODELETE: dlsym(handle, \"bar\") failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlsym(handle, \"bar\") failed but it should have worked: %s", dlerror());
     }
     int barValue = *barSym;
     void* handle3 = dlopen(RUN_DIR "/libbar.dylib", RTLD_NODELETE);
     if ( handle3 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_NODELETE: dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s", dlerror());
     }
     dlclose(handle2);
     dlclose(handle3);
     if ( dladdr(barSym, &info) != 0 ) {
-        printf("[FAIL] dlopen-RTLD_NODELETE: dladdr(barSym, xx) succeeded as if libbar.dylib was not unloaded\n");
-        return 0;
+        FAIL("dladdr(barSym, xx) succeeded as if libbar.dylib was not unloaded");
     }
     // dereference foo pointer.  If RTLD_NODELETE worked, this will not crash
     if ( *barSym != barValue ) {
-        printf("[FAIL] dlopen-RTLD_NODELETE: value at barSym changed\n");
-        return 0;
+        FAIL("value at barSym changed");
     }
 
-
-    printf("[PASS] dlopen-RTLD_NODELETE\n");
-    return 0;
+    PASS("Success");
 }
index 81bc32eb783c47c3b42868867f61aa84ea561bd3..c5752e515c5f7a148ed37beeaadf470c97279b80 100644 (file)
@@ -4,19 +4,21 @@
 #include <stdio.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
+
 bool doneInitB = false;
 bool inInitB = false;
 
 
 __attribute__((constructor))
-void initB()
+void initB(int argc, const char* argv[], const char* envp[], const char* apple[])
 {
     inInitB = true;
 
     // "upward" link to libInitA.dylib
     void* handle = dlopen(RUN_DIR "/libInitA.dylib", RTLD_NOLOAD);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: dlopen(libInitA.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror());
+        FAIL("dlopen(\"libInitA.dylib\", RTLD_NOLOAD) failed but it should have worked: %s", dlerror());
         return;
     }
     inInitB = false;
index 41201eac5c640f14996cadc446e2f8f35eca5af2..036d1022501857fe20570f61a3462b178ac2575f 100644 (file)
 #include <stdbool.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
 extern bool initsInWrongOrder;
 extern bool allInitsDone();
 
-int main()
-{
-    printf("[BEGIN] dlopen-RTLD_NOLOAD-in-initializer\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     ///
     /// This tests that using RTLD_NOLOAD in an initializer does not trigger out of order initializers
     ///
     if ( initsInWrongOrder )
-        printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: wrong init order\n");
+        FAIL("wrong init order");
     else if ( !allInitsDone() )
-        printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: all initializers not run\n");
+        FAIL("all initializers not run");
     else
-        printf("[PASS] dlopen-RTLD_NOLOAD-in-initializer\n");
-
-    return 0;
+        PASS("Success");
 }
index 8fda4867d35263a498bca26b419f7f9755c3985b..7eef719fbc5ec652632ad28475f0024e1bde4503 100644 (file)
@@ -1,7 +1,7 @@
 
 // BUILD:  $CC foo.c -dynamiclib  -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
 // BUILD:  $CC main.c -DRUN_DIR="$RUN_DIR" $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dlopen-RTLD_NOLOAD-basic.exe
-// BUILD:  cd $BUILD_DIR && ln -s libfoo.dylib libfoo-sym.dylib
+// BUILD:  $SYMLINK libfoo.dylib $BUILD_DIR/libfoo-sym.dylib
 
 // RUN:  ./dlopen-RTLD_NOLOAD-basic.exe
 
 #include <string.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] dlopen-RTLD_NOLOAD-basic\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     ///
     /// This tests that RTLD_NOLOAD finds existing dylib statically linked
     ///
     void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOLOAD);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libfoo.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(\"libfoo.dylib\", RTLD_NOLOAD) failed but it should have worked: %s", dlerror());
     }
     void* sym = dlsym(handle, "foo");
     if ( sym == NULL ) {
-        printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", dlerror());
     }
 
     ///
@@ -34,8 +30,7 @@ int main()
     ///
     void* handle2 = dlopen(RUN_DIR "/libfobbulate.dylib", RTLD_NOLOAD);
     if ( handle2 != NULL ) {
-        printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libfobbulate.dylib, RTLD_NOLOAD) succeeded but it should have failed\n");
-        return 0;
+        FAIL("dlopen(\"libfobbulate.dylib\", RTLD_NOLOAD) succeeded but it should have failed");
     }
 
 
@@ -44,8 +39,7 @@ int main()
     ///
     void* handle3 = dlopen(RUN_DIR "/libfoo-sym.dylib", RTLD_NOLOAD);
     if ( handle3 == NULL ) {
-        printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libfoo-sym.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(\"libfoo-sym.dylib\", RTLD_NOLOAD) failed but it should have worked: %s", dlerror());
     }
 
 
@@ -54,11 +48,8 @@ int main()
     ///
     void* handle4 = dlopen("/usr/lib/libz.1.dylib", RTLD_NOLOAD);
     if ( handle4 != NULL ) {
-        printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libz.dylib, RTLD_NOLOAD) worked but it should have failed\n");
-        return 0;
+        FAIL("dlopen(\"libz.dylib\", RTLD_NOLOAD) worked but it should have failed");
     }
 
-
-    printf("[PASS] dlopen-RTLD_NOLOAD-basic\n");
-    return 0;
+    PASS("Success");
 }
index 29f9b3194e094b521f2aad9bd3b5dce47751c88f..385ea0324cc73666d7592632ac9d6eadae3ac948 100644 (file)
@@ -1,9 +1,11 @@
 
 // BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/libbar.dylib         -install_name $RUN_DIR/libbar.dylib
-// BUILD:  $CC bar.c -dynamiclib -o $TEMP_DIR/libbar-present.dylib  -install_name $RUN_DIR/libbar.dylib -DHAS_SYMBOL=1
-// BUILD:  $CC foo.c -dynamiclib -Os -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib $TEMP_DIR/libbar-present.dylib
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/libbar-present.dylib  -install_name $RUN_DIR/libbar.dylib -DHAS_SYMBOL=1
+// BUILD:  $CC foo.c -dynamiclib -Os -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib $BUILD_DIR/libbar-present.dylib
 // BUILD:  $CC main.c -o $BUILD_DIR/dlopen-RTLD_NOW.exe -DRUN_DIR="$RUN_DIR"
 
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libbar-present.dylib
+
 // RUN:  ./dlopen-RTLD_NOW.exe
 
 #include <stdio.h>
 #include <dlfcn.h>
 #include <mach-o/getsect.h>
 
+#include "test_support.h"
+
 #if __LP64__
 extern struct mach_header_64 __dso_handle;
 #else
 extern struct mach_header __dso_handle;
 #endif
 
-int main()
-{
-    printf("[BEGIN] dlopen-RTLD_NOW\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     ///
     /// This tests that RTLD_NOW on dlopen() will return NULL because call from libfoo to libbar could not be bound
     ///
     void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOW);
     if ( handle != NULL ) {
-        printf("[FAIL] dlopen-RTLD_NOW: dlopen(libfoo.dylib, RTLD_NOW) should have failed\n");
-        return 0;
+        FAIL("dlopen(\"libfoo.dylib\", RTLD_NOW) should have failed");
     }
 
 
@@ -53,18 +53,14 @@ int main()
     handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
     if ( supportsLazyBinding ) {
         if ( handle == NULL ) {
-            printf("[FAIL] dlopen-RTLD_NOW: dlopen(libfoo.dylib, RTLD_LAZY) should have succeeded: %s\n", dlerror());
-            return 0;
+            FAIL("dlopen(\"libfoo.dylib\", RTLD_LAZY) should have succeeded: %s", dlerror());
         }
     }
     else {
         if ( handle != NULL ) {
-            printf("[FAIL] dlopen-RTLD_NOW: dlopen(libfoo.dylib, RTLD_LAZY) should have failed becuase a symbol was missing\n");
-            return 0;
+            FAIL("dlopen(\"libfoo.dylib\", RTLD_LAZY) should have failed becuase a symbol was missing");
         }
     }
 
-
-    printf("[PASS] dlopen-RTLD_NOW\n");
-    return 0;
+    PASS("Success");
 }
index bd55fb4d24cf59d3fd9365d2e914346932c464dd..a49cf34a515a01a51e87e0aa748b8ee15d29ac22 100644 (file)
@@ -1,27 +1,20 @@
 // BUILD_ONLY: MacOSX
 
-// BUILD:  mkdir -p $BUILD_DIR/test1
 // BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/test1/libtest1.dylib -install_name @rpath/libtest1.dylib
 // BUILD:  $CC foo.c -bundle -o $BUILD_DIR/test1.bundle -Wl,-rpath,@loader_path/test1/ $BUILD_DIR/test1/libtest1.dylib
 
-// BUILD:  mkdir -p $BUILD_DIR/test2
 // BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/test2/libtest2.dylib -install_name @loader_path/test2/libtest2.dylib
 // BUILD:  $CC foo.c -bundle -o $BUILD_DIR/test2.bundle $BUILD_DIR/test2/libtest2.dylib
 
-// BUILD:  mkdir -p $BUILD_DIR/test3
 // BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/test3/libtest3.dylib -install_name @rpath/libtest3.dylib
 // BUILD:  $CC foo.c -bundle -o $BUILD_DIR/test3.bundle -Wl,-rpath,$RUN_DIR/test3  $BUILD_DIR/test3/libtest3.dylib 
 
-// BUILD:  mkdir -p $BUILD_DIR/test4
 // BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/test4/libtest4.dylib -install_name @rpath/libtest4.dylib
 // BUILD:  $CC foo.c -bundle -o $BUILD_DIR/test4.bundle -Wl,-rpath,@executable_path/test4/ $BUILD_DIR/test4/libtest4.dylib
 
-// BUILD:  mkdir -p $BUILD_DIR/test5
 // BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/test5/libtest5.dylib -install_name @executable_path/test5/libtest5.dylib
 // BUILD:  $CC foo.c -bundle -o $BUILD_DIR/test5.bundle $BUILD_DIR/test5/libtest5.dylib
 
-
-
 // BUILD:  $CC main.c -o $BUILD_DIR/dlopen-restricted.exe -DRUN_DIR="$RUN_DIR" -sectcreate __RESTRICT __restrict /dev/null
 
 // RUN:  ./dlopen-restricted.exe
 #include <stdio.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] dlopen-restricted\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
 
     // test1: LC_RPATH not in main executable uses @loader_path
     void* handle = dlopen(RUN_DIR "/test1.bundle", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL]  dlopen-restricted test1.bundle\n");
-        return 0;
+        FAIL("test1.bundle dlerror(): %s", dlerror());
     }
 
     // test2: @loader_path not in main executable
     handle = dlopen(RUN_DIR "/test2.bundle", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL]  dlopen-restricted test2.bundle\n");
-        return 0;
+        FAIL("test2.bundle\n dlerror(): %s", dlerror());
     }
 
     // test3: LC_RPATH not in main executable uses absolute path
     handle = dlopen(RUN_DIR "/test3.bundle", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL]  dlopen-restricted test3.bundle\n");
-        return 0;
+        FAIL("test3.bundle dlerror(): %s", dlerror());
     }
 
     // test4: [SHOULD FAIL] LC_RPATH not in main executable uses @executable_path
     handle = dlopen(RUN_DIR "/test4.bundle", RTLD_LAZY);
     if ( handle != NULL ) {
-        printf("[FAIL]  dlopen-restricted test4.bundle dlopen() should not work\n");
-        return 0;
+        FAIL("test4.bundle dlopen() should not work");
     }
 
     // test5: [SHOULD FAIL] @executable_path in LC_LOAD_DYLIB
     handle = dlopen(RUN_DIR "/test5.bundle", RTLD_LAZY);
     if ( handle != NULL ) {
-        printf("[FAIL]  dlopen-restricted test5.bundle dlopen() should not work\n");
-        return 0;
+        FAIL("test5.bundle dlopen() should not work");
     }
 
-
-
-    printf("[PASS]  dlopen-restricted\n");
-       return 0;
+    PASS("Success");
 }
 
index 63108fdb4c409b1579a84616438424abee031483..2a11c5f375377713eabbd42d6e3608c9f9521d48 100644 (file)
@@ -1,5 +1,5 @@
 
-// BUILD:  cp bad.txt $BUILD_DIR/libnota.dylib
+// BUILD:  $CP bad.txt $BUILD_DIR/libnota.dylib
 // BUILD:  $CC main.c  -o $BUILD_DIR/dlopen-bad-file.exe -DRUN_DIR="$RUN_DIR"
 
 // RUN:  ./dlopen-bad-file.exe
@@ -8,40 +8,29 @@
 #include <dlfcn.h>
 #include <string.h>
 
+#include "test_support.h"
 
-
-int main()
-{
-    printf("[BEGIN] dlopen-bad-file\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // try to dlopen() a text file
-       void* handle = dlopen(RUN_DIR "/libnota.dylib", RTLD_FIRST);
-       if ( handle != NULL ) {
-        printf("[FAIL] dlopen-bad-file should have failed on non-mach-o file %s\n", RUN_DIR "/libnota.dylib");
-               return 0;
-       }
+    void* handle = dlopen(RUN_DIR "/libnota.dylib", RTLD_FIRST);
+    if ( handle != NULL ) {
+        FAIL("Should have failed on non-mach-o file %s", RUN_DIR "/libnota.dylib");
+    }
     const char* message = dlerror();
     if ( (strstr(message, "mach-o") == NULL) && (strstr(message, "too short") == NULL) ) {
-        printf("dlerror: %s\n", message);
-        printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'mach-o'\n");
-               return 0;
-       }
+        FAIL("dlerror() message '%s' did not contain 'mach-o'", message);
+    }
 
     // try to dlopen() a directory
-       handle = dlopen(RUN_DIR, RTLD_FIRST);
-       if ( handle != NULL ) {
-        printf("[FAIL] dlopen-bad-file should have failed on dir %s\n", RUN_DIR);
-               return 0;
-       }
+    handle = dlopen(RUN_DIR, RTLD_FIRST);
+    if ( handle != NULL ) {
+        FAIL("Should have failed on dir %s", RUN_DIR);
+    }
     message = dlerror();
     if ( strstr(message, "not a file") == NULL ) {
-        printf("dlerror: %s\n", message);
-        printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'not a file'\n");
-               return 0;
-       }
-
-    printf("[PASS] dlopen-bad-file\n");
+        FAIL("dlerror() message '%s' did not contain 'not a file", message);
+    }
 
-       return 0;
+    PASS("Success");
 }
 
index 26e3c5dfcd025ee49a5cd6ced8e0643eb48af4b8..60c67115e2b51d49cc58c61165e3a16c41413e57 100644 (file)
@@ -8,41 +8,31 @@
 #include <stdio.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
 static void tryImage(const char* path)
 {
-    printf("[BEGIN] dlopen-basic %s\n", path);
-       void* handle = dlopen(path, RTLD_LAZY);
-       if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-basic %s\n", path);
-               return;
-       }
-       
-       void* sym = dlsym(handle, "foo");
-       if ( sym == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-basic %s\n", path);
-               return;
-       }
-       
-       int result = dlclose(handle);
-       if ( result != 0 ) {
-        printf("dlclose() returned %d, dlerror()=%s\n", result, dlerror());
-        printf("[FAIL] dlopen-basic %s\n", path);
-               return;
-       }
-
-    printf("[PASS] dlopen-basic %s\n", path);
+    void* handle = dlopen(path, RTLD_LAZY);
+    if ( handle == NULL ) {
+        FAIL("dlopen(\"%s\"), dlerror()=%s", path, dlerror());
+    }
+
+    void* sym = dlsym(handle, "foo");
+    if ( sym == NULL ) {
+        FAIL("dlsym(\"foo\") for \"%s\" returned NULL, dlerror()=%s", path, dlerror());
+    }
+
+    int result = dlclose(handle);
+    if ( result != 0 ) {
+        FAIL("dlclose(\"%s\") returned %d, dlerror()=%s", path, result, dlerror());
+    }
 }
 
 
 
-int main()
-{
-       tryImage(RUN_DIR "/test.bundle");
-       tryImage(RUN_DIR "/test.dylib");
-  
-       return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    tryImage(RUN_DIR "/test.bundle");
+    tryImage(RUN_DIR "/test.dylib");
+    PASS("Success");
 }
 
index 9bf805116f64c4549a062c797fa37841424f22d2..430a2f77a9564fd25fdd5b95682c7a4a17d96131 100644 (file)
@@ -9,19 +9,16 @@
 #include <stdio.h>
 #include <dlfcn.h>
 
-// libfoo-static.dylib and libfoo-dynamic.dylib each have an empty (no disk size) DATA segment
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] dlopen-empty-data\n");
+// libfoo-static.dylib and libfoo-dynamic.dylib each have an empty (no disk size) DATA segment
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  dlopen-empty-data: libfoo-dynamic.dylib could not be loaded: %s\n", dlerror());
-        return 0;
+        FAIL("libfoo-dynamic.dylib could not be loaded: %s", dlerror());
     }
 
-    printf("[PASS]  dlopen-empty-data\n");
-    return 0;
+    PASS("Success");
 }
 
index ceee099ebc6f791f491f9b8666be0d744c615240..61eb1a34d698d069d7d95738c6c60a5c2c0f281e 100644 (file)
@@ -1,10 +1,13 @@
 
-// BUILD:  $CC c.c -dynamiclib -o $TEMP_DIR/libcextra.dylib -install_name $RUN_DIR/libc.dylib -DEXTRA_SYMBOL=1
+// BUILD:  $CC c.c -dynamiclib -o $BUILD_DIR/libcextra.dylib -install_name $RUN_DIR/libc.dylib -DEXTRA_SYMBOL=1
 // BUILD:  $CC c.c -dynamiclib -o $BUILD_DIR/libc.dylib -install_name $RUN_DIR/libc.dylib
-// BUILD:  $CC b.m -dynamiclib -o $BUILD_DIR/libb.dylib -install_name $RUN_DIR/libb.dylib $TEMP_DIR/libcextra.dylib -framework Foundation
+// BUILD:  $CC b.m -dynamiclib -o $BUILD_DIR/libb.dylib -install_name $RUN_DIR/libb.dylib $BUILD_DIR/libcextra.dylib -framework Foundation
 // BUILD:  $CC a.c -dynamiclib -o $BUILD_DIR/liba.dylib -install_name $RUN_DIR/liba.dylib $BUILD_DIR/libb.dylib
 // BUILD:  $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-fail-cleanly.exe
 
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libcextra.dylib
+
+
 // RUN:  ./dlopen-fail-cleanly.exe
 
 #include <stdio.h>
 #include <dlfcn.h>
 #include <mach-o/dyld.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] dlopen-fail-cleanly\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // dlopen dylib chain that should fail
     void* handle = dlopen(RUN_DIR "/liba.dylib", RTLD_NOW);
     if ( handle != NULL ) {
-        printf("[FAIL]  dlopen-fail-cleanly dlopen(liba.dylib) expected to fail but did not\n");
-        return 0;
+        FAIL("dlopen(liba.dylib) expected to fail but did not");
     }
 
     // iterate loaded images and make sure no residue from failed dlopen
@@ -29,14 +29,12 @@ int main()
     int count = _dyld_image_count();
     for (int i=0; i < count; ++i) {
         const char* path = _dyld_get_image_name(i);
-        //printf("path[%2d]=%s\n", i, path);
+        LOG("path[%2d]=%s", i, path);
         if ( strstr(path, RUN_DIR "/lib") != NULL ) {
-            printf("[FAIL]  dlopen-fail-cleanly: found unexpected loaded image: %s\n", path);
-            return 0;
+            FAIL("Found unexpected loaded image: %s", path);
         }
     }
 
-    printf("[PASS]  dlopen-fail-cleanly\n");
-       return 0;
+    PASS("Success");
 }
 
index 84f58352a6beb9c3b5531d893f8a40c642a4ee5a..67a00f5a538f62094c0b901304ec3bea29cef3b2 100644 (file)
@@ -8,85 +8,69 @@
 #include <stdio.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
+
 int gInitialisersCalled = 0;
 
-int main() {
-       printf("[BEGIN] dlopen-flat\n");
-       int result;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    int result;
 
-       // Foo exports foo()
-       void* fooHandle = 0;
-       {
-               fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
-               if (!fooHandle) {
-                       printf("dlopen failed with error: %s\n", dlerror());
-                       return 1;
-               }
-               if (gInitialisersCalled != 1) {
-               printf("gInitialisersCalled != 1\n");
-               printf("[FAIL] dlopen-flat\n");
-                       return 1;
-               }
-       }
-       // Now unload foo which should do something.
-       result = dlclose(fooHandle);
-       if (result != 0) {
-        printf("dlclose() returned %c\n", result);
-        printf("[FAIL] dlopen-flat\n");
-               return 1;
-       }
+    // Foo exports foo()
+    void* fooHandle = 0;
+    {
+        fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+        if (!fooHandle) {
+            FAIL("dlopen(\"" RUN_DIR "/libfoo.dylib\") failed with error: %s", dlerror());
+        }
+        if (gInitialisersCalled != 1) {
+            FAIL("gInitialisersCalled != 1");
+        }
+    }
+    // Now unload foo which should do something.
+    result = dlclose(fooHandle);
+    if (result != 0) {
+        FAIL("dlclose() returned %c", result);
+    }
 
-       // Open foo again which should do something.
-       {
-               fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
-               if (!fooHandle) {
-                       printf("dlopen failed with error: %s\n", dlerror());
-                       return 1;
-               }
-               if (gInitialisersCalled != 2) {
-               printf("gInitialisersCalled != 2\n");
-               printf("[FAIL] dlopen-flat\n");
-                       return 1;
-               }
-       }
+    // Open foo again which should do something.
+    {
+        fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+        if (!fooHandle) {
+            FAIL("dlopen failed with error: %s", dlerror());
+        }
+        if (gInitialisersCalled != 2) {
+            FAIL("gInitialisersCalled != 2");
+        }
+    }
 
-       // Bar is going to resolve foo()
-       void* barHandle = 0;
-       {
-               barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
-               if (!barHandle) {
-                       printf("dlopen failed with error: %s\n", dlerror());
-                       return 1;
-               }
-               if (gInitialisersCalled != 3) {
-               printf("gInitialisersCalled != 3\n");
-               printf("[FAIL] dlopen-flat\n");
-                       return 1;
-               }
-       }
-       // Now unload foo which shouldn't do anything.
-       result = dlclose(fooHandle);
-       if (result != 0) {
-        printf("dlclose() returned %c\n", result);
-        printf("[FAIL] dlopen-flat\n");
-               return 1;
-       }
+    // Bar is going to resolve foo()
+    void* barHandle = 0;
+    {
+        barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
+        if (!barHandle) {
+            FAIL("dlopen(\"" RUN_DIR "/libbar.dylib\" failed with error: %s", dlerror());
+        }
+        if (gInitialisersCalled != 3) {
+            FAIL("gInitialisersCalled != 3");
+        }
+    }
+    // Now unload foo which shouldn't do anything.
+    result = dlclose(fooHandle);
+    if (result != 0) {
+        FAIL("dlclose(\"" RUN_DIR "/libfoo.dylib\") returned %c", result);
+    }
 
-       // Open foo again which shouldn't do anything.
-       {
-               fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
-               if (!fooHandle) {
-                       printf("dlopen failed with error: %s\n", dlerror());
-                       return 1;
-               }
-               if (gInitialisersCalled != 3) {
-               printf("gInitialisersCalled != 3\n");
-               printf("[FAIL] dlopen-flat\n");
-                       return 1;
-               }
-       }
+    // Open foo again which shouldn't do anything.
+    {
+        fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+        if (!fooHandle) {
+            FAIL("dlopen(\"" RUN_DIR "/libfoo.dylib\" failed with error: %s", dlerror());
+        }
+        if (gInitialisersCalled != 3) {
+            FAIL("gInitialisersCalled != 3");
+        }
+    }
 
-    printf("[PASS] dlopen-flat\n");
-       return 0;
+    PASS("Success");
 }
 
index 02c467e8390f250a4301bcbbdee991f6f642fdb7..0f47a995c77c597068e57b29e6782d63fe028250 100644 (file)
@@ -6,30 +6,23 @@
 #include <stdio.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    // Verify dyld will fallback and look for framework in /System/Library/Frameworks/
+    void* handle = dlopen("/System/Library/BadPath/CoreFoundation.framework/CoreFoundation", RTLD_LAZY);
+    if ( handle == NULL ) {
+        FAIL("dlerror(): %s", dlerror());
+    }
 
-int main()
-{
-    printf("[BEGIN] dlopen-framework-fallback\n");
+    // validate handle works to find symbols
+    void* sym = dlsym(handle, "CFRetain");
+    if ( sym == NULL ) {
+        FAIL("dlerror(): %s", dlerror());
+    }
 
-    // Verify dyld will fallback and look for framework in /System/Library/Frameworks/
-       void* handle = dlopen("/System/Library/BadPath/CoreFoundation.framework/CoreFoundation", RTLD_LAZY);
-       if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-framework-fallback\n");
-               return 0;
-       }
-
-       // validate handle works to find symbols
-       void* sym = dlsym(handle, "CFRetain");
-       if ( sym == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-framework-fallback\n");
-               return 0;
-       }
-
-    printf("[PASS] dlopen-framework-fallback\n");
-
-       return 0;
+    PASS("Success");
+
+    return 0;
 }
 
index a76b9c51d9e0dd232d5678534ec5e9231e3cb728..abb9139c193726720c6859f0528440a8ef1e62c9 100644 (file)
@@ -13,6 +13,8 @@
 #include <mach/mach.h>
 #include <mach/mach_host.h>
 
+#include "test_support.h"
+
 typedef bool (*BoolFunc)(void);
 
 
@@ -30,22 +32,15 @@ bool isHaswell_dynamic()
 }
 
 
-int main(int arg, const char* argv[])
-{
-    printf("[BEGIN] dlopen-haswell\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     void* handle = dlopen(RUN_DIR "/libHaswellCheck.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-haswell dlopen\n");
-        return 0;
+        FAIL("dlopen(\"" RUN_DIR "/libHaswellCheck.dylib\") error: %s", dlerror());
     }
 
     BoolFunc libFunc = (BoolFunc)dlsym(handle, "isHaswell");
     if ( libFunc == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-haswell dlsym\n");
-        return 0;
+        FAIL("dlsym(\"isHaswell\") error: %s", dlerror());
     }
 
     // check if haswell slice of libHaswellCheck.dylib was loaded on haswell machines
@@ -53,11 +48,9 @@ int main(int arg, const char* argv[])
     bool runtimeIsHaswell = isHaswell_dynamic();
 
        if ( dylibIsHaswellSlice != runtimeIsHaswell )
-        printf("[FAIL] dlopen-haswell, dylibIsHaswellSlice=%d, runtimeIsHaswell=%d\n", dylibIsHaswellSlice, runtimeIsHaswell);
+        FAIL("dlopen-haswell, dylibIsHaswellSlice=%d, runtimeIsHaswell=%d", dylibIsHaswellSlice, runtimeIsHaswell);
        else
-        printf("[PASS] dlopen-haswell\n");
-
-       return 0;
+        PASS("Success");
 }
 
 
index 5f91679587c7dd039ce1d1c770e3caf59f15e1c9..6b89368b04a4aee9c5a1ae0435ea7a44a629c6f7 100644 (file)
@@ -1,16 +1,7 @@
-
-
-#include <stdio.h>
 #include <dlfcn.h>
-#include <stdlib.h>
-#include <dispatch/dispatch.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <pthread.h>
-#include <assert.h>
-#include <unistd.h>
 
+#include "test_support.h"
 
 static void* work1(void* arg)
 {
@@ -21,13 +12,11 @@ static void* work1(void* arg)
 
 
 __attribute__((constructor))
-void myinit()
-{
+void myinit(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     pthread_t workerThread;
 
     if ( pthread_create(&workerThread, NULL, work1, NULL) != 0 ) {
-        printf("[FAIL]  dlopen-in-init, pthread_create\n");
-        return;
+        FAIL("pthread_create");
     }
 
     void* dummy;
index d96489e0ace5862f79f00953d405d60f001536d5..987925f26fbd3b73be0791fb1c3c28113f879764 100644 (file)
@@ -9,6 +9,7 @@
 #include <dlfcn.h>
 #include <stdlib.h>
 
+#include "test_support.h"
 
 __attribute__((constructor))
 void myinit()
@@ -17,13 +18,8 @@ void myinit()
 }
 
 
-int main()
-{
-    printf("[BEGIN] dlopen-in-init\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // The test is for libdyld.dylib to not crash when libfoo.dylib dlopen() stuff in its initializer
-
-    printf("[PASS]  dlopen-in-init\n");
-       return 0;
+    PASS("Success");
 }
 
index 336e471af475cac6c1f9ed4677e26bf984196d85..6f930dce38f63fd05c307446c909a56a4a002a14 100644 (file)
@@ -1,3 +1,4 @@
+#include "test_support.h"
 
 static int inited = 0;
 
@@ -11,6 +12,8 @@ int barIsInited() {
        return inited;
 }
 
-int bar() {
-       return inited ? 0 : 1;
-}
\ No newline at end of file
+void bar() {
+    if (inited == 0) {
+        FAIL("libbar.dylib not initialized");
+    }
+}
index bdd1e736bf5560195ced4590d4ba2f586672e0d1..199fb4f4c94214700bed02a2bc2c8ac68b0fe802 100644 (file)
@@ -3,7 +3,8 @@
 #include <stdio.h>
 #include <unistd.h>
 
-extern int bar();
+#include "test_support.h"
+
 extern int bazInited();
 
 static void* barHandle = NULL;
@@ -12,40 +13,33 @@ static int fooInited = 0;
 static int barInited = 0;
 
 __attribute__((constructor))
-static void myinit()
-{
+static void myinit(int argc, const char* argv[], const char* envp[], const char* apple[]) {
        fooInited = 1;
        barHandle = dlopen(RUN_DIR "/libbar.dylib", 0);
        if ( barHandle == NULL ) {
-               printf("[FAIL]  dlopen-in-init2, dlopen libbar.dylib: %s\n", dlerror());
-               return;
+               FAIL("dlopen libbar.dylib: %s", dlerror());
        }
        barSymbol = dlsym(RTLD_DEFAULT, "barIsInited");
     if ( barSymbol == NULL ) {
-               printf("[FAIL]  dlopen-in-init2, dlsym libbar.dylib\n");
-        return;
+               FAIL("dlsym libbar.dylib");
     }
     barInited = ((int(*)())barSymbol)();
 }
 
-int foo() {
+void foo() {
        if ( fooInited == 0 ) {
-               printf("[FAIL]  dlopen-in-init2, didn't init foo\n");
-               return 1;
+               FAIL("Didn't init foo");
        }
        if ( barHandle == NULL ) {
-               return 1;
+        FAIL("barHandle not inited");
        }
        if ( barSymbol == NULL ) {
-               return 1;
+        FAIL("barSymbol not inited");
        }
        if ( barInited == 0 ) {
-               printf("[FAIL]  dlopen-in-init2, didn't init bar\n");
-               return 1;
+               FAIL("Didn't init bar");
        }
        if ( bazInited() == 0 ) {
-               printf("[FAIL]  dlopen-in-init2, didn't init baz\n");
-               return 1;
+               FAIL("Didn't init baz");
        }
-       return 0;
-}
\ No newline at end of file
+}
index 454802dc071e09ee5f9ad3140455035b28904fa3..a9d123122c8b2ac1c6c51eb04780b9903c1c39af 100644 (file)
 #include <dlfcn.h>
 #include <stdlib.h>
 
+#include "test_support.h"
 
-extern int foo();
-extern int bar();
-
-int main() {
-       printf("[BEGIN] dlopen-in-init2\n");
-       if ( foo() != 0 )
-               return 0;
-       if ( bar() != 0 )
-               return 0;
-       printf("[PASS]  dlopen-in-init2\n");
-       return 0;
+extern void foo();
+extern void bar();
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    foo();
+    bar();
+    PASS("Success");
 }
 
index 8629f2cdef866d73d7f408f702ad5e94f6bcd9f9..4f15068d56d9a9c1141e061ee3efe526def6e6b4 100644 (file)
@@ -3,42 +3,39 @@
 #include <stdio.h>
 #include <unistd.h>
 
+#include "test_support.h"
+
 static void* bazHandle = NULL;
 static void* bazSymbol = NULL;
 static int barInited = 0;
 static int bazInited = 0;
 
 __attribute__((constructor))
-static void myinit()
-{
-       barInited = 1;
-       bazHandle = dlopen(RUN_DIR "/libbaz.dylib", 0);
-       if ( bazHandle == NULL ) {
-               printf("[FAIL]  dlopen-in-init3, dlopen libbaz.dylib: %s\n", dlerror());
-               return;
-       }
-       bazSymbol = dlsym(RTLD_DEFAULT, "bazIsInited");
+static void myinit(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    barInited = 1;
+    bazHandle = dlopen(RUN_DIR "/libbaz.dylib", 0);
+    if ( bazHandle == NULL ) {
+        FAIL("dlopen libbaz.dylib: %s", dlerror());
+    }
+    bazSymbol = dlsym(RTLD_DEFAULT, "bazIsInited");
     if ( bazSymbol == NULL ) {
-               printf("[FAIL]  dlopen-in-init3, dlsym libbaz.dylib\n");
-        return;
+        FAIL("dlsym libbaz.dylib");
     }
     bazInited = ((int(*)())bazSymbol)();
 }
 
 int bar() {
        if ( barInited == 0 ) {
-               printf("[FAIL]  dlopen-in-init3, didn't init bar\n");
-               return 1;
+               FAIL("Didn't init bar");
        }
        if ( bazHandle == NULL ) {
-               return 1;
+        FAIL("bazHandle not inited");
        }
        if ( bazSymbol == NULL ) {
-               return 1;
+        FAIL("bazSymbol not inited");
        }
        if ( bazInited == 0 ) {
-               printf("[FAIL]  dlopen-in-init3, didn't init bar\n");
-               return 1;
+               FAIL("didn't init bar");
        }
        return 0;
-}
\ No newline at end of file
+}
index 86850a87f2d56a1aaf05ffaf94c5606293a57757..3fc5a5af5abf8e18c5b6035e39847b81d0e45613 100644 (file)
@@ -1,5 +1,4 @@
-
-#include <stdio.h>
+#include "test_support.h"
 
 extern int bar();
 extern int bazIsInited();
@@ -9,8 +8,7 @@ int foo() {
                return 1;
        }
        if ( bazIsInited() == 0 ) {
-               printf("[FAIL]  dlopen-in-init3, didn't init baz\n");
-               return 1;
+               FAIL("didn't init baz");
        }
        return 0;
-}
\ No newline at end of file
+}
index 4f6b3629f55e5ad192b803a80e417bd8d9621b55..17ac7401675e8a50d240b3ee8f3bf599d704c1d5 100644 (file)
@@ -8,7 +8,7 @@
 // RUN:  ./dlopen-in-init3.exe
 
 // This test uses dlopen to jump ahead in the initializer graph
-// main doesn't directly link any of the libraries here, but dlopen's libfoo which links libbar and libbar.
+// main doesn't directly link any of the libraries here, but dlopen's libfoo which links libbar and libbaz.
 // We should run initializers in the order libbar, libbaz, libfoo.
 // However, libbar has a static init with a dlopen of libbaz and so libbaz needs to be initialized by libbar instead of by libfoo
 
 #include <dlfcn.h>
 #include <stdlib.h>
 
-int main() {
-       printf("[BEGIN] dlopen-in-init3\n");
-       void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", 0);
-       if ( fooHandle == NULL ) {
-               printf("[FAIL]  dlopen-in-init3, dlopen libfoo.dylib: %s\n", dlerror());
-               return 0;
-       }
-       void* fooSymbol = dlsym(RTLD_DEFAULT, "foo");
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", 0);
+    if ( fooHandle == NULL ) {
+        FAIL("dlopen-in-init3, dlopen libfoo.dylib: %s", dlerror());
+    }
+    void* fooSymbol = dlsym(RTLD_DEFAULT, "foo");
     if ( fooSymbol == NULL ) {
-               printf("[FAIL]  dlopen-in-init3, dlsym libfoo.dylib\n");
-        return 0;
+        FAIL("dlsym libfoo.dylib");
+    }
+    if ( ((int(*)())fooSymbol)() != 0 ) {
+        FAIL("fooSymbol() should return 0");
     }
-       if ( ((int(*)())fooSymbol)() != 0 )
-               return 0;
-       printf("[PASS]  dlopen-in-init3\n");
-       return 0;
+    PASS("Success");
 }
 
index 4547e90f805cf8b358032b2765d8180ac6233437..2e9069415c2e9a0dc4ceba4e9e8caa740e5a5bae 100644 (file)
 #include <dlfcn.h>
 #include <mach-o/dyld.h>
 
+#include "test_support.h"
 
 static void checkBundle(const char* path, bool unlinkBeforeDestroy)
 {
        int fd = open(path, O_RDONLY, 0);
        if ( fd == -1 ) {
-               printf("[FAIL] open(%s) failed", path);
-               exit(0);
+               FAIL("open(%s) failed", path);
        }
 
        struct stat stat_buf;
        if ( fstat(fd, &stat_buf) == -1) {
-               printf("[FAIL] fstat() failed\n");
-               exit(0);
+               FAIL("fstat() failed");
        }
 
        void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
        if ( loadAddress == ((void*)(-1)) ) {
-               printf("[FAIL] mmap() failed\n");
-               exit(0);
+               FAIL("mmap() failed");
        }
 
        close(fd);
 
        NSObjectFileImage ofi;
        if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) {
-               printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n");
-               exit(0);
+               FAIL("NSCreateObjectFileImageFromMemory failed");
        }
 
        NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
        if ( mod == NULL ) {
-               printf("[FAIL] NSLinkModule failed\n");
-               exit(0);
+               FAIL("NSLinkModule failed");
        }
        
    if ( !unlinkBeforeDestroy ) {
         // API lets you destroy ofi and NSModule lives on
         if ( !NSDestroyObjectFileImage(ofi) ) {
-            printf("[FAIL] NSDestroyObjectFileImage failed\n");
-            exit(0);
+            FAIL("NSDestroyObjectFileImage failed");
         }
     }
 
        NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
        if ( sym == NULL ) {
-               printf("[FAIL] NSLookupSymbolInModule failed\n");
-               exit(0);
+               FAIL("NSLookupSymbolInModule failed");
        }
 
        void* func = NSAddressOfSymbol(sym);
        if ( func == NULL ) {
-               printf("[FAIL] NSAddressOfSymbol failed\n");
-               exit(0);
+               FAIL("NSAddressOfSymbol failed");
        }
 
     Dl_info info;
     if ( dladdr(func, &info) == 0 ) {
-        printf("[FAIL] dladdr(&p, xx) failed\n");
-        exit(0);
+        FAIL("dladdr(&p, xx) failed");
     }
-    //printf("_fooInBundle found in %s\n", info.dli_fname);
+    LOG("_fooInBundle found in %s", info.dli_fname);
 
     if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
-            printf("[FAIL] NSUnLinkModule failed\n");
-            exit(0);
+            FAIL("NSUnLinkModule failed");
     }
 
     if ( dladdr(func, &info) != 0 ) {
-        printf("[FAIL] dladdr(&p, xx) found but should not have\n");
-        exit(0);
+       FAIL("dladdr(&p, xx) found but should not have");
     }
 
     if ( unlinkBeforeDestroy ) {
         if ( !NSDestroyObjectFileImage(ofi) ) {
-            printf("[FAIL] NSDestroyObjectFileImage failed\n");
-            exit(0);
+            FAIL("NSDestroyObjectFileImage failed");
         }
     }
 }
@@ -104,31 +93,22 @@ static void tryImage(const char* path, const char* symbol)
 {
     void* handle = dlopen(path, RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-indirect-groupNum %s\n", path);
-        exit(0);
+        FAIL("dlopen(%s) error: %s", path, dlerror());
     }
     
     void* sym = dlsym(handle, symbol);
     if ( sym == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-indirect-groupNum %s\n", path);
-        exit(0);
+        FAIL("dlsym(%s) error: %s", symbol, dlerror());
     }
     
     int result = dlclose(handle);
     if ( result != 0 ) {
-        printf("dlclose() returned %c\n", result);
-        printf("[FAIL] dlopen-indirect-groupNum %s\n", path);
-        exit(0);
+        FAIL("dlclose(%s) returned %c", path, result);
     }
 }
 
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] dlopen-indirect-groupNum\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     checkBundle(argv[1], true);
     checkBundle(argv[1], false);
 
@@ -142,6 +122,5 @@ int main(int argc, const char* argv[])
     // And now open baz.dylib which depends on bar.dylib
     tryImage(argv[3], "bazInDylib");
 
-    printf("[PASS] dlopen-indirect-groupNum\n");
-    return 0;
-}
\ No newline at end of file
+    PASS("Success");
+}
index e3143fb4fc05bc9617640ef65aaa80e16b51f77a..3b37d083bcee11cdddbc12d6ba6137ab0185a8c3 100644 (file)
@@ -2,6 +2,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "test_support.h"
+
 static const char* expectedStrings[] = {
     "a() from main",
     "initC",
@@ -16,10 +18,9 @@ static const char** curState = expectedStrings;
 
 void setState(const char* from)
 {
-    printf("%s\n", from);
+//    LOG("%s", from);
     if ( strcmp(*curState, from) != 0 ) {
-        printf("[FAIL] dlopen-intertwined: expected %s\n", *curState);
-        exit(0);
+        FAIL("Expected %s", *curState);
     }
     ++curState;
 }
index 11580d31c34195689d4b73df5e68646eed56f149..93668e1036c18d2eb4510fcbf1defee677c76a49 100644 (file)
@@ -16,6 +16,8 @@
 #include <stdlib.h>
 #include <dispatch/dispatch.h>
 
+#include "test_support.h"
+
 // main deps on A
 // main dlopens B which deps on C
 // main dlopens D which deps on C
 extern void a(const char*);
 extern void setState(const char* from);
 
-int main()
-{
-    printf("[BEGIN] dlopen-intertwined\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     a("main");
     
     void* handle = dlopen(RUN_DIR "/libB.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
-        exit(0);
+        FAIL("Error: %s", dlerror());
     }
 
     handle = dlopen(RUN_DIR "/libD.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
-        exit(0);
+        FAIL("Error: %s", dlerror());
     }
 
     handle = dlopen(RUN_DIR "/libE.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
-        exit(0);
+        FAIL("Error: %s", dlerror());
     }
 
     handle = dlopen(RUN_DIR "/libF.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
-        exit(0);
+        FAIL("Error: %s", dlerror());
     }
 
     setState("DONE");
 
-    printf("[PASS] dlopen-intertwined\n");
-       return 0;
+    PASS("Success");
 }
 
index 8b0a16aff74c9f086a78d2510ac5c6ff86c638c7..d5173376be1d9704111d02c8db41f518ddaf955b 100644 (file)
@@ -8,23 +8,18 @@
 #include <string.h>
 #include <stdlib.h>
 
+#include "test_support.h"
 
-
-int main()
-{
-    printf("[BEGIN] dlopen-long-error-message\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     for (int i=0; i < 10; ++i) {
         void* handle = dlopen("/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/libbogus.dylib", RTLD_FIRST);
         if ( handle != NULL ) {
-            printf("[FAIL] dlopen-long-error-message should have failed on non-existent file\n");
+            FAIL("Should have failed on non-existent file");
             return 0;
         }
         free(strdup("hello there"));
     }
 
-    printf("[PASS] dlopen-long-error-message\n");
-
-       return 0;
+    PASS("Success");
 }
 
index 483725f83a47831f05a3a60a43c29e89c26be790..bca7d5956f4191e6167d179ce715d0589ceb9c58 100644 (file)
 #include <stdio.h>
 #include <dlfcn.h>
 
-int main()
-{
-       printf("[BEGIN] dlopen-prebuilt-dlopen-closure\n");
-
-       void* handle = dlopen("/usr/lib/libobjc-trampolines.dylib", RTLD_LAZY);
-       if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-prebuilt-dlopen-closure\n");
-               return 0;
-       }
-
-    printf("[PASS] dlopen-prebuilt-dlopen-closure\n");
-  
-       return 0;
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    void* handle = dlopen("/usr/lib/libobjc-trampolines.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        FAIL("dlerror(): %s", dlerror());
+    }
+
+    PASS("Success");
 }
 
index c8e9924b4532e6951cd233cbc7f38100105f883a..13457f29a16baf53cc214cc6b7d8a81c0529de42 100644 (file)
@@ -1,5 +1,5 @@
 int foo()
 {
-       return 10;
+    return 10;
 }
 
index bad9ff72210c7fb55fc2b3a460f82dd461733a1f..1b1c3cb64f2f7861bfb1c146230ff8ad8e86b873 100644 (file)
@@ -9,25 +9,21 @@
 #include <stdlib.h>
 #include <dispatch/dispatch.h>
 
+#include "test_support.h"
 
-
-int main()
-{
-    printf("[BEGIN] dlopen-read\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     __block bool allGood = true;
     dispatch_apply(6, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
         for (int i=0; i < 500; ++i) {
             void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
             if ( handle == NULL ) {
-                printf("[FAIL] dlopen-read: %s\n", dlerror());
-                exit(0);
+                FAIL("dlopen-read: %s", dlerror());
             }
             dlclose(handle);
         }
     });
 
-    printf("[PASS] dlopen-read\n");
+    PASS("Success");
        return 0;
 }
 
index c622e9f4257a5660fdf40b22a33c3aba71112883..30793d4899b5234e9c91032627f09867217c47d2 100644 (file)
@@ -1,39 +1,34 @@
 
 // BUILD:  $CC main.c -o $BUILD_DIR/dlopen-realpath.exe
-// BUILD:  cd $BUILD_DIR && ln -s ./IOKit.framework/IOKit IOKit && ln -s /System/Library/Frameworks/IOKit.framework IOKit.framework
+// BUILD:  $SYMLINK ./IOKit.framework/IOKit  $BUILD_DIR/IOKit
+// BUILD:  $SYMLINK /System/Library/Frameworks/IOKit.framework  $BUILD_DIR/IOKit.framework
 
-// RUN:  DYLD_FALLBACK_LIBRARY_PATH=/baz  ./dlopen-realpath.exe
+//FIXME: Use something besides IOKit so we do not need to copy it into the chroot
+// R:  DYLD_FALLBACK_LIBRARY_PATH=/baz  ./dlopen-realpath.exe
 
 #include <stdio.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
 static void tryImage(const char* path)
 {
-    printf("[BEGIN] dlopen-realpath %s\n", path);
-       void* handle = dlopen(path, RTLD_LAZY);
-       if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL] dlopen-realpath %s\n", path);
-               return;
-       }
-
-       int result = dlclose(handle);
-       if ( result != 0 ) {
-        printf("dlclose(%p): %s\n", handle, dlerror());
-        printf("[FAIL] dlopen-realpath %s\n", path);
-               return;
-       }
-
-    printf("[PASS] dlopen-realpath %s\n", path);
+    void* handle = dlopen(path, RTLD_LAZY);
+    if ( handle == NULL ) {
+        FAIL("dlerror(\"%s\"): %s", path, dlerror());
+    }
+
+    int result = dlclose(handle);
+    if ( result != 0 ) {
+        FAIL("dlclose(\"%s\"): %s", path, dlerror());
+    }
 }
 
 
 
-int main()
-{
-       tryImage("./IOKit.framework/IOKit");
-       tryImage("./././IOKit/../IOKit.framework/IOKit");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    tryImage("./IOKit.framework/IOKit");
+    tryImage("./././IOKit/../IOKit.framework/IOKit");
     tryImage("./IOKit");
 
     // Also try libSystem which has an alias in the OS to /usr/lib/libSystem.B.dylib
@@ -44,6 +39,6 @@ int main()
     tryImage("//usr/lib/libSystem.dylib");
     tryImage("/usr/./lib/libSystem.dylib");
 
-       return 0;
+    PASS("Success");
 }
 
index c86856ec33ba760ebc97b503ce3f9074396789cf..077c499d47428ffe63e2aa35555997e51c506442 100644 (file)
@@ -1,5 +1,5 @@
 int bar()
 {
-       return 0;
+    return 0;
 }
 
index 53c244eff56eb791f7b9bd682eb1df3aeca658ce..a48fc72cb4a03c829ffe01d2eeb16d80a56a0e0d 100644 (file)
 #include <stdlib.h>
 #include <dispatch/dispatch.h>
 
+#include "test_support.h"
 
-
-int main()
-{
-    printf("[BEGIN] dlopen-recurse\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // libfoo's initializer calls dlopen().  If that hangs, we have a locking bug
     void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
     dlclose(handle);
 
-    printf("[PASS] dlopen-recurse\n");
-       return 0;
+    PASS("Success");
 }
 
index c86856ec33ba760ebc97b503ce3f9074396789cf..077c499d47428ffe63e2aa35555997e51c506442 100644 (file)
@@ -1,5 +1,5 @@
 int bar()
 {
-       return 0;
+    return 0;
 }
 
index ab6936ebcb5293fd77ed321870d0ab6c9dc1a6b9..0b8be87e32952a008d7a3a6fc53bf792c80ced23 100644 (file)
@@ -1,5 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/dir
 // BUILD:  $CC bar.c  -dynamiclib -install_name @rpath/libbar.dylib -o $BUILD_DIR/dir/libbar.dylib
 // BUILD:  $CC test.c -dynamiclib -install_name $RUN_DIR/libtest.dylib -o $BUILD_DIR/libtest.dylib -rpath @loader_path/dir
 // BUILD:  $CC main.c -o $BUILD_DIR/dlopen-rpath-from-dylib.exe $BUILD_DIR/libtest.dylib 
 #include <dlfcn.h>
 #include <stdbool.h>
 
+#include "test_support.h"
 
 /// test that a call to dlopen() from a dylib uses the LC_RPATH from that dylib
 
-extern bool test_dlopen();
+extern void test_dlopen();
 
-int main()
-{
-    printf("[BEGIN] dlopen-rpath-from-dylib\n");
-
-    if ( test_dlopen() )
-        printf("[PASS]  dlopen-rpath-from-dylib\n");
-    else
-        printf("[FAIL]  dlopen-rpath-from-dylib\n");
-
-       return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    test_dlopen();
+    PASS("Succcess");
 }
 
index dba4287b91fe54b686c593f5348b410134beee9f..c655aa335c09275cb63723b6cbf23e4cdf16a321 100644 (file)
@@ -3,17 +3,14 @@
 #include <dlfcn.h>
 #include <stdbool.h>
 
-bool test_dlopen()
+#include "test_support.h"
+
+void test_dlopen()
 {
     void* handle = dlopen("@rpath/libbar.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen-rpath-from-dylib: dlopen(@rpath/libbar.dylib) failed: %s\n", dlerror());
-        return false;
+        FAIL("dlopen(\"@rpath/libbar.dylib\") failed: %s", dlerror());
     }
-
     dlclose(handle);
-
-    return true;
 }
 
-
index c8e9924b4532e6951cd233cbc7f38100105f883a..13457f29a16baf53cc214cc6b7d8a81c0529de42 100644 (file)
@@ -1,5 +1,5 @@
 int foo()
 {
-       return 10;
+    return 10;
 }
 
index 6d43d7f14647086c341e85bf2d0bcdd7eddceb76..19bde69c1a36474f03785583a64ddf96247c6c1b 100644 (file)
@@ -1,5 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/dir1
 // BUILD:  $CC foo.c -dynamiclib -install_name @rpath/libimplicitrpath.dylib -o $BUILD_DIR/dir1/libimplicitrpath.dylib
 // BUILD:  $CC main.c -o $BUILD_DIR/dlopen-rpath-implicit.exe -rpath @loader_path/dir1
 
@@ -8,23 +7,17 @@
 #include <stdio.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
 /// test that a leaf name passed to dlopen() searches the rpath
 
-int main()
-{
-    printf("[BEGIN] dlopen-rpath-implicit\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     void* handle = dlopen("libimplicitrpath.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL] dlopen-rpath-implicit: dlopen(libimplicitrpath.dylib) failed: %s\n", dlerror());
-        return 0;
+        FAIL("dlopen(\"libimplicitrpath.dylib\") failed: %s", dlerror());
     }
 
     dlclose(handle);
-
-    printf("[PASS] dlopen-rpath-implicit\n");
-
-       return 0;
+    PASS("Succcess");
 }
 
index 08b8b6a8eb1c9661350af48aa9caecb6a975adbe..d9219bf41ebc7878bf11e7417ca0de230543a189 100644 (file)
@@ -1,10 +1,7 @@
-#include <stdio.h>
-#include <stdlib.h>
-
+#include "test_support.h"
 
 __attribute__((constructor))
-void init()
+void init(int argc, const char* argv[], const char* envp[], const char* apple[])
 {
-    printf("[FAIL]  dlopen-rpath-prev-override\n");
-    exit(0);
+    FAIL("Bad dylib loaded");
 }
index 852b89b5e82183ffbb8cc3eb711eb2b5a83ef476..127cfb9e4c94f6531e04c64a43dc247479d43cbd 100644 (file)
@@ -1,5 +1,5 @@
 int sub2()
 {
-       return 2;
+    return 2;
 }
 
index c8e9924b4532e6951cd233cbc7f38100105f883a..13457f29a16baf53cc214cc6b7d8a81c0529de42 100644 (file)
@@ -1,5 +1,5 @@
 int foo()
 {
-       return 10;
+    return 10;
 }
 
index 98c93f665af5943ef9adc6e9f6935637f5755e86..d1955d65100b94b8cba177fe905ce2c11bedf71e 100644 (file)
@@ -1,5 +1,5 @@
 int sub1()
 {
-       return 1;
+    return 1;
 }
 
index 8aa206f62f9bf38eb638cbc164632a09e14f5340..30b5dbbaa57b7356adc8b2a6c79f84c3b0cc93d0 100644 (file)
@@ -1,5 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/dir $BUILD_DIR/good $BUILD_DIR/bad
 // BUILD:  $CC good.c -dynamiclib -install_name @rpath/libtest.dylib     -o $BUILD_DIR/good/libtest.dylib
 // BUILD:  $CC bad.c  -dynamiclib -install_name @rpath/libtest.dylib     -o $BUILD_DIR/bad/libtest.dylib
 // BUILD:  $CC dyn.c  -dynamiclib -install_name @rpath/libdynamic.dylib  -o $BUILD_DIR/dir/libdynamic.dylib $BUILD_DIR/good/libtest.dylib -rpath @loader_path/../bad
 #include <stdio.h>
 #include <dlfcn.h>
 
-int main()
-{
-    printf("[BEGIN] dlopen-rpath-prev-override\n");
+#include "test_support.h"
 
-       void* handle = dlopen(RUN_DIR "/dir/libdynamic.dylib", RTLD_LAZY);
-       if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL]  dlopen-rpath-prev-override\n");
-               return 0;
-       }
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    void* handle = dlopen(RUN_DIR "/dir/libdynamic.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        FAIL("dlerror(): %s", dlerror());
+    }
 
-    printf("[PASS]  dlopen-rpath-prev-override\n");
-       return 0;
+    PASS("Success");
 }
 
index c8e9924b4532e6951cd233cbc7f38100105f883a..13457f29a16baf53cc214cc6b7d8a81c0529de42 100644 (file)
@@ -1,5 +1,5 @@
 int foo()
 {
-       return 10;
+    return 10;
 }
 
index 67741d04883149575badd02b7ae382aabb1e6f2a..d1646925f10a4cf849369da359e8dc1396ce30f9 100644 (file)
@@ -1,5 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/dir1 $BUILD_DIR/dir2
 // BUILD:  $CC sub1.c -dynamiclib -install_name @rpath/librpathstatic.dylib -o $BUILD_DIR/dir1/librpathstatic.dylib
 // BUILD:  $CC sub2.c -dynamiclib -install_name @rpath/libdynamic.dylib     -o $BUILD_DIR/dir2/libdynamic.dylib $BUILD_DIR/dir1/librpathstatic.dylib
 // BUILD:  $CC foo.c  -dynamiclib -install_name $RUN_DIR/libstatic.dylib    -o $BUILD_DIR/libstatic.dylib -rpath @loader_path/dir1 $BUILD_DIR/dir1/librpathstatic.dylib
 #include <stdio.h>
 #include <dlfcn.h>
 
-int main()
-{
-    printf("[BEGIN] dlopen-rpath-prev\n");
+#include "test_support.h"
 
-       void* handle = dlopen(RUN_DIR "/dir2/libdynamic.dylib", RTLD_LAZY);
-       if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL]  dlopen-rpath-prev\n");
-               return 0;
-       }
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    void* handle = dlopen(RUN_DIR "/dir2/libdynamic.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        FAIL("dlerror(): %s", dlerror());
+    }
 
-    printf("[PASS]  dlopen-rpath-prev\n");
-       return 0;
+    PASS("Success");
 }
 
index 98c93f665af5943ef9adc6e9f6935637f5755e86..d1955d65100b94b8cba177fe905ce2c11bedf71e 100644 (file)
@@ -1,5 +1,5 @@
 int sub1()
 {
-       return 1;
+    return 1;
 }
 
index 852b89b5e82183ffbb8cc3eb711eb2b5a83ef476..127cfb9e4c94f6531e04c64a43dc247479d43cbd 100644 (file)
@@ -1,5 +1,5 @@
 int sub2()
 {
-       return 2;
+    return 2;
 }
 
index eaaa69bcb75e3b4337971326c180e552780d7f68..6a41bcd5e8d986fda300c1228e42d435d786a1a2 100644 (file)
 #include <stdio.h>
 #include <dlfcn.h>
 
-int main() {
-  printf("[BEGIN] dlopen-signing\n");
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
   void* handle = dlopen("signed.dylib", RTLD_LAZY);
   if ( handle == NULL ) {
-     printf("dlerror(): %s\n", dlerror());
-     printf("[FAIL] dlopen-signing (signed loading signed)\n");
-     return 0;
+     FAIL("dlerror(): %s", dlerror());
   } else {
     int result = dlclose(handle);
     if ( result != 0 ) {
-       printf("dlclose() returned %c\n", result);
-       printf("[FAIL] dlopen-signing (signed unloading signed)\n");
-       return 0;
+       FAIL("dlclose() returned %c", result);
     }
   }
 
   handle = dlopen("unsigned.dylib", RTLD_LAZY);
   if ( handle != NULL ) {
-     printf("dlerror(): %s\n", dlerror());
-     printf("[FAIL] dlopen-signing (signed loading unsigned)\n");
-     return 0;
+     FAIL("dlerror(): %s", dlerror());
   } else {
     int result = dlclose(handle);
     if ( result != 0 ) {
-       printf("dlclose() returned %c\n", result);
-       printf("[FAIL] dlopen-signing (signed unloading signed)\n");
-       return 0;
+       FAIL("dlclose() returned %c", result);
     }
   }
 
-  printf("[PASS] dlopen-signing\n");
-
-  return 0;
+  PASS("Success");
 }
 
 
index ce197a9fc292df66762501421d68cb0bf30b520b..50ba18d100ea0e290e75055338e615e8e8239a20 100644 (file)
@@ -1,6 +1,6 @@
 
 // BUILD:  $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
-// BUILD:  cd $BUILD_DIR && ln -s libfoo.dylib libfoo-symlink.dylib
+// BUILD:  $SYMLINK libfoo.dylib $BUILD_DIR/libfoo-symlink.dylib
 // BUILD:  $CC main.c -o $BUILD_DIR/dlopen-symlink.exe -DRUN_DIR="$RUN_DIR"
 
 // RUN:  ./dlopen-symlink.exe
 #include <mach-o/dyld.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] dlopen-symlink\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // call dlopen() with a path that is a symlink
     void* handle = dlopen(RUN_DIR "/libfoo-symlink.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL]  dlopen-symlink\n");
-        return 0;
+        FAIL("dlerror(): %s", dlerror());
     }
 
     // walk images to see if path was converted to real path
@@ -30,27 +26,23 @@ int main()
     int count = _dyld_image_count();
     for (int i=0; i < count; ++i) {
         const char* path = _dyld_get_image_name(i);
-        //printf("path[%2d]=%s\n", i, path);
+        LOG("path[%2d]=%s", i, path);
         if ( strstr(path, "libfoo") != NULL ) {
             if ( foundPath == NULL ) {
                 foundPath = path;
             }
             else {
-                printf("[FAIL]  dlopen-symlink: more than one libfoo found\n");
-                return 0;
+                FAIL("More than one libfoo found");
             }
         }
     }
     if ( foundPath == NULL ) {
-        printf("[FAIL]  dlopen-symlink: no libfoo found\n");
-        return 0;
+        FAIL("No libfoo found");
     }
     if ( strstr(foundPath, "libfoo-symlink") != NULL ) {
-        printf("[FAIL]  dlopen-symlink: path is symlink not real path: %s\n", foundPath);
-        return 0;
+        FAIL("Path is symlink not real path: %s", foundPath);
     }
 
-    printf("[PASS]  dlopen-symlink\n");
-       return 0;
+    PASS("Success");
 }
 
index 445c15ed72961e963fbf22227965f8398c76270a..a6c750d6dea3de734ffe36eeac0481712bfb6f3e 100644 (file)
@@ -10,6 +10,7 @@
 #include <string.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
 // verify RTLD_DEFAULT search order
 
@@ -33,54 +34,42 @@ static bool symbolInImage(const char* symName, const char* image)
 
 
 
-
-int main()
-{
-    printf("[BEGIN] dlsym-RTLD_DEFAULT\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // verify mainSymbol is found in main executable
     if ( !symbolInImage("mainSymbol", "dlsym-RTLD_DEFAULT") ) {
-        printf("[FAIL]  dlsym-RTLD_DEFAULT: mainSymbol\n");
-        return 0;
+        FAIL("mainSymbol");
     }
 
     // verify free is found in main executable, overrideing one in OS
     if ( !symbolInImage("free", "dlsym-RTLD_DEFAULT") ) {
-        printf("[FAIL]  dlsym-RTLD_DEFAULT: free\n");
-        return 0;
+        FAIL("free");
     }
 
     // verify foo is found in libfoo-static.dylib
     if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
-        printf("[FAIL]  dlsym-RTLD_DEFAULT: foo not in libfoo-static.dylib\n");
-        return 0;
+        FAIL("foo not in libfoo-static.dylib");
     }
 
     void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  dlsym-RTLD_DEFAULT: libfoo-dynamic.dylib could not be loaded\n");
-        return 0;
+        FAIL("libfoo-dynamic.dylib could not be loaded");
     }
 
     // verify foo is still found in statically linked lib
     if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
-        printf("[FAIL]  dlsym-RTLD_DEFAULT: foo not in libfoo-static.dylib\n");
-        return 0;
+        FAIL("foo not in libfoo-static.dylib");
     }
 
     // verify foo2 is found in libfoo-dynamic.dylib"
     if ( !symbolInImage("foo2", "libfoo-dynamic.dylib") ) {
-        printf("[FAIL]  dlsym-RTLD_DEFAULT: foo2 not in libfoo-dynamic.dylib\n");
-        return 0;
+        FAIL("foo2 not in libfoo-dynamic.dylib");
     }
 
     // renamed and re-exported symbols work
     if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) {
-        printf("[FAIL]  dlsym-RTLD_DEFAULT: strcmp not found\n");
-        return 0;
+        FAIL("strcmp not found");
     }
     
-    printf("[PASS]  dlsym-RTLD_DEFAULT\n");
-       return 0;
+    PASS("Success");
 }
 
index 2300ad100364aff1cabda70add9a30254ad18503..d0fd9e932bdbee5733b86af34735f78786180bc0 100644 (file)
@@ -10,6 +10,7 @@
 #include <string.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
 // verify RTLD_MAIN_ONLY search order
 
@@ -34,47 +35,37 @@ static bool symbolInImage(const char* symName, const char* image)
 
 
 
-int main()
-{
-    printf("[BEGIN] dlsym-RTLD_MAIN_ONLY\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // verify mainSymbol is found
     if ( !symbolInImage("mainSymbol", "dlsym-RTLD_MAIN_ONLY") ) {
-        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: mainSymbol should have been found\n");
-        return 0;
+        FAIL("mainSymbol should have been found");
     }
 
     // verify free is found in this program - not in OS
     if ( !symbolInImage("free", "dlsym-RTLD_MAIN_ONLY") ) {
-        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: free\n");
-        return 0;
+        FAIL("free");
     }
 
     // verify foo is not found
     if ( dlsym(RTLD_MAIN_ONLY, "foo") != NULL ) {
-        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: foo should not have been found\n");
-        return 0;
+        FAIL("foo should not have been found");
     }
 
     void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: libfoo-dynamic.dylib could not be loaded\n");
-        return 0;
+        FAIL("libfoo-dynamic.dylib could not be loaded");
     }
 
     // verify foo is still not found
     if ( dlsym(RTLD_MAIN_ONLY, "foo") != NULL ) {
-        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: foo should not have been found after dlopen\n");
-        return 0;
+        FAIL("foo should not have been found after dlopen");
     }
 
     // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_MAIN_ONLY only searches main executable
     if ( dlsym(RTLD_MAIN_ONLY, "foo2") != NULL ) {
-        printf("[FAIL]  dlsym-RTLD_MAIN_ONLY: foo2 found but should not have been\n");
-        return 0;
+        FAIL("foo2 found but should not have been");
     }
 
-    printf("[PASS]  dlsym-RTLD_MAIN_ONLY\n");
-       return 0;
+    PASS("Success");
 }
 
index 4a3130ebc2632ed332de3f7d2e4ea14d61dc366f..09dfda2e5cb9bccc013e6e850f8318ee227a34ff 100644 (file)
@@ -10,6 +10,7 @@
 #include <string.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
 // verify RTLD_NEXT search order
 
@@ -34,47 +35,37 @@ static bool symbolInImage(const char* symName, const char* image)
 
 
 
-int main()
-{
-    printf("[BEGIN] dlsym-RTLD_NEXT\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // verify mainSymbol is not found
     if ( dlsym(RTLD_NEXT, "mainSymbol") != NULL ) {
-        printf("[FAIL]  dlsym-RTLD_NEXT: mainSymbol should not have been found\n");
-        return 0;
+        FAIL("mainSymbol should not have been found");
     }
 
     // verify free is found in OS (not local one)
     if ( !symbolInImage("free", "/usr/lib/") ) {
-        printf("[FAIL]  dlsym-RTLD_NEXT: free\n");
-        return 0;
+        FAIL("free");
     }
 
     // verify foo is found in libfoo-static.dylib
     if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
-        printf("[FAIL]  dlsym-RTLD_NEXT: foo not in libfoo-static.dylib\n");
-        return 0;
+        FAIL("foo not in libfoo-static.dylib");
     }
 
     void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  dlsym-RTLD_NEXT: libfoo-dynamic.dylib could not be loaded\n");
-        return 0;
+        FAIL("libfoo-dynamic.dylib could not be loaded");
     }
 
     // verify foo is still found in statically linked lib
     if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
-        printf("[FAIL]  dlsym-RTLD_NEXT: foo not in libfoo-static.dylib\n");
-        return 0;
+        FAIL("foo not in libfoo-static.dylib");
     }
 
     // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_NEXT only searches thing this image would have seen
     if ( symbolInImage("foo2", "libfoo-dynamic.dylib") ) {
-        printf("[FAIL]  dlsym-RTLD_NEXT: foo2 found but should not have been\n");
-        return 0;
+        FAIL("foo2 found but should not have been");
     }
 
-    printf("[PASS]  dlsym-RTLD_NEXT\n");
-       return 0;
+    PASS("Success");
 }
 
index 5af0e2fdf4b8281d77e5bbcfdcf334ad7e876782..7f83ffeb43be297a072151f99dd5d7e2abb1a6e4 100644 (file)
@@ -10,6 +10,7 @@
 #include <string.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
 // verify RTLD_SELF search order
 
@@ -34,47 +35,37 @@ static bool symbolInImage(const char* symName, const char* image)
 
 
 
-int main()
-{
-    printf("[BEGIN] dlsym-RTLD_SELF\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // verify mainSymbol is found
     if ( dlsym(RTLD_SELF, "mainSymbol") == NULL ) {
-        printf("[FAIL]  dlsym-RTLD_SELF: mainSymbol should have been found\n");
-        return 0;
+        FAIL("mainSymbol should have been found");
     }
 
     // verify free is found in this program - not in OS
     if ( !symbolInImage("free", "dlsym-RTLD_SELF") ) {
-        printf("[FAIL]  dlsym-RTLD_SELF: free\n");
-        return 0;
+        FAIL("free");
     }
 
     // verify foo is found in libfoo-static.dylib
     if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
-        printf("[FAIL]  dlsym-RTLD_SELF: foo not in libfoo-static.dylib\n");
-        return 0;
+        FAIL("foo not in libfoo-static.dylib");
     }
 
     void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  dlsym-RTLD_SELF: libfoo-dynamic.dylib could not be loaded\n");
-        return 0;
+        FAIL("libfoo-dynamic.dylib could not be loaded");
     }
 
     // verify foo is still found in statically linked lib
     if ( !symbolInImage("foo", "libfoo-static.dylib") ) {
-        printf("[FAIL]  dlsym-RTLD_SELF: foo not in libfoo-static.dylib\n");
-        return 0;
+        FAIL("foo not in libfoo-static.dylib");
     }
 
     // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_SELF only searches thing this image would have seen
     if ( symbolInImage("foo2", "libfoo-dynamic.dylib") ) {
-        printf("[FAIL]  dlsym-RTLD_SELF: foo2 found but should not have been\n");
-        return 0;
+        FAIL("foo2 found but should not have been");
     }
 
-    printf("[PASS]  dlsym-RTLD_SELF\n");
-       return 0;
+    PASS("Success");
 }
 
index 52e77507cff24de1b4280e0e0676b571f8b76d23..1bd549984b1c83d1b2d2bc1286dc41af8f5bc053 100644 (file)
 #include <dlfcn.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
 // verify RTLD_DEFAULT search order
 
 int mainSymbol = 4;
 
-int main()
-{
-    printf("[BEGIN] dlsym-handle\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
     if ( fooHandle == NULL ) {
-        printf("[FAIL]  dlsym-handle: libfoo.dylib could not be loaded, %s\n", dlerror());
-        return 0;
+        FAIL("libfoo.dylib could not be loaded, %s", dlerror());
     }
 
     void* barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
     if ( barHandle == NULL ) {
-        printf("[FAIL]  dlsym-handle: libbar.dylib could not be loaded, %s\n", dlerror());
-        return 0;
+        FAIL("libbar.dylib could not be loaded, %s", dlerror());
     }
 
     // verify fooHandle does not find mainSymbol
     if ( dlsym(fooHandle, "mainSymbol") != NULL ) {
-        printf("[FAIL]  dlsym-handle: mainSymbol was found with fooHandle\n");
-        return 0;
+        FAIL("mainSymbol was found with fooHandle");
     }
 
     // verify fooHandle can find foo
     if ( dlsym(fooHandle, "foo") == NULL ) {
-        printf("[FAIL]  dlsym-handle: foo not found with fooHandle\n");
-        return 0;
+        FAIL("foo not found with fooHandle");
     }
 
     // verify fooHandle can find base
     if ( dlsym(fooHandle, "base") == NULL ) {
-        printf("[FAIL]  dlsym-handle: base not found with fooHandle\n");
-        return 0;
+        FAIL("base not found with fooHandle");
     }
 
     // verify fooHandle cannot find bar
     if ( dlsym(fooHandle, "bar") != NULL ) {
-        printf("[FAIL]  dlsym-handle: bar found with fooHandle\n");
-        return 0;
+        FAIL("bar found with fooHandle");
     }
 
     // verify barHandle can find bar
     if ( dlsym(barHandle, "bar") == NULL ) {
-        printf("[FAIL]  dlsym-handle: bar not found with barHandle\n");
-        return 0;
+        FAIL("bar not found with barHandle");
     }
 
     // verify barHandle can find base
     if ( dlsym(barHandle, "base") == NULL ) {
-        printf("[FAIL]  dlsym-handle: base not found with barHandle\n");
-        return 0;
+        FAIL("base not found with barHandle");
     }
 
     // verify barHandle cannot find foo
     if ( dlsym(barHandle, "foo") != NULL ) {
-        printf("[FAIL]  dlsym-handle: foo found with barHandle\n");
-        return 0;
+        FAIL("foo found with barHandle");
     }
 
     // verify renamed and re-exported symbols work
     if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) {
-        printf("[FAIL]  dlsym-handle: strcmp not found\n");
-        return 0;
+        FAIL("strcmp not found");
     }
     
     // verify bad handle errors
     if ( dlsym((void*)0xdeadbeef, "malloc") != NULL ) {
-        printf("[FAIL]  dlsym-handle: malloc found with bad handle\n");
-        return 0;
+        FAIL("malloc found with bad handle");
     }
     else {
         const char* message = dlerror();
         if ( strstr(message, "invalid") == NULL ) {
-            printf("[FAIL]  dlsym-handle: invalid handle error message missing 'invalid'\n");
-            return 0;
+            FAIL(" invalid handle error message missing 'invalid'");
         }
     }
 
-    printf("[PASS]  dlsym-handle\n");
-       return 0;
+    PASS("Success");
 }
 
index 802f30e519319db4a6666aafcbcc6d3284ceaece..ef66f2d4febec895636db34cbec028e9eab24e2a 100644 (file)
@@ -5,6 +5,8 @@
 #include <string.h>
 #include <mach-o/dyld-interposing.h>
 
+#include "test_support.h"
+
 static bool inMalloc = false;
 static bool forceSystemMalloc = false;
 
@@ -18,8 +20,7 @@ void* mymalloc(size_t size)
     if (inMalloc) {
         // Recursion!   This shouldn't happen.
         forceSystemMalloc = true;
-        printf("[FAIL] dlsym-in-interposed-malloc mymalloc() is recursive\n");
-        exit(1);
+        FAIL("mymalloc() is recursive");
     }
 
     inMalloc = true;
@@ -28,14 +29,12 @@ void* mymalloc(size_t size)
     void* sym = dlsym(RTLD_DEFAULT, "malloc");
     if (sym == NULL) {
         forceSystemMalloc = true;
-        printf("[FAIL] dlsym-in-interposed-malloc dlsym failed\n");
-        exit(1);
+        FAIL("dlsym failed");
     }
 
     if (sym != mymalloc) {
         forceSystemMalloc = true;
-        printf("[FAIL] dlsym-in-interposed-malloc dlsym result %p != mymalloc %p\n", sym, &mymalloc);
-        exit(1);
+        FAIL("dlsym result %p != mymalloc %p", sym, &mymalloc);
     }
     void* result = malloc(size);
 
index 70adcf0bb272243c936a1d49935e57f99a050113..a7faa59ca37aeb3880af8ab54ccda40f60fe7fd6 100644 (file)
@@ -9,16 +9,12 @@
 #include <stdlib.h>
 #include <string.h>
 
-int main()
-{
-    printf("[BEGIN] dlsym-in-interposed-malloc\n");
+#include "test_support.h"
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // malloc should have been called when dyld3's libdyld was initialized, but
     // call it one more time anyway just to make sure its working
     (void)malloc(1);
     
-
-    //printf("%p %p %p %p\n", p1, p2, p3, p4);
-    printf("[PASS] dlsym-in-interposed-malloc\n");
-       return 0;
+    PASS("Success");
 }
index b1752d65a95f2913ed29b63f77c1e8461059a2aa..4bc5ee365aff3d6e8c6915d022104a191cba7c43 100644 (file)
@@ -1,5 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/sub1 $BUILD_DIR/sub2
 // BUILD:  $CC sub1.c -dynamiclib -install_name @rpath/libsub1.dylib   -o $BUILD_DIR/sub1/libsub1.dylib
 // BUILD:  $CC sub2.c -dynamiclib -install_name @rpath/libsub2.dylib   -o $BUILD_DIR/sub2/libsub2.dylib
 // BUILD:  $CC foo.c  -dynamiclib -install_name $RUN_DIR/libfoo.dylib  -o $BUILD_DIR/libfoo.dylib -rpath @loader_path/sub1 -Wl,-reexport_library,$BUILD_DIR/sub1/libsub1.dylib -Wl,-reexport_library,$BUILD_DIR/sub2/libsub2.dylib
 #include <stdio.h>
 #include <dlfcn.h>
 
-int main()
-{
-    printf("[BEGIN] dlsym-re-export\n");
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // RTLD_FIRST means dlsym() should only search libfoo.dylib (and any re-exports)
-       void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST);
-       if ( handle == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL]  dlsym-re-export\n");
-               return 0;
-       }
-
-       void* sym1 = dlsym(handle, "sub1");
-       if ( sym1 == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL]  dlsym-re-export\n");
-               return 0;
-       }
-
-       void* sym2 = dlsym(handle, "sub2");
-       if ( sym2 == NULL ) {
-        printf("dlerror(): %s\n", dlerror());
-        printf("[FAIL]  dlsym-re-export\n");
-               return 0;
-       }
-
-    printf("[PASS]  dlsym-re-export\n");
-       return 0;
+    void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST);
+    if ( handle == NULL ) {
+        FAIL("dlerror(): %s", dlerror());
+    }
+
+    void* sym1 = dlsym(handle, "sub1");
+    if ( sym1 == NULL ) {
+        FAIL("dlerror(): %s", dlerror());
+    }
+
+    void* sym2 = dlsym(handle, "sub2");
+    if ( sym2 == NULL ) {
+        FAIL("dlerror(): %s", dlerror());
+    }
+
+    PASS("Success");
 }
 
index 088e50cc2a8d40909463d3c54a3309e00c6d831b..b3b79952313b272e1aea7dca4c34e1b984220f94 100644 (file)
@@ -2,31 +2,27 @@
 
 // if we ever re-enable this on iOS we will need to add // BOOT_ARGS: dtrace_dof_mode=1
 
-// BUILD:  /usr/sbin/dtrace -h -s main.d -o $TEMP_DIR/probes.h
-// BUILD:  $CC main.c -I$TEMP_DIR -o $BUILD_DIR/dtrace.exe
+// BUILD:  $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
 
-// RUN:    $SUDO dtrace -l -n 'dyld_testing*:dtrace.exe:main:callback' -c ./dtrace.exe
-
-
+// RUN:    $SUDO /usr/sbin/dtrace -o /dev/null 2> /dev/null -n 'dyld_testing*:dtrace.exe:main:callback' -c $RUN_DIR/dtrace.exe
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/sdt.h>
 
+#include "test_support.h"
+
 #include "probes.h"
 
-int main()
-{
-    printf("[BEGIN] dtrace\n");
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     DYLD_TESTING_CALLBACK();
 
     if (!DYLD_TESTING_CALLBACK_ENABLED())
-        printf("[FAIL] dtrace: DYLD_TESTING_CALLBACK_ENABLED() returned false\n");
+        FAIL("DYLD_TESTING_CALLBACK_ENABLED() returned false");
     else
-        printf("[PASS] dtrace\n");
-
-       return 0;
+        PASS("Success");
 }
index 113964778ca4e784a8556c16022b8fd6c9c80198..cc29682b7f220a73291a4fabfa36ec925bc48526 100644 (file)
@@ -1,7 +1,7 @@
 
 // BOOT_ARGS: dyld_flags=2
 
-// BUILD:  $CC main.cpp -std=c++11 -o $BUILD_DIR/double_insert_main.exe
+// BUILD:  $CXX main.cpp -std=c++11 -o $BUILD_DIR/double_insert_main.exe
 // BUILD:  $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
 // BUILD:  $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
 // BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/double_insert_main.exe
@@ -16,6 +16,8 @@
 #include <string.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
+
 bool gFoundLibrary = false;
 const char* gLibraryName = NULL;
 
@@ -28,19 +30,14 @@ bool wasImageLoaded(const char* libraryName) {
                }
        });
     if (!gFoundLibrary)
-        printf("[FAIL] dyld-insert-library-double: expected insert to pass for '%s'\n", libraryName);
+        FAIL("Expected insert to pass for '%s'", libraryName);
        return gFoundLibrary;
 }
 
-int main()
-{
-    printf("[BEGIN] dyld-insert-library-double\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if (!wasImageLoaded("libfoo.dylib") || !wasImageLoaded("libbar.dylib")) {
         return 0;
     }
 
-    printf("[PASS]  dyld-insert-library-double\n");
-
-       return 0;
+    PASS("Success");
 }
index 3bf1204b2af197c9f8de24fa1dd9511ca2512a09..d714e9334657e202326ee2af6157647d1063f99d 100644 (file)
@@ -1,8 +1,7 @@
 
 // BOOT_ARGS: dyld_flags=2
 
-// BUILD:  mkdir -p $BUILD_DIR/lib
-// BUILD:  $CC main.cpp -std=c++11 -o $BUILD_DIR/rpath_insert_main.exe  -Wl,-rpath,$RUN_DIR/lib
+// BUILD:  $CXX main.cpp -std=c++11 -o $BUILD_DIR/rpath_insert_main.exe  -Wl,-rpath,$RUN_DIR/lib
 // BUILD:  $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/lib/libfoo.dylib
 // 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
@@ -22,6 +21,8 @@
 #include <string.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
+
 bool gFoundLibrary = false;
 const char* gLibraryName = NULL;
 
@@ -36,13 +37,9 @@ bool wasImageLoaded(const char* libraryName) {
        return gFoundLibrary;
 }
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] dyld-insert-library-rpath\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if (argc != 2) {
-               printf("[FAIL] dyld-insert-library-rpath: expected library name\n");
-               return 0;
+        FAIL("Expected library name");
     }
 
     bool expectInsertFailure = getenv("DYLD_AMFI_FAKE") != NULL;
@@ -50,18 +47,14 @@ int main(int argc, const char* argv[])
     if (wasImageLoaded(argv[1])) {   
         // Image was loaded, but make sure that is what we wanted to happen
         if ( expectInsertFailure ) {
-            printf("[FAIL] dyld-insert-library-rpath: expected insert to fail for '%s'\n", argv[1]);
-            return 0;       
+            FAIL("Expected insert to fail for '%s'", argv[1]);
         }
     } else {
         // Image was not loaded, so make sure we are ok with that
         if ( !expectInsertFailure ) {
-            printf("[FAIL] dyld-insert-library-rpath: expected insert to pass for '%s'\n", argv[1]);
-            return 0;       
+            FAIL("Expected insert to pass for '%s'", argv[1]);
         }
     }
 
-    printf("[PASS]  dyld-insert-library-rpath\n");
-
-       return 0;
+    PASS("Success");
 }
diff --git a/testing/test-cases/dyld_abort_payload.dtest/main.c b/testing/test-cases/dyld_abort_payload.dtest/main.c
deleted file mode 100644 (file)
index 3657491..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-
-// BUILD:  $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib
-// BUILD:  $CC foo.c -dynamiclib $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libMissingDylib.dylib -o $BUILD_DIR/libMissingDylib.dylib
-// BUILD:  $CC emptyMain.c $BUILD_DIR/libMissingDylib.dylib  -o $BUILD_DIR/prog_missing_dylib.exe
-// BUILD:  $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib
-// BUILD:  $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL
-// BUILD:  $CC useSymbol.c $BUILD_DIR/libHasSymbols.dylib -o $BUILD_DIR/prog_missing_symbol.exe
-// BUILD:  $CC main.c -o $BUILD_DIR/dyld_abort_tests.exe
-
-// NO_CRASH_LOG: prog_missing_dylib.exe
-// NO_CRASH_LOG: prog_missing_symbol.exe
-
-// RUN:  ./dyld_abort_tests.exe 
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <err.h>
-#include <System/sys/reason.h>
-#include <System/sys/proc_info.h>
-#include <System/kern/kern_cdata.h>
-#include <libproc.h>
-#include <mach-o/dyld_priv.h>
-
-
-static bool        sSignalCaught = false;
-static bool        sChildAbortInfoCorrect = false;
-static pid_t       sChildPid = 0;
-static uint64_t    sExpectedDyldReason = 0;
-static const char* sExpectedDylibPath = NULL;
-static const char* sExpectedSymbol = NULL;
-
-
-static void childDied(int sig)
-{
-    sSignalCaught = true;
-    //printf("sigchld for pid=%d\n", sChildPid);
-
-    struct proc_exitreasoninfo info;
-    bzero(&info, sizeof(info));
-    uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE];
-    bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE);
-    info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
-    info.eri_kcd_buf = (user_addr_t)packReasonData;
-    //fprintf(stderr, "info=%p\n", &info);
-    int procResult = proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE);
-    if ( procResult != sizeof(struct proc_exitreasoninfo) ) {
-        printf("bad return size from proc_pidinfo(), %d expected %lu\n", procResult, PROC_PIDEXITREASONINFO_SIZE);
-        return;
-    }
-    if ( info.eri_namespace != OS_REASON_DYLD ) {
-        printf("eri_namespace (%d) != OS_REASON_DYLD\n", info.eri_namespace);
-        return;
-    }
-    if ( info.eri_code != sExpectedDyldReason ) {
-        printf("eri_code (%llu) != %lld\n", sExpectedDyldReason, info.eri_code);
-        return;
-    }
-    kcdata_iter_t iter = kcdata_iter(packReasonData, info.eri_reason_buf_size);
-
-    if ( !kcdata_iter_valid(iter) ) {
-        printf("invalid kcdata iterator from payload data\n");
-        return;
-    }
-
-    if ( kcdata_iter_type(iter) != KCDATA_BUFFER_BEGIN_OS_REASON ){
-        printf("first kcdata from payload data is not KCDATA_BUFFER_BEGIN_OS_REASON\n");
-        return;
-    }
-
-    kcdata_iter_t payloadIter = kcdata_iter_find_type(iter, EXIT_REASON_USER_PAYLOAD);
-    if ( !kcdata_iter_valid(payloadIter) ) {
-        printf("invalid kcdata payload iterator from payload data\n");
-        return;
-    }
-    const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter);
-
-    if ( dyldInfo->version != 1 ) {
-        printf("dyld payload is not version 1\n");
-        return;
-    }
-
-    if ( (dyldInfo->flags & 1) == 0 ) {
-        printf("dyld flags should have low bit set to me process terminated at launch\n");
-        return;
-    }
-
-    if ( sExpectedDylibPath != NULL ) {
-        if ( dyldInfo->targetDylibPathOffset != 0 ) {
-            const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset;
-            if ( strstr(targetDylib, sExpectedDylibPath) == NULL ) {
-                printf("dylib path (%s) not what expected (%s)\n", targetDylib, sExpectedDylibPath);
-                return;
-            }
-        }
-        else {
-            printf("dylib path (%s) not provided by dyld\n", sExpectedDylibPath);
-            return;
-        }
-    }
-
-   if ( sExpectedSymbol != NULL ) {
-        if ( dyldInfo->targetDylibPathOffset != 0 ) {
-            const char* missingSymbol = (char*)dyldInfo + dyldInfo->symbolOffset;
-            if ( strcmp(sExpectedSymbol, missingSymbol) != 0 ) {
-                printf("symbol (%s) not what expected (%s)\n", missingSymbol, sExpectedSymbol);
-                return;
-            }
-        }
-        else {
-            printf("symbol (%s) not provided by dyld\n", sExpectedSymbol);
-            return;
-        }
-    }
-
-   sChildAbortInfoCorrect = true;
-}
-
-
-bool runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* expectedSymbol)
-{
-    sSignalCaught = false;
-    sChildAbortInfoCorrect = false;
-    sExpectedDyldReason = dyldReason;
-    sExpectedDylibPath = expectedDylibPath;
-    sExpectedSymbol = expectedSymbol;
-
-    // fork and exec child
-       sChildPid = fork();
-       if ( sChildPid < 0 )
-               err(EXIT_FAILURE, "fork");
-       if ( sChildPid == 0 ) {
-               // child side
-        char* childArgv[] = { (char*)prog, NULL };
-               int result = execvp(prog, childArgv);
-        err(EXIT_FAILURE, "exec(\"%s\",...)", prog);
-       }
-    for(int i=0; i < 10; ++i) {
-        if ( sSignalCaught )
-            break;
-        sleep(1);
-    }
-
-    return sChildAbortInfoCorrect;
-}
-
-
-int main(int argc, const char* argv[])
-{
-    bool someTestFailed = false;
-    printf("[BEGIN] dyld_abort_payload\n");
-
-    // set up signal handler for catching child terminations
-    signal(SIGCHLD, childDied);
-
-    // test launch program with missing library
-    if ( !runTest("./prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL) ) {
-        printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_DYLIB_MISSING\n");
-        someTestFailed = true;
-    }
-
-    // test launch program with missing symbol
-    if ( !runTest("./prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol") ) {
-        printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_SYMBOL_MISSING\n");
-        someTestFailed = true;
-    }
-
-    if ( !someTestFailed )
-        printf("[PASS] dyld_abort_payload\n");
-
-    return 0;
-}
-
diff --git a/testing/test-cases/dyld_abort_payload.dtest/main.cpp b/testing/test-cases/dyld_abort_payload.dtest/main.cpp
new file mode 100644 (file)
index 0000000..9dfdc34
--- /dev/null
@@ -0,0 +1,118 @@
+
+// BUILD:  $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib
+// BUILD:  $CC foo.c -dynamiclib $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libMissingDylib.dylib -o $BUILD_DIR/libMissingDylib.dylib
+// BUILD:  $CC emptyMain.c $BUILD_DIR/libMissingDylib.dylib  -o $BUILD_DIR/prog_missing_dylib.exe
+// 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
+
+// NO_CRASH_LOG: prog_missing_dylib.exe
+// NO_CRASH_LOG: prog_missing_symbol.exe
+
+// RUN:  ./dyld_abort_tests.exe 
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <err.h>
+#include <System/sys/reason.h>
+#include <System/sys/proc_info.h>
+#include <System/kern/kern_cdata.h>
+#include <libproc.h>
+#include <mach-o/dyld_priv.h>
+
+#include "test_support.h"
+
+
+void runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* 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");
+        }
+        kcdata_iter_t autopsyData = kcdata_iter((void*)corpse_data, corpse_size);
+        if (!kcdata_iter_valid(autopsyData)) {
+            FAIL("Corpse Data Invalid");
+        }
+        kcdata_iter_t exitReasonData = kcdata_iter_find_type(autopsyData, EXIT_REASON_SNAPSHOT);
+        if (!kcdata_iter_valid(exitReasonData)) {
+            FAIL("Could not find exit data");
+        }
+        struct exit_reason_snapshot *ers = (struct exit_reason_snapshot *)kcdata_iter_payload(exitReasonData);
+
+        if ( ers->ers_namespace != OS_REASON_DYLD ) {
+            FAIL("eri_namespace (%d) != OS_REASON_DYLD", ers->ers_namespace);
+        }
+        if ( ers->ers_code != dyldReason ) {
+            FAIL("eri_code (%llu) != dyldReason (%lld)", ers->ers_code, dyldReason);
+        }
+        kcdata_iter_t iter = kcdata_iter((void*)corpse_data, corpse_size);
+
+        KCDATA_ITER_FOREACH(iter) {
+            if (kcdata_iter_type(iter) == KCDATA_TYPE_NESTED_KCDATA) {
+                kcdata_iter_t nestedIter = kcdata_iter(kcdata_iter_payload(iter), kcdata_iter_size(iter));
+                if ( kcdata_iter_type(nestedIter) != KCDATA_BUFFER_BEGIN_OS_REASON ){
+                    return;
+                }
+                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");
+                }
+                const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter);
+
+                if ( dyldInfo->version != 1 ) {
+                    FAIL("dyld payload is not version 1");
+                }
+
+                if ( (dyldInfo->flags & 1) == 0 ) {
+                    FAIL("dyld flags should have low bit  set to indicate process terminated during launch");
+                }
+
+                if ( expectedDylibPath != NULL ) {
+                    if ( dyldInfo->targetDylibPathOffset != 0 ) {
+                        const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset;
+                        if ( strstr(targetDylib, expectedDylibPath) == NULL ) {
+                            FAIL("dylib path (%s) not what expected (%s)", targetDylib, expectedDylibPath);
+                        }
+                    } else {
+                        FAIL("dylib path (%s) not provided by dyld", expectedDylibPath);
+                    }
+                }
+
+                if ( expectedSymbol != NULL ) {
+                    if ( dyldInfo->targetDylibPathOffset != 0 ) {
+                        const char* missingSymbol = (char*)dyldInfo + dyldInfo->symbolOffset;
+                        if ( strcmp(expectedSymbol, missingSymbol) != 0 ) {
+                            FAIL("symbol (%s) not what expected (%s)", missingSymbol, expectedSymbol);
+                        }
+                    } else {
+                        FAIL("symbol (%s) not provided by dyld", expectedSymbol);
+                    }
+                }
+                PASS("Success");
+            }
+        }
+        FAIL("Did not find EXIT_REASON_USER_PAYLOAD");
+    });
+    process.launch();
+}
+
+
+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");
+    PASS("Success");
+}
+
index 371c5a8ded6f7fdd410367f251f81821db378742..db601302990de73ef773fce16e6544c1c83bbbb0 100644 (file)
@@ -7,6 +7,8 @@
 #include <dlfcn.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
+
 bool isParent = true;
 
 static void notifyBeforeFork(const struct mach_header* mh, intptr_t vmaddr_slide)
@@ -19,8 +21,7 @@ static void notifyBeforeFork(const struct mach_header* mh, intptr_t vmaddr_slide
     // fork and exec child
     pid_t sChildPid = fork();
     if ( sChildPid < 0 ) {
-        printf("[FAIL] dyld_fork_test didn't fork\n");
-        return;
+        FAIL("Didn't fork");
     }
     if ( sChildPid == 0 ) {
         // child side
@@ -28,15 +29,12 @@ static void notifyBeforeFork(const struct mach_header* mh, intptr_t vmaddr_slide
     }
 }
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] dyld_fork_test\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     _dyld_register_func_for_add_image(&notifyBeforeFork);
 
     if (isParent) {
-        printf("[PASS] dyld_fork_test\n");
+        PASS("Success");
     }
 
     return 0;
-}
\ No newline at end of file
+}
index 71f6bc262e599f7e547f661f9528c4ac4dd1ab6f..a8c4a979f7674736b34322614771e8cb022d48fb 100644 (file)
@@ -7,15 +7,14 @@
 #include <string.h>
 #include <mach-o/dyld_priv.h>
 
-extern struct mach_header __dso_handle;
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] dyld_get_image_versions\n");
+extern struct mach_header __dso_handle;
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // should succeed
     dyld_get_image_versions(&__dso_handle, ^(dyld_platform_t platform, uint32_t sdkVersion, uint32_t minOS) {
-        printf("main binary: platform=%d, sdk=0x%08X, minOS-0x%08X\n", platform, sdkVersion, minOS);
+        LOG("main binary: platform=%d, sdk=0x%08X, minOS-0x%08X", platform, sdkVersion, minOS);
     });
 
     uint8_t badFile[4096];
@@ -31,11 +30,9 @@ int main()
 
     // should detect the mh is bad and not crash or call the callback
     dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdkVersion, uint32_t minOS) {
-        printf("bad binary: platform=%d, sdk=0x%08X, minOS-0x%08X\n", platform, sdkVersion, minOS);
+        LOG("bad binary: platform=%d, sdk=0x%08X, minOS-0x%08X", platform, sdkVersion, minOS);
     });
 
-
-    printf("[PASS] dyld_get_image_versions\n");
-       return 0;
+    PASS("Success");
 }
 
diff --git a/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt b/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt
deleted file mode 100644 (file)
index 43b3565..0000000
+++ /dev/null
@@ -1 +0,0 @@
-bad file
index c5032a0e9f75b6c491ccf5b295f8feefc4f23a9d..b2df7525af2dd53fbf7b5abd641154384356d9b4 100644 (file)
@@ -7,23 +7,20 @@
 #include <string.h>
 #include <mach-o/dyld_priv.h>
 
-extern struct mach_header __dso_handle;
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] dyld_get_sdk_version\n");
+extern struct mach_header __dso_handle;
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // should succeed
     if ( dyld_get_sdk_version(&__dso_handle) == 0 ) {
-        printf("[FAIL] dyld_get_sdk_version: expected SDK\n");
-        return 0;
+        FAIL("dyld_get_sdk_version: expected SDK");
     }
 
     // should fail
     const char* text = "bad text";
     if ( dyld_get_sdk_version((struct mach_header*)text) != 0 ) {
-        printf("[FAIL] dyld_get_sdk_version: expected failure\n");
-        return 0;
+        FAIL("dyld_get_sdk_version: expected failure");
     }
 
 
@@ -31,19 +28,16 @@ int main()
     uint32_t iosVersion = dyld_get_program_sdk_version();
     uint32_t watchOSVersion = dyld_get_program_sdk_watch_os_version();
     if (iosVersion != (watchOSVersion + 0x00070000)) {
-        printf("[FAIL] dyld_get_program_sdk_watch_os_version\n");
-        return 0;
+        FAIL("dyld_get_program_sdk_watch_os_version");
     }
 #endif
 #if TARGET_OS_BRIDGE
     uint32_t iosVersion = dyld_get_program_sdk_version();
     uint32_t bridgeOSVersion = dyld_get_program_sdk_bridge_os_version();
     if (bridgeOSVersion != (watchOSVersion + 0x00090000)) {
-        printf("[FAIL] dyld_get_program_sdk_watch_os_version\n");
-        return 0;
+        FAIL("dyld_get_program_sdk_watch_os_version");
     }
 #endif
-    printf("[PASS] dyld_get_sdk_version\n");
-       return 0;
+    PASS("Success");
 }
 
index 765b47da04131c5a771f6ea58f95221f9df60f6d..27df5a1b2029cb452f275890f7d41bc9a87d4b83 100644 (file)
 #include <string.h>
 #include <mach-o/dyld_priv.h>
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] dyld_has_inserted_or_interposing_libraries\n");
+#include "test_support.h"
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     bool actual = dyld_has_inserted_or_interposing_libraries();
     bool expected = (argc == 2) && (strcmp(argv[1], "true") == 0);
 
-    const char* result = (actual == expected) ? "PASS" : "FAIL";
-    printf("[%s]  dyld_has_inserted_or_interposing_libraries\n", result);
+    if (actual != expected) {
+        FAIL("dyld_has_inserted_or_interposing_libraries");
+    }
 
-       return 0;
+    PASS("Success");
 }
index 9064caf0051aed8509229cd7afcab20bc10ead9c..cf31cdaff9e455fa5e6b868f984dc4d6557094ad 100644 (file)
 #include <mach-o/dyld.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] dyld_image_path_containing_address-test\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     int count = _dyld_image_count();
     for (int i=0; i < count; ++i) {
         const struct mach_header* mh    = _dyld_get_image_header(i);
         const char*               name1 = _dyld_get_image_name(i);
         const char*               name2 = dyld_image_path_containing_address(mh);
         if ( strcmp(name1, name2) != 0 ) {
-            printf("[FAIL] dyld_image_path_containing_address-test: %s != %s\n", name1, name2);
-            return 0;
+            FAIL("%s != %s", name1, name2);
         }
     }
 
-    printf("[PASS] dyld_image_path_containing_address-test\n");
-       return 0;
+    PASS("Success");
 }
 
index 68aa687dbe1f179ff66149e0bc1e1d80f2d079d1..20b502962109e013af1487bd72e7fc215744facb 100644 (file)
@@ -8,24 +8,18 @@
 #include <stdlib.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] dyld_need_closure\n");
-
+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/") ) {
-        printf("[FAIL] dyld_closure: Should have needed a closure for containerised path\n");
-        return 0;
+        FAIL("Should have needed a closure for containerised path");
     }
 
     if ( dyld_need_closure("./foo.exe", "/tmp/Containers/Data2/") ) {
-        printf("[FAIL] dyld_closure: Should have rejected a closure for non-containerised path\n");
-        return 0;
+        FAIL("Should have rejected a closure for non-containerised path");
     }
 
-    printf("[PASS] dyld_need_closure\n");
-
-    return 0;
+    PASS("Success");
 }
index 862da98db7c58b5c1b91dd8e0d88ec2418b3a1d3..96c3a651aaa7569aef5d9fef28ca4866e53f8ec8 100644 (file)
@@ -1,13 +1,21 @@
+#include <stdio.h>
 #include <signal.h>
-#include <unistd.h>
-#include <mach/mach.h>
+#include <stdlib.h>
+#include <dispatch/dispatch.h>
 
 
 __attribute__((section("__DATA,__allow_alt_plat"))) uint64_t dummy;
 
-int main()
-{
-    (void)kill(getpid(), SIGSTOP);
-    return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    dispatch_source_t exitSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, getppid(),
+                                                            DISPATCH_PROC_EXIT, dispatch_get_main_queue());
+    dispatch_source_set_event_handler(exitSource, ^{
+        exit(0);
+    });
+    dispatch_resume(exitSource);
+    dispatch_async(dispatch_get_main_queue(), ^{
+        kill(getppid(), SIGUSR1);
+    });
+    dispatch_main();
 }
 
diff --git a/testing/test-cases/dyld_process_info.dtest/main.c b/testing/test-cases/dyld_process_info.dtest/main.c
deleted file mode 100644 (file)
index edd700b..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-
-// BUILD:  $CC linksWithCF.c  -o $BUILD_DIR/linksWithCF.exe -framework CoreFoundation
-// BUILD:  $CC main.c         -o $BUILD_DIR/dyld_process_info.exe -ldarwintest
-// BUILD:  $TASK_FOR_PID_ENABLE  $BUILD_DIR/dyld_process_info.exe
-
-// RUN:  $SUDO ./dyld_process_info.exe
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <spawn.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-#include <sys/types.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <mach-o/dyld_priv.h>
-#include <mach-o/dyld_process_info.h>
-#include <Availability.h>
-
-#include "dyld_test.h"
-
-static void inspectProcess(task_t task, bool launchedSuspended, bool expectCF, bool forceIOSMac)
-{
-    kern_return_t result;
-    dyld_process_info info = _dyld_process_info_create(task, 0, &result);
-    T_EXPECT_MACH_SUCCESS(result, "dyld_process_info() should succeed");
-    T_ASSERT_NOTNULL(info, "dyld_process_info(task, 0) alwats return a value");
-
-    dyld_process_state_info stateInfo;
-    bzero(&stateInfo, sizeof(stateInfo));
-    _dyld_process_info_get_state(info, &stateInfo);
-    T_EXPECT_EQ_UINT((stateInfo.dyldState == dyld_process_state_not_started), launchedSuspended, "If launchSuspended then stateInfo.dyldState shoould be dyld_process_state_not_started");
-    if ( !launchedSuspended ) {
-        T_EXPECT_GE_UCHAR(stateInfo.dyldState, dyld_process_state_libSystem_initialized, "libSystem should be initalized by now");
-        T_EXPECT_GT_UINT(stateInfo.imageCount, 0, "image count should be > 0");
-        T_EXPECT_GT_UINT(stateInfo.initialImageCount, 0, "initial image count should be > 0");
-        T_EXPECT_GE_UINT(stateInfo.imageCount, stateInfo.initialImageCount, "image count should be >= initial image count");
-    }
-
-    if (launchedSuspended) {
-        T_EXPECT_EQ_UINT(_dyld_process_info_get_platform(info), 0, "_dyld_process_info_get_platform() should be 0 for launchSuspended processes");
-    } else if (forceIOSMac) {
-        T_EXPECT_EQ_UINT(_dyld_process_info_get_platform(info), PLATFORM_IOSMAC, "_dyld_process_info_get_platform() should be PLATFORM_IOSMAC");
-    } else {
-        T_EXPECT_EQ_UINT(_dyld_process_info_get_platform(info), dyld_get_active_platform(), "_dyld_process_info_get_platform() should be the same dyld_get_active_platform()");
-    }
-
-    __block bool foundDyld = false;
-    __block bool foundMain = false;
-    __block bool foundCF = false;
-    _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
-        if ( strstr(path, "/dyld") != NULL )
-            foundDyld = true;
-        if ( strstr(path, "/linksWithCF.exe") != NULL )
-            foundMain = true;
-        if ( strstr(path, "/dyld_process_info.exe") != NULL )
-            foundMain = true;
-        if ( strstr(path, "/CoreFoundation.framework/") != NULL )
-            foundCF = true;
-    });
-    T_EXPECT_TRUE(foundDyld, "dyld should always be in the image list");
-    T_EXPECT_TRUE(foundMain, "The main executable should always be in the image list");
-    if (expectCF) {
-        T_EXPECT_TRUE(foundCF, "CF should be in the image list");
-    }
-
-     _dyld_process_info_release(info);
-}
-
-static void launchTest(bool launchOtherArch, bool launchSuspended, bool forceIOSMac)
-{
-    if (forceIOSMac) { setenv("DYLD_FORCE_PLATFORM", "6", 1); }
-    pid_t pid = T_POSIXSPAWN_ASSERT(launchSuspended, launchOtherArch, INSTALL_PATH "/linksWithCF.exe");
-    task_t task = T_TASK_FOR_PID_ASSERT(pid);
-    if (forceIOSMac) { unsetenv("DYLD_FORCE_PLATFORM"); }
-
-    // wait until process is up and has suspended itself
-    struct task_basic_info info;
-    do {
-        unsigned count = TASK_BASIC_INFO_COUNT;
-        kern_return_t kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count);
-        usleep(10000);
-    } while ( info.suspend_count == 0 );
-
-    inspectProcess(task, launchSuspended, !launchSuspended, forceIOSMac);
-    int r = kill(pid, SIGKILL);
-    waitpid(pid, &r, 0);
-}
-
-T_DECL_DYLD(dyld_process_info, "Test basic dyld_process_info functionality", T_META_ASROOT(true)) {
-    launchTest(false, false, false);
-    launchTest(false, true, false);
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-    // FIXME: Reenable these ones i386 is turned back on for simulators
-    //launchTest(true, false, false);
-    //launchTest(true, true, false);
-    launchTest(false, false, true);
-    launchTest(false, true, true);
-    //FIXME: This functionality is broken, but it is an edge case no one should ever hit
-    //launchTest(true, true, true);
-#endif
-    inspectProcess(mach_task_self(), false, false, false);
-}
diff --git a/testing/test-cases/dyld_process_info.dtest/main.cpp b/testing/test-cases/dyld_process_info.dtest/main.cpp
new file mode 100644 (file)
index 0000000..75d6789
--- /dev/null
@@ -0,0 +1,162 @@
+
+// BUILD:  $CC linksWithCF.c    -o $BUILD_DIR/linksWithCF.exe -framework CoreFoundation
+// BUILD:  $CXX main.cpp          -o $BUILD_DIR/dyld_process_info.exe -DRUN_DIR="$RUN_DIR"
+// BUILD:  $TASK_FOR_PID_ENABLE  $BUILD_DIR/dyld_process_info.exe
+
+// RUN:  $SUDO ./dyld_process_info.exe
+
+#include <Block.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/dyld_priv.h>
+#include <mach-o/dyld_process_info.h>
+#include <Availability.h>
+
+#include "test_support.h"
+
+
+static void inspectProcess(task_t task, bool launchedSuspended, bool expectCF, bool forceIOSMac)
+{
+    kern_return_t result;
+    dyld_process_info info = _dyld_process_info_create(task, 0, &result);
+    if (result != KERN_SUCCESS) {
+        FAIL("dyld_process_info() should succeed");
+    }
+    if (info == NULL) {
+        FAIL("dyld_process_info(task, 0) alwats return a value");
+    }
+
+    dyld_process_state_info stateInfo;
+    bzero(&stateInfo, sizeof(stateInfo));
+    _dyld_process_info_get_state(info, &stateInfo);
+    if ((stateInfo.dyldState == dyld_process_state_not_started) != launchedSuspended) {
+        FAIL("If launchSuspended then stateInfo.dyldState shoould be dyld_process_state_not_started");
+    }
+    if ( !launchedSuspended ) {
+        if (stateInfo.dyldState < dyld_process_state_libSystem_initialized) { FAIL("libSystem should be initalized by now"); }
+        if (stateInfo.imageCount == 0) { return FAIL("image count should be > 0"); }
+        if (stateInfo.initialImageCount == 0) { return FAIL("initial image count should be > 0"); }
+        if (stateInfo.imageCount < stateInfo.initialImageCount) { FAIL("image count should be >= initial image count"); }
+    }
+
+    dyld_platform_t remotePlatform = _dyld_process_info_get_platform(info);
+    dyld_platform_t localPlatform = dyld_get_active_platform();
+    if (launchedSuspended) {
+        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 != localPlatform)) {
+        FAIL("_dyld_process_info_get_platform(%u) should be the same dyld_get_active_platform(%u)",
+             remotePlatform, localPlatform);
+    }
+
+    __block bool foundDyld = false;
+    __block bool foundMain = false;
+    __block bool foundCF = false;
+    _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
+        if ( strstr(path, "/dyld") != NULL )
+            foundDyld = true;
+        if ( strstr(path, "/linksWithCF.exe") != NULL )
+            foundMain = true;
+        if ( strstr(path, "/dyld_process_info.exe") != NULL )
+            foundMain = true;
+        if ( strstr(path, "/CoreFoundation.framework/") != NULL )
+            foundCF = true;
+    });
+    if (!foundDyld) { FAIL("dyld should always be in the image list"); }
+    if (!foundMain) { FAIL("The main executable should always be in the image list"); }
+    if (expectCF && !foundCF) { FAIL("CF should be in the image list"); }
+
+     _dyld_process_info_release(info);
+}
+
+#if __x86_64__
+cpu_type_t otherArch[] = { CPU_TYPE_I386 };
+#elif __i386__
+cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
+#elif __arm64__
+cpu_type_t otherArch[] = { CPU_TYPE_ARM };
+#elif __arm__
+cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
+#endif
+
+
+static void launchTest(bool launchOtherArch, bool launchSuspended, bool forceIOSMac)
+{
+    LOG("launchTest %s", launchSuspended ? "suspended" : "unsuspended");
+    const char * program = RUN_DIR "/linksWithCF.exe";
+
+    _process process;
+    process.set_executable_path(RUN_DIR "/linksWithCF.exe");
+    process.set_launch_suspended(launchSuspended);
+    process.set_launch_async(true);
+    if (forceIOSMac) {
+        LOG("Launching native");
+        const char* env[] = { "TEST_OUTPUT=None", "DYLD_FORCE_PLATFORM=6", NULL};
+        process.set_env(env);
+    } else {
+        LOG("Launching iOSMac");
+        const char* env[] = { "TEST_OUTPUT=None", NULL};
+        process.set_env(env);
+    }
+    pid_t pid = process.launch();
+    LOG("launchTest pid: %d", pid);
+
+    task_t task;
+    if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS) {
+        FAIL("task_for_pid() failed");
+    }
+    LOG("launchTest task: %u", task);
+
+    // wait until process is up and has suspended itself
+    if (!launchSuspended) {
+        dispatch_queue_t queue = dispatch_queue_create("com.apple.test.dyld_process_info", NULL);
+        // We do this instead of using a dispatch_semaphore to prevent priority inversions
+        dispatch_block_t oneShotSemaphore = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
+        dispatch_source_t signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1,
+                                                                0, queue);
+        dispatch_source_set_event_handler(signalSource, ^{
+            LOG("Recieved signal");
+            oneShotSemaphore();
+            dispatch_source_cancel(signalSource);
+        });
+        dispatch_resume(signalSource);
+        dispatch_block_wait(oneShotSemaphore, DISPATCH_TIME_FOREVER);
+    }
+    LOG("task running");
+
+    inspectProcess(task, launchSuspended, !launchSuspended, forceIOSMac);
+}
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    signal(SIGUSR1, SIG_IGN);
+    TIMEOUT(120);
+    launchTest(false, false, false);
+    launchTest(false, true, false);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+    // FIXME: Reenable these ones i386 is turned back on for simulators
+    //launchTest(true, false, false);
+    //launchTest(true, true, false);
+    launchTest(false, false, true);
+    launchTest(false, true, true);
+    //FIXME: This functionality is broken, but it is an edge case no one should ever hit
+    //launchTest(true, true, true);
+#endif
+    dispatch_async( dispatch_get_main_queue(), ^{
+        inspectProcess(mach_task_self(), false, false, false);
+        PASS("Success");
+    });
+    dispatch_main();
+}
diff --git a/testing/test-cases/dyld_process_info_notify.dtest/main.c b/testing/test-cases/dyld_process_info_notify.dtest/main.c
deleted file mode 100644 (file)
index eaab560..0000000
+++ /dev/null
@@ -1,377 +0,0 @@
-
-// BUILD:  $CC target.c      -o $BUILD_DIR/target.exe
-// BUILD:  $CC foo.c         -o $BUILD_DIR/libfoo.dylib -dynamiclib
-// BUILD:  $CC main.c        -o $BUILD_DIR/dyld_process_info_notify.exe
-// BUILD:  $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_notify.exe
-
-// RUN_TIMEOUT: 2400
-// RUN:  $SUDO ./dyld_process_info_notify.exe  $RUN_DIR/target.exe
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <spawn.h>
-#include <errno.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <mach-o/dyld_process_info.h>
-#include <dispatch/dispatch.h>
-#include <Availability.h>
-
-
-extern char** environ;
-
-#if __x86_64__
-    cpu_type_t otherArch[] = { CPU_TYPE_I386 };
-#elif __i386__
-    cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
-#elif __arm64__
-    cpu_type_t otherArch[] = { CPU_TYPE_ARM };
-#elif __arm__
-    cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
-#endif
-
-struct task_and_pid {
-    pid_t pid;
-    task_t task;
-};
-
-static struct task_and_pid launchTest(const char* testProgPath, const char* arg1, bool launchOtherArch, bool launchSuspended)
-{
-    //fprintf(stderr, "launchTest() launchOtherArch=%d, launchSuspended=%d, arg=%s\n", launchOtherArch, launchSuspended, arg1);
-    posix_spawnattr_t attr = 0;
-    if ( posix_spawnattr_init(&attr) != 0 ) {
-        printf("[FAIL] dyld_process_info_notify posix_spawnattr_init()\n");
-        exit(0);
-    }
-    if ( launchSuspended ) {
-        if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
-            printf("[FAIL] dyld_process_info_notify POSIX_SPAWN_START_SUSPENDED\n");
-            exit(0);
-        }
-    }
-    if ( launchOtherArch ) {
-        size_t copied;
-        if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) {
-           printf("[FAIL] dyld_process_info_notify posix_spawnattr_setbinpref_np()\n");
-            exit(0);
-        }
-    }
-
-    struct task_and_pid child;
-    const char* argv[] = { testProgPath, arg1, NULL };
-    int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ);
-    if ( psResult != 0 ) {
-        printf("[FAIL] dyld_process_info_notify posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
-        exit(0);
-    }
-    if (posix_spawnattr_destroy(&attr) != 0) {
-        printf("[FAIL] dyld_process_info_notify posix_spawnattr_destroy()\n");
-        exit(0);
-    }
-    if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
-        printf("[FAIL] dyld_process_info_notify task_for_pid()\n");
-        (void)kill(child.pid, SIGKILL);
-        exit(0);
-    }
-
-    return child;
-}
-
-static void killTest(struct task_and_pid tp) {
-    int r = kill(tp.pid, SIGKILL);
-    waitpid(tp.pid, &r, 0);
-}
-
-static void wait_util_task_suspended(task_t task)
-{
-   struct task_basic_info info;
-    do {
-        unsigned count = TASK_BASIC_INFO_COUNT;
-        kern_return_t kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count);
-        //fprintf(stderr, "task_info() => %d, suspendCount=%d\n", kr, info.suspend_count);
-        sleep(1);
-    } while ( info.suspend_count == 0 );
-}
-
-
-static bool monitor(struct task_and_pid tp, bool disconnectEarly, bool attachLate)
-{
-    kern_return_t kr;
-    __block bool sawMainExecutable = false;
-    __block bool sawlibSystem = false;
-    __block bool gotTerminationNotice = false;
-    __block bool gotEarlyNotice = false;
-    __block bool gotMainNotice = false;
-    __block bool gotMainNoticeBeforeAllInitialDylibs = false;
-    __block bool gotFooNoticeBeforeMain = false;
-
-    __block int libFooLoadCount = 0;
-    __block int libFooUnloadCount = 0;
-    dispatch_semaphore_t taskDone = dispatch_semaphore_create(0);
-
-    dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
-
-    unsigned count = 0;
-    dyld_process_info_notify handle;
-    do {
-        handle = _dyld_process_info_notify(tp.task, serviceQueue,
-                                          ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
-                                            if ( strstr(path, "/target.exe") != NULL )
-                                                sawMainExecutable = true;
-                                            if ( strstr(path, "/libSystem") != NULL )
-                                                sawlibSystem = true;
-                                            if ( strstr(path, "/libfoo.dylib") != NULL ) {
-                                                if ( !gotMainNotice )
-                                                    gotFooNoticeBeforeMain = true;
-                                                if ( unload )
-                                                    ++libFooUnloadCount;
-                                                else
-                                                    ++libFooLoadCount;
-                                                if ( disconnectEarly ) {
-                                                    gotEarlyNotice = true;
-                                                    dispatch_semaphore_signal(taskDone);
-                                                }
-                                            }
-                                          },
-                                          ^{
-                                            gotTerminationNotice = true;
-                                            dispatch_semaphore_signal(taskDone);
-                                          },
-                                          &kr);
-        ++count;
-        if ( handle == NULL )
-            fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d, count=%d\n", kr, count);
-     } while ( (handle == NULL) && (count < 5) );
-
-    if ( handle == NULL ) {
-        return false;
-    }
-
-    if (!attachLate) {
-        // If the process starts suspended register for main(),
-        // otherwise skip since this test is a race between
-        // process setup and notification registration
-        _dyld_process_info_notify_main(handle, ^{
-                                                //fprintf(stderr, "target entering main()\n");
-                                                gotMainNotice = true;
-                                                if ( !sawMainExecutable || !sawlibSystem )
-                                                    gotMainNoticeBeforeAllInitialDylibs = true;
-                                                });
-    } else {
-        // if process suspends itself, wait until it has done so
-        wait_util_task_suspended(tp.task);
-    }
-
-    // resume from initial suspend
-    kill(tp.pid, SIGCONT);
-
-    // block waiting for notification that target has exited
-    bool gotSignal = (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0);
-    _dyld_process_info_notify_release(handle);
-
-
-    if ( !gotSignal ) {
-        fprintf(stderr, "did not get exit signal\n");
-        return false;
-    }
-
-    // Do not run any tests associated with startup unless the kernel suspended us
-    // before main()
-    if (!attachLate) {
-        if ( !sawMainExecutable ) {
-            fprintf(stderr, "did not get load notification of main executable\n");
-            return false;
-        }
-
-        if ( !gotMainNotice ) {
-            fprintf(stderr, "did not get notification of main()\n");
-            return false;
-        }
-
-        if ( gotMainNoticeBeforeAllInitialDylibs ) {
-            fprintf(stderr, "notification of main() arrived before all initial dylibs\n");
-            return false;
-        }
-
-        if ( gotFooNoticeBeforeMain ) {
-            fprintf(stderr, "notification of main() arrived after libfoo load notice\n");
-            return false;
-        }
-
-        if ( !sawlibSystem ) {
-            fprintf(stderr, "did not get load notification of libSystem\n");
-            return false;
-        }
-    }
-
-    if ( disconnectEarly ) {
-        if ( libFooLoadCount != 1 ) {
-            fprintf(stderr, "got %d load notifications about libFoo instead of 1\n", libFooLoadCount);
-            return false;
-        }
-        if ( libFooUnloadCount != 0 ) {
-            fprintf(stderr, "got %d unload notifications about libFoo instead of 1\n", libFooUnloadCount);
-            return false;
-        }
-    }
-    else {
-        if ( libFooLoadCount != 3 ) {
-            fprintf(stderr, "got %d load notifications about libFoo instead of 3\n", libFooLoadCount);
-            return false;
-        }
-        if ( libFooUnloadCount != 3 ) {
-            fprintf(stderr, "got %d unload notifications about libFoo instead of 3\n", libFooUnloadCount);
-            return false;
-        }
-    }
-
-    return true;
-}
-
-static void validateMaxNotifies(struct task_and_pid tp)
-{
-    dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
-    dyld_process_info_notify handles[10];
-    // This loop goes through 10 iterations
-    // i = 0..7 Should succeed
-    // i = 8 Should fail,  but trigger a release that frees up a slot
-    // i = 9 Should succeed
-    for (int i=0; i < 10; ++i) {
-        kern_return_t kr;
-        handles[i] = _dyld_process_info_notify(tp.task, serviceQueue,
-                                          ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
-                                            //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n",
-                                            //   unload, machHeader, uuid[0],  uuid[1],  uuid[2],  uuid[3],  uuid[4],  uuid[5],  uuid[6],  uuid[7],
-                                            //                        uuid[8],  uuid[9],  uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path);
-                                          },
-                                          ^{
-                                            //fprintf(stderr, "target exited\n");
-                                          },
-                                          &kr);
-        if ( handles[i] == NULL ) {
-            if ( i == 8 ) {
-                // expected failure, because only 8 simultaneous connections allowed
-                // release one and try again
-                _dyld_process_info_notify_release(handles[4]);
-                handles[4] = NULL;
-            }
-            else {
-                fprintf(stderr, "_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d\n", kr, i);
-                killTest(tp);
-                exit(0);
-            }
-        }
-    }
-    // release all
-    for (int i=0; i < 10; ++i) {
-        if ( handles[i] != NULL ) {
-            _dyld_process_info_notify_release(handles[i]);
-        }
-    }
-    dispatch_release(serviceQueue);
-}
-
-static bool testSelfAttach(void) {
-    __block bool retval = false;
-    kern_return_t kr = KERN_SUCCESS;
-    dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
-    dyld_process_info_notify handle = _dyld_process_info_notify(mach_task_self(), serviceQueue,
-                                       ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
-                                           if ( strstr(path, "/libfoo.dylib") != NULL ) {
-                                               retval = true;
-                                           }
-                                       },
-                                       ^{},
-                                       &kr);
-    if ( handle == NULL ) {
-        fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d\n", kr);
-    }
-    void* h = dlopen("./libfoo.dylib", 0);
-    dlclose(h);
-    return retval;
-}
-
-int main(int argc, const char* argv[])
-{
-    if ( argc < 2 ) {
-        printf("[FAIL] dyld_process_info_notify missing argument\n");
-        exit(0);
-    }
-    const char* testProgPath = argv[1];
-
-    dispatch_async(dispatch_get_main_queue(), ^{
-        struct task_and_pid child;
-
-        // test 1) launch test program suspended in same arch as this program
-        printf("[BEGIN] dyld_process_info_notify laucnh suspended (same arch)\n");
-        child = launchTest(testProgPath, "", false, true);
-        if ( ! monitor(child, false, false) ) {
-            printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n");
-            killTest(child);
-            exit(0);
-        }
-        killTest(child);
-        printf("[PASS] dyld_process_info_notify laucnh suspended (same arch)\n");
-
-        // test 2) launch test program in same arch as this program where it sleeps itself
-        printf("[BEGIN] dyld_process_info_notify laucnh suspend-in-main (same arch)\n");
-        child = launchTest(testProgPath, "suspend-in-main", false, false);
-        validateMaxNotifies(child);
-        if ( ! monitor(child, false, true) ) {
-            printf("[FAIL] dyld_process_info_notify launch suspend-in-main missed some notifications\n");
-            killTest(child);
-            exit(0);
-        }
-        killTest(child);
-        printf("[PASS] dyld_process_info_notify laucnh suspend-in-main (same arch)\n");
-
-#if 0
-        // test 3) launch test program suspended in opposite arch as this program
-        printf("[BEGIN] dyld_process_info_notify laucnh suspended (other arch)\n");
-        child = launchTest(testProgPath, "", true, true);
-        if ( ! monitor(child, false, false) ) {
-            printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n");
-            killTest(child);
-            exit(0);
-        }
-        killTest(child);
-        printf("[PASS] dyld_process_info_notify laucnh suspended (other arch)\n");
-
-        // test 4) launch test program in opposite arch as this program where it sleeps itself
-        printf("[BEGIN] dyld_process_info_notify laucnh suspend-in-main (other arch)\n");
-        child = launchTest(testProgPath, "suspend-in-main", true, false);
-        if ( ! monitor(child, false, true) ) {
-            printf("[FAIL] dyld_process_info_notify launch other arch suspend-in-main missed some notifications\n");
-            killTest(child);
-            exit(0);
-        }
-        killTest(child);
-        printf("[PASS] dyld_process_info_notify laucnh suspend-in-main (other arch)\n");
-#endif
-
-        // test 5) launch test program where we disconnect from it after first dlopen
-        printf("[BEGIN] dyld_process_info_notify disconnect\n");
-        child = launchTest(testProgPath, "", false, true);
-        if ( ! monitor(child, true, false) ) {
-            printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n");
-            killTest(child);
-            exit(0);
-        }
-        killTest(child);
-        printf("[PASS] dyld_process_info_notify disconnect\n");
-
-        // test 6) attempt to monitor the monitoring process
-        printf("[BEGIN] dyld_process_info_notify self-attach\n");
-        if (! testSelfAttach() ) {
-            printf("[FAIL] dyld_process_info_notify self notification\n");
-        }
-        printf("[PASS] dyld_process_info_notify self-attach\n");
-
-        exit(0);
-    });
-
-    dispatch_main();
-}
diff --git a/testing/test-cases/dyld_process_info_notify.dtest/main.cpp b/testing/test-cases/dyld_process_info_notify.dtest/main.cpp
new file mode 100644 (file)
index 0000000..35d8f15
--- /dev/null
@@ -0,0 +1,277 @@
+
+// BUILD:  $CC target.c      -o $BUILD_DIR/target.exe -DRUN_DIR="$RUN_DIR"
+// BUILD:  $CC foo.c         -o $BUILD_DIR/libfoo.dylib -dynamiclib
+// BUILD:  $CXX main.cpp        -o $BUILD_DIR/dyld_process_info_notify.exe -DRUN_DIR="$RUN_DIR"
+// BUILD:  $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_notify.exe
+
+// RUN_TIMEOUT: 2400
+// XFAIL:  $SUDO ./dyld_process_info_notify.exe  $RUN_DIR/target.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/dyld_process_info.h>
+#include <dispatch/dispatch.h>
+#include <Availability.h>
+
+#include "test_support.h"
+
+extern char** environ;
+
+void launchTest(bool launchSuspended, bool disconnectEarly)
+{
+    LOG("launchTest (%s)", launchSuspended ? "suspended" : "unsuspened");
+    dispatch_queue_t queue = dispatch_queue_create("com.apple.dyld.test.dyld_process_info", NULL);
+    // We do this instead of using a dispatch_semaphore to prevent priority inversions
+    dispatch_block_t taskDone = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
+    dispatch_block_t taskStarted = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
+    pid_t pid;
+    
+    task_t task;
+    char subTestNameBuffer[256];
+    char *subTestName = &subTestNameBuffer[0];
+    __block bool sawMainExecutable = false;
+    __block bool sawlibSystem = false;
+    __block bool gotTerminationNotice = false;
+    __block bool gotEarlyNotice = false;
+    __block bool gotMainNotice = false;
+    __block bool gotMainNoticeBeforeAllInitialDylibs = false;
+    __block bool gotFooNoticeBeforeMain = false;
+
+    __block int libFooLoadCount = 0;
+    __block int libFooUnloadCount = 0;
+    __block dyld_process_info_notify handle;
+
+    _process process;
+    process.set_executable(RUN_DIR "/target.exe");
+    const char* env[] = { "TEST_OUTPUT=None", NULL};
+    process.set_env(env);
+    process.set_launch_suspended(launchSuspended);
+    if (!launchSuspended) {
+        const char* args[] = {"suspend-in-main", NULL};
+        _process_config_set_args(process, args);
+        _process_config_set_stderr_handler(process, ^(int fd) {
+            dispatch_semaphore_signal(taskStarted);
+        });
+        _process_config_set_exit_handler(process, ^(pid_t pid) {
+            LOG("DIED (pid: %d)", pid);
+        });
+    }
+    pid = process.launch(queue);
+
+    if (!launchSuspended && dispatch_semaphore_wait(taskStarted, dispatch_time(DISPATCH_TIME_NOW, 5LL * NSEC_PER_SEC)) != 0) {
+        FAIL("Child launch timeout");
+    }
+#if 1
+    snprintf(&subTestNameBuffer[0], 256, "%s (arch: %d)", launchSuspended ? "launch suspended" : "launch suspend-in-main", currentArch);
+
+    if ( task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS ) {
+        FAIL("task_for_pid()");
+    }
+
+    kern_return_t kr;
+    unsigned count = 0;
+    do {
+        handle = _dyld_process_info_notify(task, queue,
+                                          ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
+                                            if ( strstr(path, "/target.exe") != NULL )
+                                                sawMainExecutable = true;
+                                            if ( strstr(path, "/libSystem") != NULL )
+                                                sawlibSystem = true;
+                                            if ( strstr(path, "/libfoo.dylib") != NULL ) {
+                                                if ( !gotMainNotice ) {
+                                                    gotFooNoticeBeforeMain = true;
+                                                }
+                                                if ( unload ) {
+                                                    ++libFooUnloadCount;
+                                                } else {
+                                                    ++libFooLoadCount;
+                                                }
+                                                if ( disconnectEarly ) {
+                                                    LOG("EARLY DISCONNECT");
+                                                    gotEarlyNotice = true;
+                                                    dispatch_semaphore_signal(taskDone);
+                                                }
+                                            }
+                                          },
+                                          ^{
+                                            LOG("TERMINATED (pid: %d)", pid);
+                                            gotTerminationNotice = true;
+                                            dispatch_semaphore_signal(taskDone);
+                                          },
+                                          &kr);
+        ++count;
+        if ( handle == NULL )
+            LOG("_dyld_process_info_notify() returned NULL, result=%d, count=%d", kr, count);
+     } while ( (handle == NULL) && (count < 5) );
+
+    if ( handle == NULL ) {
+        FAIL("%s: did not not get handle", subTestName);
+    }
+
+    if (launchSuspended) {
+        // If the process starts suspended register for main(),
+        // otherwise skip since this test is a race between
+        // process setup and notification registration
+        _dyld_process_info_notify_main(handle, ^{
+                                                LOG("target entering main()");
+                                                gotMainNotice = true;
+                                                if ( !sawMainExecutable || !sawlibSystem )
+                                                    gotMainNoticeBeforeAllInitialDylibs = true;
+                                                });
+        kill(pid, SIGCONT);
+        LOG("Sent SIGCONT");
+    } else {
+        kill(pid, SIGUSR1);
+        LOG("Sent SIGUSR1");
+    }
+
+    // block waiting for notification that target has exited
+    if (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) != 0) {
+         FAIL("%s: did not get exit signal", subTestName);
+    }
+
+//    dispatch_release(taskDone);
+//    dispatch_release(queue);
+//    _dyld_process_info_notify_release(handle);
+
+    // Do not run any tests associated with startup unless the kernel suspended us
+    // before main()
+    if (launchSuspended) {
+        if ( !sawMainExecutable ) {
+            FAIL("%s: did not get load notification of main executable", subTestName);
+        }
+
+        if ( !gotMainNotice ) {
+            FAIL("%s: did not get notification of main()", subTestName);
+        }
+
+        if ( gotMainNoticeBeforeAllInitialDylibs ) {
+            FAIL("%s: notification of main() arrived before all initial dylibs", subTestName);
+        }
+
+        if ( gotFooNoticeBeforeMain ) {
+            FAIL("%s: notification of main() arrived after libfoo load notice", subTestName);
+        }
+
+        if ( !sawlibSystem ) {
+            FAIL("%s: did not get load notification of libSystem", subTestName);
+        }
+    }
+
+    if ( disconnectEarly ) {
+        if ( libFooLoadCount != 1 ) {
+            FAIL("%s: got %d load notifications about libFoo instead of 1", subTestName, libFooLoadCount);
+        }
+        if ( libFooUnloadCount != 0 ) {
+            FAIL("%s: got %d unload notifications about libFoo instead of 1", subTestName, libFooUnloadCount);
+        }
+    }
+    else {
+        if ( libFooLoadCount != 3 ) {
+            FAIL("%s: got %d load notifications about libFoo instead of 3", subTestName, libFooLoadCount);
+        }
+        if ( libFooUnloadCount != 3 ) {
+            FAIL("%s: got %d unload notifications about libFoo instead of 3", subTestName, libFooUnloadCount);
+        }
+    }
+#endif
+}
+
+#if 0
+static void validateMaxNotifies(struct task_and_pid tp)
+{
+    dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
+    dyld_process_info_notify handles[10];
+    // This loop goes through 10 iterations
+    // i = 0..7 Should succeed
+    // i = 8 Should fail,  but trigger a release that frees up a slot
+    // i = 9 Should succeed
+    for (int i=0; i < 10; ++i) {
+        kern_return_t kr;
+        handles[i] = _dyld_process_info_notify(tp.task, serviceQueue,
+                                          ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
+                                            LOG("unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s",
+                                                unload, machHeader, uuid[0],  uuid[1],  uuid[2],  uuid[3],  uuid[4],  uuid[5],  uuid[6],  uuid[7],
+                                                uuid[8],  uuid[9],  uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path);
+                                          },
+                                          ^{
+                                            LOG("target exited");
+                                          },
+                                          &kr);
+        if ( handles[i] == NULL ) {
+            if ( i == 8 ) {
+                // expected failure, because only 8 simultaneous connections allowed
+                // release one and try again
+                _dyld_process_info_notify_release(handles[4]);
+                handles[4] = NULL;
+            }
+            else {
+                LOG("_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d", kr, i);
+                killTest(tp);
+                exit(0);
+            }
+        }
+    }
+    // release all
+    for (int i=0; i < 10; ++i) {
+        if ( handles[i] != NULL ) {
+            _dyld_process_info_notify_release(handles[i]);
+        }
+    }
+    dispatch_release(serviceQueue);
+}
+#endif
+
+static void testSelfAttach(void) {
+    LOG("7");
+    __block bool dylibLoadNotified = false;
+    kern_return_t kr = KERN_SUCCESS;
+    dispatch_queue_t queue = dispatch_queue_create("com.apple.dyld.test.dyld_process_info.self-attach", NULL);
+    LOG("7.5");
+    dyld_process_info_notify handle = _dyld_process_info_notify(mach_task_self(), queue,
+                                       ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
+                                           if ( strstr(path, "/libfoo.dylib") != NULL ) {
+                                               dylibLoadNotified = true;
+                                           }
+                                       },
+                                       ^{},
+                                       &kr);
+    LOG("8");
+    if ( handle == NULL ) {
+        LOG("_dyld_process_info_notify() returned NULL, result=%d", kr);
+    }
+    LOG("8.5");
+    void* h = dlopen(RUN_DIR "/libfoo.dylib", 0);
+    LOG("8.75");
+    dlclose(h);
+    if (!dylibLoadNotified) {
+        FAIL("testSelfAttach");
+    }
+    LOG("9");
+}
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+
+    // test 1) launch test program suspended in same arch as this program
+    launchTest(true, false);
+
+    // test 2) launch test program in same arch as this program where it sleeps itself
+    launchTest(false, false);
+//        validateMaxNotifies(child);
+
+    // test 3) launch test program where we disconnect from it after first dlopen
+    launchTest(true, true);
+//        monitor("disconnect", child, true, false);
+
+    // test 4) attempt to monitor the monitoring process
+//    testSelfAttach();
+    PASS("Success");
+
+}
index 55fd668c8aa39f9c6914ac5cecd2a8fced83d89a..dc80b0874cae843be0a3894c811e1d140064e003 100644 (file)
@@ -5,19 +5,40 @@
 #include <signal.h>
 #include <unistd.h>
 #include <mach/mach.h>
+#include <dispatch/dispatch.h>
 
-
-
-int main(int argc, const char* argv[])
-{
-    if ( (argc > 1) && (strcmp(argv[1], "suspend-in-main") == 0) )
-        (void)kill(getpid(), SIGSTOP);
-
+void performDylibOperations(void) {
     for (int i=0; i < 3; ++i) {
-        void* h = dlopen("./libfoo.dylib", 0);
+        void* h = dlopen(RUN_DIR "/libfoo.dylib", 0);
         dlclose(h);
     }
+    fprintf(stderr, "Done (pid: %d)\n", getpid());
+    exit(0);
+}
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    signal(SIGUSR1, SIG_IGN);
+    dispatch_source_t signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, getppid(),
+                                                            DISPATCH_PROC_EXIT, dispatch_get_main_queue());
+    dispatch_source_set_event_handler(signalSource, ^{
+        exit(0);
+    });
+    dispatch_resume(signalSource);
+
+    if ( (argc > 1) && (strcmp(argv[1], "suspend-in-main") == 0) ) {
+        dispatch_source_t signalSourceSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL,
+                                                                      SIGUSR1, 0, dispatch_get_main_queue());
+        dispatch_source_set_event_handler(signalSourceSource, ^{
+            performDylibOperations();
+        });
+        dispatch_resume(signalSourceSource);
+        dispatch_async(dispatch_get_main_queue(), ^{
+            fprintf(stderr, "Ready (pid: %d)\n", getpid());
+        });
+    } else {
+        performDylibOperations();
+    }
 
-    return 0;
+    dispatch_main();
 }
 
diff --git a/testing/test-cases/dyld_process_info_unload.dtest/main.c b/testing/test-cases/dyld_process_info_unload.dtest/main.c
deleted file mode 100644 (file)
index 3872e7d..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-
-// BUILD:  $CC target.c       -o $BUILD_DIR/target.exe
-// BUILD:  $CC foo.c          -o $BUILD_DIR/libfoo.dylib -dynamiclib -install_name $RUN_DIR/libfoo.dylib
-// BUILD:  $CC main.c         -o $BUILD_DIR/dyld_process_info_unload.exe
-// BUILD:  $TASK_FOR_PID_ENABLE  $BUILD_DIR/dyld_process_info_unload.exe
-
-// RUN:  $SUDO ./dyld_process_info_unload.exe $RUN_DIR/target.exe
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <spawn.h>
-#include <errno.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <mach-o/dyld_process_info.h>
-
-
-extern char** environ;
-
-#if __x86_64__
-    cpu_type_t otherArch[] = { CPU_TYPE_I386 };
-#elif __i386__
-    cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
-#elif __arm64__
-    cpu_type_t otherArch[] = { CPU_TYPE_ARM };
-#elif __arm__
-    cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
-#endif
-
-struct task_and_pid {
-    pid_t pid;
-    task_t task;
-};
-
-static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
-{
-    posix_spawnattr_t attr = 0;
-    if ( posix_spawnattr_init(&attr) != 0 ) {
-        printf("[FAIL] dyld_process_info_unload posix_spawnattr_init()\n");
-        exit(0);
-    }
-    if ( launchSuspended ) {
-        if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
-            printf("[FAIL] dyld_process_info_unload POSIX_SPAWN_START_SUSPENDED\n");
-            exit(0);
-        }
-    }
-    if ( launchOtherArch ) {
-        size_t copied;
-        if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) {
-           printf("[FAIL] dyld_process_info_unload posix_spawnattr_setbinpref_np()\n");
-            exit(0);
-        }
-    }
-
-    struct task_and_pid child;
-    const char* argv[] = { testProgPath, NULL };
-    int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ);
-    if ( psResult != 0 ) {
-        printf("[FAIL] dyld_process_info_unload posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
-        exit(0);
-    }
-    if (posix_spawnattr_destroy(&attr) != 0) {
-        printf("[FAIL] dyld_process_info_unload posix_spawnattr_destroy()\n");
-        exit(0);
-    }
-
-    if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
-        printf("[FAIL] dyld_process_info_unload task_for_pid()\n");
-        kill(child.pid, SIGKILL);
-        exit(0);
-    }
-
-    // wait until process is up and has suspended itself
-    struct task_basic_info info;
-    do {
-        unsigned count = TASK_BASIC_INFO_COUNT;
-        kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count);
-        sleep(1);
-    } while ( info.suspend_count == 0 );
-
-    return child;
-}
-
-static void killTest(struct task_and_pid tp) {
-    int r = kill(tp.pid, SIGKILL);
-    waitpid(tp.pid, &r, 0);
-}
-
-static bool alwaysGetImages(struct task_and_pid tp, bool launchedSuspended)
-{
-    int failCount = 0;
-    for (int i=0; i < 100; ++i ) {
-        kern_return_t result;
-        dyld_process_info info = _dyld_process_info_create(tp.task, 0, &result);
-        //fprintf(stderr, "info=%p, result=%08X\n", info, result);
-        if ( i == 0 )
-            (void)kill(tp.pid, SIGCONT);
-        if ( info == NULL ) {
-            failCount++;
-            //fprintf(stderr, "info=%p, result=%08X\n", info, result);
-        }
-        else {
-            usleep(100);
-            _dyld_process_info_release(info);
-        }
-    }
-    // ideally the fail count would be zero.  But the target is dlopen/dlclosing in a tight loop, so there may never be a stable set of images.
-    // The real bug driving this test case was _dyld_process_info_create() crashing when the the image list changed too fast.
-    // The important thing is to not crash.  Getting NULL back is ok.
-    return true;
-}
-
-
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] dyld_process_info_unload\n");
-
-    if ( argc < 2 ) {
-        printf("[FAIL] dyld_process_info_unload missing argument\n");
-        exit(0);
-    }
-    const char* testProgPath = argv[1];
-    struct task_and_pid child;
-
-    // launch test program suspended
-    child = launchTest(testProgPath, false, true);
-    if ( ! alwaysGetImages(child, true) ) {
-        killTest(child);
-        exit(0);
-    }
-    killTest(child);
-
-    printf("[PASS] dyld_process_info_unload\n");
-       return 0;
-}
diff --git a/testing/test-cases/dyld_process_info_unload.dtest/main.cpp b/testing/test-cases/dyld_process_info_unload.dtest/main.cpp
new file mode 100644 (file)
index 0000000..961ed8a
--- /dev/null
@@ -0,0 +1,60 @@
+
+// BUILD:  $CC target.c       -o $BUILD_DIR/target.exe
+// BUILD:  $CC foo.c          -o $BUILD_DIR/libfoo.dylib -dynamiclib -install_name $RUN_DIR/libfoo.dylib
+// BUILD:  $CXX main.cpp         -o $BUILD_DIR/dyld_process_info_unload.exe -DRUN_DIR="$RUN_DIR"
+// BUILD:  $TASK_FOR_PID_ENABLE  $BUILD_DIR/dyld_process_info_unload.exe
+
+// RUN:  $SUDO ./dyld_process_info_unload.exe $RUN_DIR/target.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/dyld_process_info.h>
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    _process process;
+    process.set_executable_path(RUN_DIR "/target.exe");
+    process.set_launch_suspended(true);
+    process.set_launch_async(true);
+    const char* env[] = { "TEST_OUTPUT=None", NULL};
+    process.set_env(env);
+    pid_t pid = process.launch();
+    task_t task;
+    if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS) {
+        FAIL("task_for_pid() failed");
+    }
+
+    dispatch_async(dispatch_get_main_queue(), ^{
+        int failCount = 0;
+        for (int i=0; i < 100; ++i ) {
+            kern_return_t result;
+            dyld_process_info info = _dyld_process_info_create(task, 0, &result);
+            LOG("info=%p, result=%08X", info, result);
+            if ( i == 0 )
+                (void)kill(pid, SIGCONT);
+            if ( info == NULL ) {
+                //FIXME: Compact info will never fail, make this a FAIL()
+                failCount++;
+                // ideally the fail count would be zero.  But the target is dlopen/dlclosing in a tight loop, so there may never be a stable set of images.
+                // The real bug driving this test case was _dyld_process_info_create() crashing when the the image list changed too fast.
+                // The important thing is to not crash.  Getting NULL back is ok.
+                LOG("info=%p, result=%08X", info, result);
+            }
+            else {
+                usleep(100);
+                _dyld_process_info_release(info);
+            }
+        }
+        PASS("Success");
+    });
+
+    dispatch_main();
+}
index be923e7fec1a84941675998f70b95cb95caba752..b70c2681e4adcace0118e46497d221870ad98c84 100644 (file)
@@ -3,22 +3,31 @@
 #include <string.h>
 #include <dlfcn.h>
 #include <unistd.h>
-#include <mach/mach.h>
+#include <dispatch/dispatch.h>
 
+#include "test_support.h"
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    signal(SIGUSR1, SIG_IGN);
+    dispatch_source_t signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, getppid(),
+                                                            DISPATCH_PROC_EXIT, dispatch_get_main_queue());
+    dispatch_source_set_event_handler(signalSource, ^{
+        exit(0);
+    });
+    dispatch_resume(signalSource);
 
-int main(int argc, const char* argv[])
-{
-    //fprintf(stderr, "target starting\n");
-    usleep(1000);
-    // load and unload in a loop
-    for (int i=1; i < 10000; ++i) {
-        void* h = dlopen("./libfoo.dylib", 0);
-        usleep(100000/(i*100));
-        dlclose(h);
-    }
-    //fprintf(stderr, "target done\n");
+    dispatch_async(dispatch_get_main_queue(), ^{
+            LOG("target starting");
+            usleep(1000);
+            // load and unload in a loop
+            for (int i=1; i < 10000; ++i) {
+                void* h = dlopen("./libfoo.dylib", 0);
+                usleep(100000/(i*100));
+                dlclose(h);
+            }
+            LOG("target done");
+    });
 
-    return 0;
+    dispatch_main();
 }
 
index 827e8934a3c500241d12a0a8d597fbc7c525e383..06abd8a141e273c043cd727f98b32afd8c1473f7 100644 (file)
@@ -1,6 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/override
-// BUILD:  mkdir -p $BUILD_DIR/re-export-override
 // BUILD:  $CC myzlib.c -dynamiclib -o $BUILD_DIR/override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0
 // BUILD:  $CC main.c  -o $BUILD_DIR/dyld_shared_cache_some_image_overridden.exe -lz
 // BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dyld_shared_cache_some_image_overridden.exe
 #include <dlfcn.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.
 // We then ensure that dyld_shared_cache_some_image_overridden returns the correct value to match whether we took a root
 
 extern const char* zlibVersion();
 
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // If we aren't using a shared cache, eg, have DYLD_SHARED_REGION=avoid, then just assume we work
     uuid_t currentCacheUUID;
     if ( !_dyld_get_shared_cache_uuid(currentCacheUUID) ) {
-        printf("[BEGIN] dyld_shared_cache_some_image_overridden\n");
         if (dyld_shared_cache_some_image_overridden())
-            printf("[FAIL] dyld_shared_cache_some_image_overridden\n");
+            FAIL("Overriden but no shared cache ");
         else
-            printf("[PASS] dyld_shared_cache_some_image_overridden\n");
-        return 0;
+            PASS("No shared cache");
     }
 
 #if NO_LZ
-       // This run doesn't link lz so instead dlopen's it
+    // This run doesn't link lz so instead dlopen's it
     bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL);
 
-    printf("[BEGIN] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os");
-
     void* handle = dlopen("/usr/lib/libz.1.dylib", RTLD_NOLOAD);
     if ( handle != NULL ) {
         // Uh oh.  Someone else has started linking libz so we can't use it as our root any more
-        printf("[FAIL] dyld_shared_cache_some_image_overridden, libz is hard linked now.  Update test to use a new dylib\n");
-        return 0;
+        FAIL("libz is hard linked now.  Update test to use a new dylib");
     }
 
     bool launchedWithOverriddenBinary = dyld_shared_cache_some_image_overridden();
@@ -57,76 +51,62 @@ int main()
     // Now dlopen libz
     handle = dlopen("/usr/lib/libz.1.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  dyld_shared_cache_some_image_overridden: /usr/lib/libz.1.dylib could not be loaded, %s\n", dlerror());
-        return 0;
+        FAIL("/usr/lib/libz.1.dylib could not be loaded, %s", dlerror());
     }
 
     // verify handle has the version symbol
     __typeof(&zlibVersion) versionSymbol = (__typeof(&zlibVersion))dlsym(handle, "zlibVersion");
     if ( versionSymbol == NULL ) {
-        printf("[FAIL]  dyld_shared_cache_some_image_overridden: zlibVersion was not found\n");
-        return 0;
+        FAIL("zlibVersion was not found");
     }
 
     bool usingMyDylib = (strcmp(versionSymbol(), "my") == 0);
 
-       if ( usingMyDylib != expectMyDylib ) {
-               // Not using the right dylib
-        printf("[FAIL] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os");
-        return 0;
+    if ( usingMyDylib != expectMyDylib ) {
+        // Not using the right dylib
+        FAIL("%s", expectMyDylib ? "my" : "os");
     }
 
     // Using the right dylib, so now see if we returned the correct value for dyld_shared_cache_some_image_overridden
     if (usingMyDylib) {
-       if (!dyld_shared_cache_some_image_overridden()) {
-               printf("[FAIL] dyld_shared_cache_some_image_overridden, my dylib but not some dylib overridden\n");
-               return 0;
-       }
+        if (!dyld_shared_cache_some_image_overridden()) {
+            FAIL("My dylib but not some dylib overridden");
+        }
     } else if (!launchedWithOverriddenBinary) {
         // We didn't have a root when we launched, so now we can make sure we do have a root after the dlopen
         // Assume we aren't testing against a root of libz in the system itself...
         if (dyld_shared_cache_some_image_overridden()) {
-            printf("[FAIL] dyld_shared_cache_some_image_overridden, system dylib was overridden\n");
-            return 0;
+            FAIL("System dylib was overridden");
         }
     } else {
-       // We can't actually be sure of the result here.  There may be other roots on the system so call the API to
-       // make sure it doesn't crash, but don't actually check it.
+        // We can't actually be sure of the result here.  There may be other roots on the system so call the API to
+        // make sure it doesn't crash, but don't actually check it.
         dyld_shared_cache_some_image_overridden();
     }
-    
-
-    printf("[PASS] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os");
-
 #else
     // This run links libz directly
     bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL);
 
-    printf("[BEGIN] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os");
-
     bool usingMyDylib = (strcmp(zlibVersion(), "my") == 0);
 
-       if ( usingMyDylib != expectMyDylib ) {
-               // Not using the right dylib
-        printf("[FAIL] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os");
-        return 0;
+    if ( usingMyDylib != expectMyDylib ) {
+        // Not using the right dylib
+        FAIL("%s", expectMyDylib ? "my" : "os");
     }
 
     // Using the right dylib, so now see if we returned the correct value for dyld_shared_cache_some_image_overridden
     if (usingMyDylib) {
-       if (!dyld_shared_cache_some_image_overridden()) {
-               printf("[FAIL] dyld_shared_cache_some_image_overridden, my dylib but not some dylib overridden\n");
-               return 0;
-       }
+        if (!dyld_shared_cache_some_image_overridden()) {
+            FAIL("My dylib but not some dylib overridden");
+        }
     } else {
-       // We can't actually be sure of the result here.  There may be other roots on the system so call the API to
-       // make sure it doesn't crash, but don't actually check it.
+        // We can't actually be sure of the result here.  There may be other roots on the system so call the API to
+        // make sure it doesn't crash, but don't actually check it.
         dyld_shared_cache_some_image_overridden();
     }
-    
-    printf("[PASS] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os");
 #endif
+    PASS("%s", expectMyDylib ? "my" : "os");
 
-       return 0;
+    return 0;
 }
 
index f5c77ce44c3a7dfc8f4d2f89e23238da48f16942..3e738c9db6871bc447b872c3896482f5d410f21c 100644 (file)
@@ -7,11 +7,11 @@
 #include <string.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
+
 extern struct mach_header __dso_handle;
 
-int main()
-{
-    printf("[BEGIN] dyld_version_spi\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     dyld_platform_t active = dyld_get_active_platform();
     dyld_platform_t base = dyld_get_base_platform(active);
     dyld_build_version_t absoluteMin = { .platform = base, .version = 0 };
@@ -19,72 +19,58 @@ int main()
 
 #if TARGET_OS_OSX
     if ( base != PLATFORM_MACOS ) {
-        printf("[FAIL] base.platform %u incorrect for macOS\n", base);
-        return 0;
+        FAIL("base.platform %u incorrect for macOS", base);
     }
 #elif TARGET_OS_IOS
     if ( base != PLATFORM_IOS ) {
-        printf("[FAIL] base.platform %u incorrect for iOS\n", base);
-        return 0;
+        FAIL("base.platform %u incorrect for iOS", base);
     }
 #elif TARGET_OS_TV
     if ( base != PLATFORM_TVOS ) {
-        printf("[FAIL] base.platform %u incorrect for tvOS\n", base);
-        return 0;
+        FAIL("base.platform %u incorrect for tvOS", base);
     }
 #elif TARGET_OS_BRIDGE
     if ( base != PLATFORM_BRIDGEOS ) {
-        printf("[FAIL] base.platform %u incorrect for wacthOS\n", base);
-        return 0;
+        FAIL("base.platform %u incorrect for wacthOS", base);
     }
 #elif TARGET_OS_WATCH
     if ( base != PLATFORM_WATCHOS ) {
-        printf("[FAIL] base.platform %u incorrect for bridgeOS\n", base);
-        return 0;
+        FAIL("base.platform %u incorrect for bridgeOn", base);
     }
 #else
-    printf("[FAIL] Running on unknown platform\n");
-    return 0;
+    FAIL("Running on unknown platform");
 #endif
 
 #if TARGET_OS_SIMULATOR
     if (dyld_is_simulator_platform(active) != true) {
-        printf("[FAIL] active platform %u should be a simulator\n", active);
-        return 0;
+        FAIL("active platform %u should be a simulator", active);
     }
 #else
     if (dyld_is_simulator_platform(active) == true) {
-        printf("[FAIL] active platform %u should not be a simulator\n", active);
-        return 0;
+        FAIL("active platform %u should not be a simulator", active);
     }
 #endif
 
     if (dyld_is_simulator_platform(base) == true) {
-        printf("[FAIL] base platform %u should not be a simulator\n", base);
-        return 0;
+        FAIL("base platform %u should not be a simulator", base);
     }
 
     if (!dyld_sdk_at_least(&__dso_handle, absoluteMin)) {
-        printf("[FAIL] executable sdk version should not < 1.0.0\n");
-        return 0;
+        FAIL("executable sdk version should not < 1.0.0");
     }
 
     if (dyld_sdk_at_least(&__dso_handle, absoluteMax)) {
-        printf("[FAIL] executable sdk version should not > 65536.0.0\n");
-        return 0;
+        FAIL("executable sdk version should not > 65536.0.0");
     }
 
     if (!dyld_minos_at_least(&__dso_handle, absoluteMin)) {
-        printf("[FAIL] executable min version should not < 1.0.0\n");
-        return 0;
+        FAIL("executable min version should not < 1.0.0");
     }
 
     if (dyld_minos_at_least(&__dso_handle, absoluteMax)) {
-        printf("[FAIL] executable min version should not > 65536.0.0\n");
-        return 0;
+        FAIL("executable min version should not > 65536.0.0");
     }
 
-    printf("[PASS] dyld_version_spi\n");
-    return 0;
+    PASS("Success");
 }
 
index ea4d7f04141922eea5bb582217a3ffab41d48ec6..80ce72f22075a8d17e8ab963c0e107aa5dd48f23 100644 (file)
@@ -1,26 +1,24 @@
 // BUILD_ONLY:      MacOSX
 // BUILD_MIN_OS:    10.5
-// BUILD:           $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib -nostdlib -ldylib1.o
-// BUILD:           $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -nostdlib -ldylib1.o
-// BUILD:           $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR -nostdlib -lSystem -lcrt1.10.5.o
+// 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
 
 // RUN:  ./dylib-re-export.exe
 
 
 #include <stdio.h>
 
+#include "test_support.h"
+
 extern int bar();
 
 
-int main()
-{
-    printf("[BEGIN] dylib-re-export-old-format\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if ( bar() == 42 )
-        printf("[PASS] dylib-re-export-old-format\n");
+        PASS("Success");
     else
-        printf("[FAIL] dylib-re-export-old-format, wrong value\n");
-
-       return 0;
+        FAIL("Wrong value");
 }
 
 
index 879b0b611c7b32a2f9f757738dbd343dd7b864e5..3484893d9d740edf1306f38e543233877aefaaac 100644 (file)
@@ -9,16 +9,16 @@
 
 #include <stdio.h>
 
+#include "test_support.h"
+
 extern int bar();
 
 
-int main()
-{
-    printf("[BEGIN] dylib-re-export\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if ( bar() == 42 )
-        printf("[PASS] dylib-re-export\n");
+        PASS("Success");
     else
-        printf("[FAIL] dylib-re-export, wrong value\n");
+        FAIL("Wrong value");
 
        return 0;
 }
diff --git a/testing/test-cases/dylib-static-link.dtest/main.c b/testing/test-cases/dylib-static-link.dtest/main.c
new file mode 100644 (file)
index 0000000..6dd84b6
--- /dev/null
@@ -0,0 +1,23 @@
+
+
+// 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/dylib-static-link.exe
+
+// RUN:  ./dylib-static-link.exe
+
+
+#include <stdio.h>
+
+#include "test_support.h"
+
+extern int foo;
+
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    if ( foo == 42 )
+        PASS("Success");
+    else
+        FAIL("Wrong value");
+}
+
+
diff --git a/testing/test-cases/dylib-static-link.dtest/missing.c b/testing/test-cases/dylib-static-link.dtest/missing.c
deleted file mode 100644 (file)
index 6a4a62d..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#include <stdio.h>
-#include <signal.h>
-
-int main()
-{
-    printf("[BEGIN] dylib-static-link missing\n");
-    printf("[FAIL] dylib-static-link missing, program should not have launched\n");
-
-       return 0;
-}
-
-
diff --git a/testing/test-cases/dylib-static-link.dtest/present.c b/testing/test-cases/dylib-static-link.dtest/present.c
deleted file mode 100644 (file)
index 2eb3393..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
-// BUILD:  $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-present.exe
-// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib
-// BUILD:  $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-missing.exe
-
-// RUN:  ./dylib-static-present.exe
-// RUN:  NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe
-
-
-#include <stdio.h>
-
-extern int foo;
-
-
-int main()
-{
-    printf("[BEGIN] dylib-static-link present\n");
-    if ( foo == 42 )
-        printf("[PASS] dylib-static-link present\n");
-    else
-        printf("[FAIL] dylib-static-link present, wrong value\n");
-
-       return 0;
-}
-
-
index c6330478e1fd4d3cd4e7222438247ee5e2f122f9..af32304b57f0f4d202f28ae6bb9c6fdea5a173fd 100644 (file)
@@ -1,19 +1,14 @@
-#include <stddef.h>
-#include <stdio.h>
+#include "test_support.h"
 
 extern int foo __attribute__((weak_import));
 
 
-int main()
-{
-    printf("[BEGIN] dylib-static-weak-link missing\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // dylib won't be found at runtime, so &foo should be NULL
     if ( &foo == NULL )
-        printf("[PASS] dylib-static-weak-link missing\n");
+        PASS("Success");
     else
-        printf("[FAIL] dylib-static-weak-link missing, &foo != NULL\n");
-
-       return 0;
+        FAIL("&foo != NULL");
 }
 
 
index f51e384c80fcf8e541e32eeacfbc6f50ad6c4d81..6f1aa5ba35b672477635d30e20ff1a196f83edc1 100644 (file)
@@ -1,7 +1,9 @@
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
 // BUILD:  $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-weak-present.exe
-// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib
-// BUILD:  $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib
+// BUILD:  $CC missing.c $BUILD_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe
+
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libfoo2.dylib
 
 // RUN:  ./dylib-static-weak-present.exe
 // RUN:  ./dylib-static-weak-missing.exe
 #include <stddef.h>
 #include <stdio.h>
 
+#include "test_support.h"
+
 extern int foo __attribute__((weak_import));
 
 
-int main()
-{
-    printf("[BEGIN] dylib-static-weak-link present\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // dylib will be found at runtime, so &foo should never be NULL
     if ( &foo != NULL ) {
        if ( foo == 42 )
-            printf("[PASS] dylib-static-weak-link present\n");
+            PASS("Success");
         else
-            printf("[FAIL] dylib-static-weak-link present, wrong value\n");
+            FAIL("Wrong value");
     }
     else {
-        printf("[FAIL] dylib-static-weak-link present, &foo == NULL\n");
+        FAIL("&foo == NULL");
     }
-
-       return 0;
 }
 
 
index 6b547bb8915c0df82973ae6ae8ad9b8e3d0257c2..d3c94cf6e2353f6f1f304185b88d6ff82c7e8521 100644 (file)
@@ -1,25 +1,23 @@
 
-// BUILD:  mkdir -p $TEMP_DIR/Foo.framework $BUILD_DIR/FallbackFrameworks/Foo.framework
-// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/Foo.framework/Foo                     -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=1
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/Foo.framework/Foo                     -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=1
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/FallbackFrameworks/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=42
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_FALLBACK_FRAMEWORK_PATH.exe $TEMP_DIR/Foo.framework/Foo
+// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_FALLBACK_FRAMEWORK_PATH.exe $BUILD_DIR/Foo.framework/Foo
 // BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_FALLBACK_FRAMEWORK_PATH.exe
 
+// BUILD: $SKIP_INSTALL $BUILD_DIR/Foo.framework/Foo
+
 // RUN:  DYLD_FALLBACK_FRAMEWORK_PATH=$RUN_DIR/FallbackFrameworks/ ./env-DYLD_FALLBACK_FRAMEWORK_PATH.exe
 
 #include <stdio.h>
 #include <stdlib.h>
 
-extern int foo();
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
+extern int foo();
 
-       if ( foo() == 42 )
-        printf("[PASS] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    if ( foo() == 42 )
+        PASS("Success");
     else
-        printf("[FAIL] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
-
-       return 0;
+        FAIL("foo() was not 42");
 }
index 9972627fa84c6d903b12d2cf18585fbc30f75584..0422db7d02e141f0cabb5c026171739e6f18b314 100644 (file)
@@ -1,25 +1,23 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/fallback
-// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo.dylib           -install_name $RUN_DIR/libfoo.dylib -DVALUE=1
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib           -install_name $RUN_DIR/libfoo.dylib -DVALUE=1
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/fallback/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=42
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_FALLBACK_LIBRARY_PATH.exe $TEMP_DIR/libfoo.dylib
+// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_FALLBACK_LIBRARY_PATH.exe $BUILD_DIR/libfoo.dylib
 // BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_FALLBACK_LIBRARY_PATH.exe
 
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libfoo.dylib
+
 // RUN:  DYLD_FALLBACK_LIBRARY_PATH=$RUN_DIR/fallback/ ./env-DYLD_FALLBACK_LIBRARY_PATH.exe
 
 #include <stdio.h>
 
-extern int foo();
+#include "test_support.h"
 
-int main()
-{
-     printf("[BEGIN] env-DYLD_FALLBACK_LIBRARY_PATH\n");
+extern int foo();
 
-       if ( foo() == 42 )
-        printf("[PASS] env-DYLD_FALLBACK_LIBRARY_PATH\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    if ( foo() == 42 )
+        PASS("Success");
     else
-        printf("[FAIL] env-DYLD_FALLBACK_LIBRARY_PATH\n");
-
-       return 0;
+        FAIL("libfoo.dylib incorrectly used fallback");
 }
 
index 9fcf357cfee36b5e7c2788229c29205f4c99accd..7411281b7255e51af4308754ee0b236db7de0a07 100644 (file)
@@ -1,38 +1,42 @@
 
 // BUILD_ONLY: MacOSX
-// BUILD:  $CC main.c            -o $BUILD_DIR/DYLD_FORCE_PLATFORM.exe -DENABLE_ALT_PLATFORMS=1 -ldarwintest
-// BUILD:  $CC main.c            -o $BUILD_DIR/DYLD_FORCE_PLATFORM_FAIL.exe -ldarwintest
-// BUILD:  $TASK_FOR_PID_ENABLE  $BUILD_DIR/DYLD_FORCE_PLATFORM.exe
-// BUILD:  $TASK_FOR_PID_ENABLE  $BUILD_DIR/DYLD_FORCE_PLATFORM_FAIL.exe
+// 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
 
-// RUN:  ./DYLD_FORCE_PLATFORM.exe
-// RUN:  ./DYLD_FORCE_PLATFORM_FAIL.exe
+// RUN:  DYLD_FORCE_PLATFORM=6 ./env-DYLD_FORCE_PLATFORM.exe
+// RUN:  DYLD_FORCE_PLATFORM=6 ./env-DYLD_FORCE_PLATFORM-fail.exe
 
 #include <mach-o/dyld_priv.h>
-#include "dyld_test.h"
+#include <dlfcn.h>
+
+#include "test_support.h"
 
 #if ENABLE_ALT_PLATFORMS
 __attribute__((section("__DATA,__allow_alt_plat"))) uint64_t dummy;
-T_DECL_DYLD(DYLD_FORCE_PLATFORM, "Test that DYLD_FORCE_PLATFORM works correctly", T_META_ENVVAR("DYLD_FORCE_PLATFORM=6")) {
-    dyld_build_version_t ios12 = { .platform = PLATFORM_IOSMAC, .version = 0x000c0000 };
-    T_EXPECT_EQ_UINT(dyld_get_active_platform(), PLATFORM_IOSMAC, "dyld_get_active_platform() should return PLATFORM_IOSMAC");
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    dyld_build_version_t ios12 = { .platform = PLATFORM_IOS, .version = 0x000c0000 };
+    if (dyld_get_active_platform() != PLATFORM_MACCATALYST) { FAIL("dyld_get_active_platform() should return PLATFORM_MACCATALYST"); }
     // libswiftUIKit.dylib exists in /System/iOSSupport/usr/lib/swift
     // We should be able to dlopen it only if we are correctly prepending the /System/iOSSupport root path
-    T_EXPECT_TRUE(dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib"), "Should be able to dlopen libswiftUIKit but %s", dlerror());
 #if 0
-    //FIXME: This has to be disabled until we can fix rdar://47156760
-    T_EXPECT_TRUE(dyld_program_minos_at_least(ios12), "DYLD_FORCE_PLATFORM should synthesize an iOS min version greater than 12.0");
-    T_EXPECT_TRUE(dyld_program_sdk_at_least(ios12), "DYLD_FORCE_PLATFORM should synthesize an iOS sdk versio greater than 12.0");
+    // FIXME: We don't want to bring in such large dylib graphs. We can repurpose root testing support for this
+    if (!dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib")) { FAIL("Should be able to dlopen libswiftUIKit but %s", dlerror()); }
 #endif
+    if (!dyld_program_minos_at_least(ios12)) { FAIL("DYLD_FORCE_PLATFORM should synthesize an iOS min version greater than 12.0"); }
+    if (!dyld_program_sdk_at_least(ios12)) { FAIL("DYLD_FORCE_PLATFORM should synthesize an iOS sdk versio greater than 12.0"); }
+    PASS("Success");
 }
 #else
-T_DECL_DYLD(DYLD_FORCE_PLATFORM_FAIL, "Test that DYLD_FORCE_PLATFORM fails correctly", T_META_ENVVAR("DYLD_FORCE_PLATFORM=6")) {
-    T_EXPECT_EQ_UINT(dyld_get_active_platform(), PLATFORM_MACOS, "dyld_get_active_platform() should return PLATFORM_IOSMAC");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    if (dyld_get_active_platform() != PLATFORM_MACOS) { FAIL("dyld_get_active_platform() should return PLATFORM_MACOS"); }
 
     // libswiftUIKit.dylib exists in /System/iOSSupport/usr/lib/swift
     // We should not be able to dlopen this as we don't expect to find it in a macOS location.  If it starts
     // being in a macOS location then we should update this test
-    T_EXPECT_FALSE(dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib"), "Should not be able to dlopen libswiftUIKit");
+    if(dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib")) { FAIL("Should not be able to dlopen libswiftUIKit"); }
+    PASS("Success");
 }
 #endif
index 8a2b4f659456f603223a7a9ff1bd0f8fe2c29b87..c1da4eb31d31dda032c44937604bf3ebe8360125 100644 (file)
@@ -1,5 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/Frameworks/Foo.framework $BUILD_DIR/Frameworks-alt/Foo.framework
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks/Foo.framework/Foo     -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=1
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks-alt/Foo.framework/Foo -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=42
 // BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_FRAMEWORK_PATH.exe $BUILD_DIR/Frameworks/Foo.framework/Foo
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "test_support.h"
+
 extern int foo();
 
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     int expected = (getenv("DYLD_FRAMEWORK_PATH") != NULL) ? 42 : 1;
 
-    printf("[BEGIN] env-DYLD_FRAMEWORK_PATH, expect %d\n", expected);
-
        if ( foo() == expected )
-        printf("[PASS] env-DYLD_FRAMEWORK_PATH\n");
+        PASS("Success");
     else
-        printf("[FAIL] env-DYLD_FRAMEWORK_PATH\n");
-
-       return 0;
+        FAIL("Incorrect libfoo.dylib loaded");
 }
 
index 49101dece3c1708044274d05397f80786ddf142b..5b4f4f87d049abc3575d6f7ef9aa1f1bab625b1b 100644 (file)
@@ -1,5 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/Bar.framework
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib            -install_name $RUN_DIR/libfoo.dylib -DVALUE=1
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo_other.dylib      -install_name $RUN_DIR/libfoo.dylib -DVALUE=42
 // BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/Bar.framework/Bar       -install_name $RUN_DIR/Bar.framework/Bar  -DVALUE=1
 #include <string.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
+
 extern int foo();
 extern int bar();
 
 typedef int (*IntProc)();
 
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     const char* suffix = getenv("DYLD_IMAGE_SUFFIX");
     if ( suffix == NULL )
         suffix = "";
-    printf("[BEGIN] env-DYLD_IMAGE_SUFFIX-%s\n", suffix);
 
     const int expectedFoo = (strstr(suffix, "_other") != NULL) ? 42 : 1;
     const int expectedBar = (strstr(suffix, "_alt") != NULL) ? 42 : 1;;
@@ -44,23 +43,19 @@ int main()
 #ifdef RUN_DIR
     void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
     if ( fooHandle == NULL ) {
-        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, libfoo.dylib could not be loaded, %s\n", suffix, dlerror());
-        return 0;
+        FAIL("libfoo.dylib could not be loaded, %s", dlerror());
     }
     void* barHandle = dlopen(RUN_DIR "/Bar.framework/Bar", RTLD_LAZY);
     if ( barHandle == NULL ) {
-        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, Bar.framework/Bar could not be loaded, %s\n", suffix, dlerror());
-        return 0;
+        FAIL("Bar.framework/Bar could not be loaded, %s", dlerror());
     }
     IntProc fooProc = (IntProc)dlsym(fooHandle, "foo");
     if ( fooProc == NULL ) {
-        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, symbol 'foo' not found %s\n", suffix, dlerror());
-        return 0;
+        FAIL("symbol 'foo' not found %s", dlerror());
     }
     IntProc barProc = (IntProc)dlsym(barHandle, "bar");
     if ( barProc == NULL ) {
-        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, symbol 'bar' not found %s\n", suffix, dlerror());
-        return 0;
+        FAIL("symbol 'bar' not found %s", dlerror());
     }
     int fooValue = (*fooProc)();
     int barValue = (*barProc)();
@@ -69,12 +64,10 @@ int main()
     int barValue = bar();
 #endif
        if ( fooValue != expectedFoo )
-        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, foo()=%d expected=%d\n", suffix, fooValue, expectedFoo);
+        FAIL("foo()=%d expected=%d", fooValue, expectedFoo);
     else if ( barValue != expectedBar )
-        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, bar()=%d expected=%d\n", suffix, barValue, expectedBar);
+        FAIL("bar()=%d expected=%d", barValue, expectedBar);
     else
-        printf("[PASS]  env-DYLD_IMAGE_SUFFIX-%s\n", suffix);
-
-       return 0;
+        PASS("Success");
 }
 
index bcdc22fd1233b6f4cc7e42d7d4f17c09c4540db5..3062bea8e6781f7ac511e935c918a8b229681736 100644 (file)
@@ -1,6 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/override
-// BUILD:  mkdir -p $BUILD_DIR/re-export-override
 // BUILD:  $CC myzlib.c -dynamiclib -o $BUILD_DIR/override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -framework CoreFoundation
 // BUILD:  $CC 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
 // BUILD:  $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
 #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 main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL) && !_dyld_shared_cache_optimized();
 
-    printf("[BEGIN] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
-
     bool usingMyDylib = (strcmp(zlibVersion(), "my") == 0);
 
        if ( usingMyDylib == expectMyDylib )
-        printf("[PASS] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
+        PASS("Succes");
     else
-        printf("[FAIL] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
-
-       return 0;
+        FAIL("Expected %s, got %s", expectMyDylib ? "my" : "os", expectMyDylib ? "os" : "my");
 }
 
index 9402412a4e8e66c9ca77951238acbbd18431af14..6683eb585d4b199d3611afffd5e940be982fb31e 100644 (file)
@@ -1,5 +1,4 @@
 
-// BUILD:  mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/door1/libfoo.dylib -DVALUE=1
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/door2/libfoo.dylib -install_name $RUN_DIR/door2/libfoo.dylib -DVALUE=42
 // BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_LIBRARY_PATH.exe $BUILD_DIR/door1/libfoo.dylib
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "test_support.h"
+
 extern int foo();
 
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     int expected = (getenv("DYLD_LIBRARY_PATH") != NULL) ? 42 : 1;
 
-    printf("[BEGIN] env-DYLD_LIBRARY_PATH, expect %d\n", expected);
-
        if ( foo() == expected )
-        printf("[PASS] env-DYLD_LIBRARY_PATH\n");
+        PASS("Success");
     else
-        printf("[FAIL] env-DYLD_LIBRARY_PATH\n");
-
-       return 0;
+        FAIL("Wrong dylib loaded");
 }
 
index 2fbce3adaeb1bad3783859c96d1450f0d5fafa94..3010022f33eb2224ee6192bdb8cfa62a4d14963c 100644 (file)
@@ -1,8 +1,6 @@
 
 // BUILD_ONLY: MacOSX
-// BUILD:  mkdir -p $BUILD_DIR/Foo.framework $BUILD_DIR/alt11/Foo.framework/Versions/A $BUILD_DIR/alt9/Foo.framework
-// BUILD:  mkdir -p $BUILD_DIR/alt12/Foo.framework $BUILD_DIR/Bar.framework $BUILD_DIR/alt11/Bar.framework/Versions/A
-// BUILD:  mkdir -p $BUILD_DIR/Foo2.framework $BUILD_DIR/alt12/Foo2.framework
+
 // 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
@@ -14,8 +12,7 @@
 // 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:  cd $BUILD_DIR/alt11/Foo.framework && ln -sf Versions/A/Foo
-// BUILD:  rm -rf $BUILD_DIR/Foo2.framework
+// 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
 
 // RUN: ./env-DYLD_VERSIONED_FRAMEWORK_PATH.exe 10
 #include <string.h>
 #include <stdlib.h> // for atoi()
 
+#include <mach-o/dyld_priv.h>
+
 #include "test_support.h"
 
 extern int foo();
 
 int main(int argc, const char* argv[])
 {
-       if ( argc > 2 ) {
-               bool found = false;
-               uint32_t count = _dyld_image_count();
-               for(uint32_t i=0; i < count; ++i) {
-                       const char*  name = _dyld_get_image_name(i);
+    if ( argc > 2 ) {
+        bool found = false;
+        uint32_t count = _dyld_image_count();
+        for(uint32_t i=0; i < count; ++i) {
+            const char*  name = _dyld_get_image_name(i);
             if ( strstr(name, argv[2]) != NULL ) {
-                               found = true;
+                found = true;
             }
-               }
-               if ( !found ) {
-                       FAIL("Dylib has wrong path");
-                       return EXIT_SUCCESS;
-               }
-       }
+        }
+        if ( !found ) {
+            FAIL("Dylib has wrong path");
+            return EXIT_SUCCESS;
+        }
+    }
 
-       int expectedResult = atoi(argv[1]);
-       int actualResult = foo();
+    int expectedResult = atoi(argv[1]);
+    int actualResult = foo();
     if ( actualResult != expectedResult ) {
-               FAIL("Using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
+        FAIL("Using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
     } else {
-               PASS("Success");
+        PASS("Success");
     }
-       return 0;
+    return 0;
 }
 
index b0a7e6495309490d1ddd441d8b8282ac6aaa9513..2554358b94351d77560026afc464f88768071ed5 100644 (file)
@@ -1,5 +1,5 @@
 // BUILD_ONLY: MacOSX
-// BUILD:  mkdir -p $BUILD_DIR/alt11 $BUILD_DIR/alt9 $BUILD_DIR/alt12
+
 // 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
@@ -22,7 +22,7 @@
 // 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:  rm -rf $BUILD_DIR/libfoo2.dylib
+// BUILD:  $SKIP_INSTALL $BUILD_DIR/libfoo2.dylib
 
 // 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
 // FIXME: Forcibly disable testing with closures since macOS does not use them and they are currently broken
 // RUN: DYLD_USE_CLOSURES=0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing.exe 12
 
-// RUN: DYLD_USE_CLOSURES=0 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 10
-// RUN: DYLD_USE_CLOSURES=0 DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt11 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 11
-// RUN: DYLD_USE_CLOSURES=0 DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt9 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 10
-// RUN: DYLD_USE_CLOSURES=0 DYLD_VERSIONED_LIBRARY_PATH="/AppleInternal/CoreOS/tests/dyld/env-DYLD_VERSIONED_LIBRARY_PATH/alt12" ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dlopen.exe 12
+// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 10
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt11 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 11
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt9 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 10
+// RUN: DYLD_VERSIONED_LIBRARY_PATH="/AppleInternal/CoreOS/tests/dyld/env-DYLD_VERSIONED_LIBRARY_PATH/alt12" ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dlopen.exe 12
 
 #include <stdio.h>  // fprintf(), NULL
 #include <stdlib.h> // exit(), EXIT_SUCCESS
@@ -66,7 +66,7 @@ extern int foo();
 
 int main(int argc, const char* argv[])
 {
-    int expectedResult = atoi(argv[1]);
+       int expectedResult = atoi(argv[1]);
 #if USE_DLOPEN
     void * handle = dlopen(RUN_DIR "/" DYLIB_NAME, RTLD_LAZY);
     if (!handle) {
@@ -77,11 +77,12 @@ int main(int argc, const char* argv[])
         FAIL("dlsym(\"foo\") failed with error \"%s\"", dlerror());
     }
 #endif
-    int actualResult = foo();
+       int actualResult = foo();
     if ( actualResult != expectedResult ) {
-        FAIL("Using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
+               FAIL("Using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
     } else {
-        PASS("Success");
+               PASS("Success");
     }
-    return 0;
+       return 0;
 }
+
index 6fcd005151853b462df73d73d7fcb55ea54d7e29..87670c438b74b7ee68de86029f491387811c65e8 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include "test_support.h"
+
 extern int myAbs1;
 int* ptr = &myAbs1;
 
-int main()
-{
-    printf("[BEGIN] flat-namespace-absolute-symbol\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if ( ptr != 0 ) {
-        printf("[FAIL] absolute symbol not bound to zero with flat lookup\n");
-        return 0;
+        FAIL("Absolute symbol not bound to zero with flat lookup");
     }
 
-    printf("[PASS] flat-namespace-absolute-symbol\n");
-       return 0;
+    PASS("Success");
 }
index 37c99fc6999e373e377a9945152be1fa03339292..306bd7e7ee5c7125066f02d057ee01856e61c0b5 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
-int main()
-{
-    printf("[BEGIN] flat-namespace\n");
+#include "test_support.h"
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // check that the malloc in libfoo.dylib was used by looking at the content the allocated buffer
     // <rdar://problem/31921090> strncmp is tricky for flat namespace because it is re-exporte and renamed
     char* p1 = malloc(10);
     if ( strncmp(p1, "##########", 10) != 0 ) {
-        printf("[FAIL] malloc() from main executable not interposed\n");
-        return 0;
+        FAIL("malloc() from main executable not interposed");
     }
 
-    printf("[PASS] flat-namespace\n");
-       return 0;
+    PASS("Success");
 }
index b96c6983825c23cbb730423ab7b6581c663a7ba5..a592be5bd398e8907bf4d57324f0aedc9ac8a5ad 100644 (file)
@@ -11,6 +11,8 @@
 #include <mach-o/dyld_images.h>
 #include <uuid/uuid.h>
 
+#include "test_support.h"
+
 extern const struct mach_header __dso_handle;
 
 static void printUUIDs(const struct dyld_all_image_infos* infos)
@@ -20,35 +22,29 @@ static void printUUIDs(const struct dyld_all_image_infos* infos)
             const struct dyld_uuid_info* nonCacheInfo = &infos->uuidArray[i];
             uuid_string_t uuidStr;
             uuid_unparse_upper(nonCacheInfo->imageUUID, uuidStr);
-            printf("%p %s\n", nonCacheInfo->imageLoadAddress, uuidStr);
+            LOG("%p %s", nonCacheInfo->imageLoadAddress, uuidStr);
           }
     }
 }
 
-int main()
-{
-    printf("[BEGIN] image_infos-uuids\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // NOTE: dyld_all_image_infos is private, but currently looked at by kernel during stackshots
     // This test is to validate that the data available to the kernel is correct
 
     task_dyld_info_data_t task_dyld_info;
     mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
     if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) {
-        printf("[FAIL]  image_infos-uuids: task_info() failed\n");
-        return 0;
+        FAIL("task_info() failed");
     }
     const struct dyld_all_image_infos* infos = (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr;
 
 
     if ( infos->uuidArray == NULL ) {
-        printf("[FAIL]  infos->uuidArray == NULL\n");
-        return 0;
+        FAIL("infos->uuidArray == NULL");
     }
     if ( infos->uuidArrayCount < 2 ) {
         // expect to contain main executable and dyld
-        printf("[FAIL]  infos->uuidArrayCount != 2 (is %lu)\n", infos->uuidArrayCount);
-        return 0;
+        FAIL("infos->uuidArrayCount != 2 (is %lu)", infos->uuidArrayCount);
     }
     printUUIDs(infos);
     uint32_t initialCount = infos->uuidArrayCount;
@@ -63,30 +59,25 @@ int main()
             foundDyld = true;
     }
     if ( !foundMain ) {
-        printf("[FAIL]  image_infos-uuids uuid array does not contain main program\n");
-        return 0;
+        FAIL("image_infos-uuids uuid array does not contain main program");
     }
     if ( !foundDyld ) {
-        printf("[FAIL]  image_infos-uuids uuid array does not contain dyld\n");
-        return 0;
+        FAIL("image_infos-uuids uuid array does not contain dyld");
     }
 
     void* handle = dlopen(RUN_DIR "/test.bundle", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  image_infos-uuids %s\n", dlerror());
-        return 0;
+        FAIL("image_infos-uuids %s", dlerror());
     }
-    printf("loaded test.bundle\n");
+    LOG("loaded test.bundle");
 
     // now expect UUID list to be three
     if ( infos->uuidArrayCount != initialCount+1 ) {
         // expect to contain main executable and dyld
-        printf("[FAIL]  infos->uuidArrayCount was not incremented (is %lu)\n", infos->uuidArrayCount);
-        return 0;
+        FAIL("infos->uuidArrayCount was not incremented (is %lu)", infos->uuidArrayCount);
     }
     printUUIDs(infos);
 
-    printf("[PASS]  image_infos-uuids\n");
-       return 0;
+    PASS("Success");
 }
 
index e91a2cdb455c41b16df8586213a1cb10193eeaff..f0ef7b813fd4d2cec667cb805d63bd0c349d43c5 100644 (file)
@@ -9,42 +9,35 @@
 #include <dlfcn.h>
 #include <stdlib.h>
 
-extern bool foo(bool* ptr);
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] init-term-segments\n");
+extern bool foo(bool* ptr);
 
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     void* h = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOW);
     if (h == NULL) {
-               printf("[FAIL]  init-term-segments - dlerror = %s\n", dlerror());
-               return 0;
+               FAIL("dlerror = %s", dlerror());
     }
 
     void* fooSym = dlsym(RTLD_DEFAULT, "foo");
     if ( fooSym == NULL ) {
-               printf("[FAIL]  init-term-segments - dlsym failure\n");
-               return 0;
+        FAIL("dlsym failure");
     }
 
     bool ranTerm = false;
     bool ranInit = ((__typeof(&foo))fooSym)(&ranTerm);
     if (!ranInit) {
-               printf("[FAIL]  init-term-segments - didn't run init\n");
-               return 0;
+        FAIL("didn't run init");
     }
 
     if ( dlclose(h) != 0 ) {
-               printf("[FAIL]  init-term-segments - didn't dlclose\n");
-               return 0;
+        FAIL("didn't dlclose");
     }
 
     if (!ranTerm) {
-               printf("[FAIL]  init-term-segments - didn't run term\n");
-               return 0;
+        FAIL("didn't run term");
     }
 
-    printf("[PASS]  init-term-segments\n");
-       return 0;
+    PASS("Success");
 }
 
index 9ef1842b2a1c7e808a5be4ee2871d1c26570e2ed..5baba7d181449e2010951c078533aac715c562cc 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include "test_support.h"
+
 extern void* myalloc1(size_t);
 extern void* myalloc2(size_t);
 
-int main()
-{
-    printf("[BEGIN] interpose-malloc\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     char* p1 = malloc(10);
     if ( strncmp(p1+10, "##########", 10) != 0 ) {
-        printf("[FAIL] interpose-malloc malloc() from main executable not interposed\n");
-        return 0;
+        FAIL("malloc() from main executable not interposed");
     }
 
     void* p2 = myalloc1(6);
     if ( strncmp(p2+6, "######", 6) != 0 ) {
-        printf("[FAIL] interpose-malloc myalloc1() from libfoo.dylib not interposed\n");
-        return 0;
+        FAIL("myalloc1() from libfoo.dylib not interposed");
     }
 
     void* p3 = myalloc2(10);
     if ( strncmp(p3+10, "##########", 10) != 0 ) {
-        printf("[FAIL] interpose-malloc myalloc2() from libfoo.dylib not interposed\n");
-        return 0;
+        FAIL("myalloc2() from libfoo.dylib not interposed");
     }
 
     void* p4 = strdup("hello");
      if ( strncmp(p4+6, "#######", 6) != 0 ) {
-        printf("[FAIL] interpose-malloc malloc() from strdup not interposed\n");
-        return 0;
+        FAIL("malloc() from strdup not interposed");
     }
 
-    //printf("%p %p %p %p\n", p1, p2, p3, p4);
-    printf("[PASS] interpose-malloc\n");
-       return 0;
+    LOG("%p %p %p %p", p1, p2, p3, p4);
+    PASS("Success");
 }
index 89651e824c89d4f53d4ddee161414993797559e6..bee065937d45edd8de75f13b4948c626949ba439 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "test_support.h"
+
 extern int foo();
 int (*pFoo)() = &foo;
 
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
 #if INTERPOSED
-    printf("[BEGIN] interpose-resolver\n");
     if ( foo() != 11 )
-        printf("[FAIL]  interpose-resolver: foo() != 11\n");
+        FAIL("foo() != 11");
     else if ( (*pFoo)() != 11 )
-        printf("[FAIL]  interpose-resolver: *pFoo() != 11\n");
+        FAIL("*pFoo() != 11");
     else
-        printf("[PASS]  interpose-resolver\n");
+        PASS("Success");
 #else
-    printf("[BEGIN] resolver-only\n");
     if ( foo() != 10 )
-        printf("[FAIL]  resolver-only: foo() != 10\n");
+        FAIL(" foo() != 10");
     else if ( (*pFoo)() != 10 )
-        printf("[FAIL]  resolver-only: *pFoo() != 10\n");
+        FAIL(" *pFoo() != 10");
     else
-        printf("[PASS]  resolver-only\n");
+        PASS("Success");
 #endif
-       return 0;
 }
index e712974d769b17e70699247aa658458053d3ee2f..8e7e99e84946bc43ed1e113cdd14710cd0fcfd7d 100644 (file)
@@ -1,7 +1,7 @@
 // BUILD:  $CC fooimpl.c -dynamiclib -o $BUILD_DIR/libfooimpl.dylib -install_name $RUN_DIR/libfooimpl.dylib
-// BUILD:  $CC foo.c -dynamiclib $BUILD_DIR/libfooimpl.dylib -o $BUILD_DIR/libfoo.dylib -Wl,-interposable_list,interposable.txt -install_name $RUN_DIR/libfoo.dylib
-// BUILD:  $CC bar.c -bundle     $BUILD_DIR/libfooimpl.dylib -o $BUILD_DIR/libbar.bundle -Wl,-interposable_list,interposable.txt
-// BUILD:  $CC main.c -DRUN_DIR="$RUN_DIR" -Wl,-interposable_list,interposable.txt -o $BUILD_DIR/interpose-then-dlopen.exe
+// BUILD:  $CC foo.c -dynamiclib $BUILD_DIR/libfooimpl.dylib -o $BUILD_DIR/libfoo.dylib -Wl,-interposable_list,$SRC_DIR/interposable.txt -install_name $RUN_DIR/libfoo.dylib
+// BUILD:  $CC bar.c -bundle     $BUILD_DIR/libfooimpl.dylib -o $BUILD_DIR/libbar.bundle -Wl,-interposable_list,$SRC_DIR/interposable.txt
+// BUILD:  $CC main.c -DRUN_DIR="$RUN_DIR" -Wl,-interposable_list,$SRC_DIR/interposable.txt -o $BUILD_DIR/interpose-then-dlopen.exe
 // BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-then-dlopen.exe
 // BUILD:  $CC interposer.c -dynamiclib $BUILD_DIR/libfooimpl.dylib -o $BUILD_DIR/libinterposer.dylib -install_name libinterposer.dylib
 
@@ -14,6 +14,8 @@
 #include <string.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
+
 // Note, libinterposer.dylib interposes interposableFoo
 extern int interposableFoo();
 
@@ -23,55 +25,38 @@ extern int interposableBar();
 extern int callFunc();
 
 
-static int tryImage(const char* path, int expectedFoo, int expectedBar)
+static void tryImage(const char* path, int expectedFoo, int expectedBar)
 {
   void* handle = dlopen(path, RTLD_LAZY);
   if ( handle == NULL ) {
-    printf("dlerror(): %s\n", dlerror());
-    printf("[FAIL] interpose-then-dlopen %s\n", path);
-    return 1;
+    FAIL("dlopen(\"%s\") error: %s", path, dlerror());
   }
   
   __typeof(&callFunc) callFooSym = (__typeof(&callFunc))dlsym(handle, "callFoo");
   if ( callFooSym == NULL ) {
-    printf("dlerror(): %s\n", dlerror());
-    printf("[FAIL] interpose-then-dlopen %s\n", path);
-    return 1;
+    FAIL("dlsym(\"callFoo\") error: %s", dlerror());
   }
   
   int fooResult = callFooSym();
   if ( fooResult != expectedFoo ) {
-    printf("[FAIL] interpose-then-dlopen callFoo() from %s not interposed as it returned %d\n", path, fooResult);
-    return 1;
+    FAIL("callFoo() from %s not interposed as it returned %d", path, fooResult);
   }
   
   __typeof(&callFunc) callBarSym = (__typeof(&callFunc))dlsym(handle, "callBar");
   if ( callBarSym == NULL ) {
-    printf("dlerror(): %s\n", dlerror());
-    printf("[FAIL] interpose-then-dlopen %s\n", path);
-    return 1;
+    FAIL("dlsym(\"callBar\") error: %s", dlerror());
   }
   
   int barResult = callBarSym();
   if ( barResult != expectedBar ) {
-    printf("[FAIL] interpose-then-dlopen callBar() from %s not interposed as it returned %d\n", path, barResult);
-    return 1;
+    FAIL("callBar() from %s not interposed as it returned %d", path, barResult);
   }
   
-  return 0;
 }
 
-int main()
-{
-    printf("[BEGIN] interpose-then-dlopen\n");
-
-    if (tryImage(RUN_DIR "/libfoo.dylib", 4, 2))
-      return 0;
-  
-    if (tryImage(RUN_DIR "/libbar.bundle", 4, 100))
-      return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    tryImage(RUN_DIR "/libfoo.dylib", 4, 2);
+    tryImage(RUN_DIR "/libbar.bundle", 4, 100);
 
-    //printf("%p %p %p %p\n", p1, p2, p3, p4);
-    printf("[PASS] interpose-then-dlopen\n");
-       return 0;
+    PASS("Success");
 }
index 52484276d2453041bcf842907c668aec324bae61..07024b0e07bc1923092109e13135c536eb736206 100644 (file)
@@ -3,12 +3,13 @@
 // BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-weak-present.exe
 // BUILD:  $CC interposer.c -dynamiclib $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/libinterposer.dylib -install_name libinterposer.dylib
 
-// BUILD:  $CC foo.c            -dynamiclib -o $TEMP_DIR/libfoo2.dylib  -install_name $RUN_DIR/libfoo2.dylib
+// BUILD:  $CC foo.c            -dynamiclib -o $BUILD_DIR/foo34/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib
 // BUILD:  $CC foo.c -DNO_FOO34 -dynamiclib -o $BUILD_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib
-// BUILD:  $CC main.c -DNO_FOO34  $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/interpose-weak-missing.exe
+// BUILD:  $CC main.c -DNO_FOO34  $BUILD_DIR/foo34/libfoo2.dylib -o $BUILD_DIR/interpose-weak-missing.exe
 // BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-weak-missing.exe
-// BUILD:  $CC interposer.c -dynamiclib $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/libinterposer2.dylib -install_name libinterposer.dylib
+// BUILD:  $CC interposer.c -dynamiclib $BUILD_DIR/foo34/libfoo2.dylib -o $BUILD_DIR/libinterposer2.dylib -install_name libinterposer.dylib
 
+// BUILD: $SKIP_INSTALL $BUILD_DIR/foo34/libfoo2.dylib
 
 // RUN:  DYLD_INSERT_LIBRARIES=libinterposer.dylib   ./interpose-weak-present.exe
 // RUN:  DYLD_INSERT_LIBRARIES=libinterposer2.dylib  ./interpose-weak-missing.exe
@@ -19,6 +20,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "test_support.h"
 
 extern int foo1();
 extern int foo2();
@@ -31,42 +33,32 @@ extern int foo4() __attribute__((weak_import));
     #define MODE "missing"
 #endif
 
-int main()
-{
-    printf("[BEGIN] interpose-weak-" MODE "\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
        if ( foo1() != 1 ) {
-        printf("[FAIL] interpose-weak-" MODE ", foo1() != 1\n");
-        return 0;
+        FAIL(MODE ", foo1() != 1");
     }
 
        if ( foo2() != 12 ) {
-        printf("[FAIL] interpose-weak-" MODE ", foo2() != 12\n");
-        return 0;
+        FAIL(MODE ", foo2() != 12");
     }
 
 #ifndef NO_FOO34
        if ( foo3() != 3 ) {
-        printf("[FAIL] interpose-weak-" MODE ", foo3() != 3\n");
-        return 0;
+        FAIL(MODE ", foo3() != 3");
     }
 
        if ( foo4() != 14 ) {
-        printf("[FAIL] interpose-weak-" MODE ", foo4() != 14\n");
-        return 0;
+        FAIL(MODE ", foo4() != 14");
     }
 #else
        if ( &foo3 != NULL ) {
-        printf("[FAIL] interpose-weak-" MODE ", &foo3 != NULL\n");
-        return 0;
+        FAIL(MODE ", &foo3 != NULL");
     }
 
        if ( &foo4 != NULL ) {
-        printf("[FAIL] interpose-weak-" MODE ", &foo4 != NULL\n");
-        return 0;
+        FAIL(MODE ", &foo4 != NULL");
     }
 #endif
 
-    printf("[PASS] interpose-weak-" MODE "\n");
-       return 0;
+    PASS("Success");
 }
index a4f4a1b33061e7ca4afdd1f21b479d57827ed6cc..48fe6a8798b4065106bd16232b93c637d67f640b 100644 (file)
@@ -45,6 +45,8 @@
 
 #include <stdio.h>
 
+#include "test_support.h"
+
 extern int foo1();
 extern int foo2();
 extern int foo3();
@@ -78,52 +80,46 @@ extern int foo30();
 extern int foo31();
 extern int foo32();
 
-int failedCheck(int i, int j) {
+void failedCheck(int i, int j) {
     if (i != j) {
-        printf("[FAIL] launch-image-cache: expected %d but got %d\n", j, i);
-        return 1;
+        FAIL("expected %d but got %d", j, i);
     }
-    return 0;
 }
 
-int main()
-{
-    printf("[BEGIN] launch-image-cache\n");
-
-    if (failedCheck(foo1(), 1)) return 0;
-    if (failedCheck(foo2(), 2)) return 0;
-    if (failedCheck(foo3(), 3)) return 0;
-    if (failedCheck(foo4(), 4)) return 0;
-    if (failedCheck(foo5(), 5)) return 0;
-    if (failedCheck(foo6(), 6)) return 0;
-    if (failedCheck(foo7(), 7)) return 0;
-    if (failedCheck(foo8(), 8)) return 0;
-    if (failedCheck(foo9(), 9)) return 0;
-    if (failedCheck(foo10(), 10)) return 0;
-    if (failedCheck(foo11(), 11)) return 0;
-    if (failedCheck(foo12(), 12)) return 0;
-    if (failedCheck(foo13(), 13)) return 0;
-    if (failedCheck(foo14(), 14)) return 0;
-    if (failedCheck(foo15(), 15)) return 0;
-    if (failedCheck(foo16(), 16)) return 0;
-    if (failedCheck(foo17(), 17)) return 0;
-    if (failedCheck(foo18(), 18)) return 0;
-    if (failedCheck(foo19(), 19)) return 0;
-    if (failedCheck(foo20(), 20)) return 0;
-    if (failedCheck(foo21(), 21)) return 0;
-    if (failedCheck(foo22(), 22)) return 0;
-    if (failedCheck(foo23(), 23)) return 0;
-    if (failedCheck(foo24(), 24)) return 0;
-    if (failedCheck(foo25(), 25)) return 0;
-    if (failedCheck(foo26(), 26)) return 0;
-    if (failedCheck(foo27(), 27)) return 0;
-    if (failedCheck(foo28(), 28)) return 0;
-    if (failedCheck(foo29(), 29)) return 0;
-    if (failedCheck(foo30(), 30)) return 0;
-    if (failedCheck(foo31(), 31)) return 0;
-    if (failedCheck(foo32(), 32)) return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    failedCheck(foo1(), 1);
+    failedCheck(foo2(), 2);
+    failedCheck(foo3(), 3);
+    failedCheck(foo4(), 4);
+    failedCheck(foo5(), 5);
+    failedCheck(foo6(), 6);
+    failedCheck(foo7(), 7);
+    failedCheck(foo8(), 8);
+    failedCheck(foo9(), 9);
+    failedCheck(foo10(), 10);
+    failedCheck(foo11(), 11);
+    failedCheck(foo12(), 12);
+    failedCheck(foo13(), 13);
+    failedCheck(foo14(), 14);
+    failedCheck(foo15(), 15);
+    failedCheck(foo16(), 16);
+    failedCheck(foo17(), 17);
+    failedCheck(foo18(), 18);
+    failedCheck(foo19(), 19);
+    failedCheck(foo20(), 20);
+    failedCheck(foo21(), 21);
+    failedCheck(foo22(), 22);
+    failedCheck(foo23(), 23);
+    failedCheck(foo24(), 24);
+    failedCheck(foo25(), 25);
+    failedCheck(foo26(), 26);
+    failedCheck(foo27(), 27);
+    failedCheck(foo28(), 28);
+    failedCheck(foo29(), 29);
+    failedCheck(foo30(), 30);
+    failedCheck(foo31(), 31);
+    failedCheck(foo32(), 32);
 
-    printf("[PASS]  launch-image-cache\n");
-    return 0;
+    PASS("Success");
 }
 
index a6c14e66b90873645e97400bfb39ea236b28d140..0c74e996df70dbca770661d5c11c9a825236ecaf 100644 (file)
@@ -1,11 +1,13 @@
 // note: -Os is needed for armv7 to work around compiler issue where at -O0 it computes pointer to function and calls that
 
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib         -install_name $RUN_DIR/libfoo.dylib
-// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo-present.dylib  -install_name $RUN_DIR/libfoo.dylib -DHAS_SYMBOL=1
-// BUILD:  $CC main.c            -o $BUILD_DIR/lazy-symbol-missing.exe        $TEMP_DIR/libfoo-present.dylib -Os
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo-present.dylib  -install_name $RUN_DIR/libfoo.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 $TEMP_DIR/libfoo-present.dylib -Os
-// BUILD:  $CC runner.c          -o $BUILD_DIR/lazy-symbol-runner.exe -DRUN_DIR="$RUN_DIR"
+// BUILD:  $CC main-call.c       -o $BUILD_DIR/lazy-symbol-missing-called.exe $BUILD_DIR/libfoo-present.dylib -Os
+// BUILD:  $CXX runner.cpp         -o $BUILD_DIR/lazy-symbol-runner.exe -DRUN_DIR="$RUN_DIR"
+
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libfoo-present.dylib
 
 // NO_CRASH_LOG: lazy-symbol-missing-called.exe
 
@@ -18,6 +20,8 @@
 #include <stdbool.h>
 #include <mach-o/getsect.h>
 
+#include "test_support.h"
+
 #if __LP64__
 extern struct mach_header_64 __dso_handle;
 #else
@@ -32,10 +36,7 @@ extern int slipperySymbol();
   #define TESTNAME "lazy-symbol-missing"
 #endif
 
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] " TESTNAME "\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
 #if __arm64e__
     // arm64e always uses chained binds which does not support lazy binding
     bool supportsLazyBinding = false;
@@ -55,9 +56,6 @@ int main(int argc, const char* argv[])
         if ( argc < 0 )
             slipperySymbol();
     }
-
-    printf("[PASS]  " TESTNAME "\n");
-
-       return 0;
+    PASS("%s", TESTNAME);
 }
 
diff --git a/testing/test-cases/lazy-symbol-missing.dtest/runner.c b/testing/test-cases/lazy-symbol-missing.dtest/runner.c
deleted file mode 100644 (file)
index 5aaeff3..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <err.h>
-#include <System/sys/reason.h>
-#include <System/sys/proc_info.h>
-#include <System/kern/kern_cdata.h>
-#include <libproc.h>
-#include <mach-o/dyld_priv.h>
-
-#if __arm64e__
-    // arm64e uses chained binds which does not support lazy binding
-    #define SUPPORTS_LAZY_BINDING 0
-#else
-    #define SUPPORTS_LAZY_BINDING 1
-#endif
-
-
-static bool        sSignalCaught = false;
-static bool        sChildAbortInDyld = false;
-static pid_t       sChildPid = 0;
-
-
-static void childDied(int sig)
-{
-    sSignalCaught = true;
-    //printf("sigchld for pid=%d\n", sChildPid);
-
-    struct proc_exitreasoninfo info;
-    bzero(&info, sizeof(info));
-    uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE];
-    bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE);
-    info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
-    info.eri_kcd_buf = (user_addr_t)packReasonData;
-    //fprintf(stderr, "info=%p\n", &info);
-    int procResult = proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE);
-    if ( procResult != sizeof(struct proc_exitreasoninfo) ) {
-        printf("bad return size from proc_pidinfo(), %d expected %lu\n", procResult, PROC_PIDEXITREASONINFO_SIZE);
-        return;
-    }
-    if ( info.eri_namespace != OS_REASON_DYLD ) {
-        printf("eri_namespace (%d) != OS_REASON_DYLD\n", info.eri_namespace);
-        return;
-    }
-
-   sChildAbortInDyld = true;
-}
-
-
-bool runTest(const char* prog)
-{
-    sSignalCaught = false;
-    sChildAbortInDyld = false;
-
-    // fork and exec child
-       sChildPid = fork();
-       if ( sChildPid < 0 )
-               err(EXIT_FAILURE, "fork");
-       if ( sChildPid == 0 ) {
-               // child side
-        char* childArgv[] = { (char*)prog, NULL };
-               int result = execvp(prog, childArgv);
-        err(EXIT_FAILURE, "exec(\"%s\",...)", prog);
-       }
-    for(int i=0; i < 10; ++i) {
-        if ( sSignalCaught )
-            break;
-        sleep(1);
-    }
-
-    return sChildAbortInDyld;
-}
-
-
-int main(int argc, const char* argv[])
-{
-    printf("[BEGIN] lazy-symbol-missing and called\n");
-
-#if SUPPORTS_LAZY_BINDING
-    // set up signal handler for catching child terminations
-    signal(SIGCHLD, childDied);
-
-    // test launch program with missing library
-    runTest(RUN_DIR "/lazy-symbol-missing-called.exe");
-
-    if ( sSignalCaught && sChildAbortInDyld )
-        printf("[PASS] lazy-symbol-missing and called\n");
-    else
-        printf("[FAIL] lazy-symbol-missing and called\n");
-#else
-    printf("[PASS] lazy-symbol-missing and called\n");
-#endif
-    return 0;
-}
-
diff --git a/testing/test-cases/lazy-symbol-missing.dtest/runner.cpp b/testing/test-cases/lazy-symbol-missing.dtest/runner.cpp
new file mode 100644 (file)
index 0000000..aadd53f
--- /dev/null
@@ -0,0 +1,55 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <err.h>
+#include <System/sys/reason.h>
+#include <System/sys/proc_info.h>
+#include <System/kern/kern_cdata.h>
+#include <libproc.h>
+#include <mach-o/dyld_priv.h>
+
+#if __arm64e__
+    // arm64e uses chained binds which does not support lazy binding
+    #define SUPPORTS_LAZY_BINDING 0
+#else
+    #define SUPPORTS_LAZY_BINDING 1
+#endif
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+#if SUPPORTS_LAZY_BINDING
+    _process process;
+    process.set_executable_path(RUN_DIR "/lazy-symbol-missing-called.exe");
+    const char* env[] = { "TEST_OUTPUT=None", NULL};
+    process.set_env(env);
+    process.set_exit_handler(^(pid_t pid) {
+        LOG("Child exited pid=%d", pid);
+
+        struct proc_exitreasoninfo info;
+        bzero(&info, sizeof(info));
+        uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE];
+        bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE);
+        info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
+        info.eri_kcd_buf = (user_addr_t)packReasonData;
+        LOG("info=%p", &info);
+        int procResult = proc_pidinfo(pid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE);
+        if ( procResult != sizeof(struct proc_exitreasoninfo) ) {
+            FAIL("bad return size from proc_pidinfo(), %d expected %lu", procResult, PROC_PIDEXITREASONINFO_SIZE);
+        }
+        if ( info.eri_namespace != OS_REASON_DYLD ) {
+            FAIL("eri_namespace (%d) != OS_REASON_DYLD", info.eri_namespace);
+        }
+        PASS("Success");
+    });
+    (void)process.launch();
+#endif
+    PASS("Success");
+}
+
diff --git a/testing/test-cases/macOS-cache-rebuild.dtest/main.c b/testing/test-cases/macOS-cache-rebuild.dtest/main.c
deleted file mode 100644 (file)
index d60aee3..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-// BUILD_ONLY: MacOSX
-
-// BUILD:  $CC main.c            -o $BUILD_DIR/rebuild-dyld-cache.exe
-
-// RUN:  ./rebuild-dyld-cache.exe
-
-
-#include <stdio.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <spawn.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-#include <sys/types.h>
-
-extern char** environ;
-
-int main()
-{
-    printf("[BEGIN] macOS-cache-rebuild\n");
-
-    const char* argv[] = { "/usr/bin/update_dyld_shared_cache", "-cache_dir", "/tmp/", NULL };
-    pid_t child;
-    int psResult = posix_spawn(&child, "/usr/bin/update_dyld_shared_cache", NULL, NULL, (char**)argv, environ);
-    if ( psResult != 0 ) {
-        printf("[FAIL]  macOS-cache-rebuild: posix_spawn failed, err=%d\n", psResult);
-        return 0;
-    }
-
-    int childStatus;
-    (void)wait4(child, &childStatus, 0, NULL);
-    if (WIFEXITED(childStatus) == 0)
-        printf("[FAIL]  macOS-cache-rebuild: update_dyld_shared_cache did not exit\n");
-    else if (WEXITSTATUS(childStatus) != 0)
-        printf("[FAIL]  macOS-cache-rebuild: update_dyld_shared_cache failed\n");
-    else
-        printf("[PASS]  macOS-cache-rebuild\n");
-
-       return 0;
-}
-
diff --git a/testing/test-cases/macOS-cache-rebuild.dtest/main.cpp b/testing/test-cases/macOS-cache-rebuild.dtest/main.cpp
new file mode 100644 (file)
index 0000000..9d3ea90
--- /dev/null
@@ -0,0 +1,43 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CXX main.cpp          -o $BUILD_DIR/rebuild-dyld-cache.exe
+
+// 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
+
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+
+#include "test_support.h"
+
+extern char** environ;
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    _process process;
+    process.set_executable_path("/usr/bin/update_dyld_shared_cache");
+    const char* env[] = { "TEST_OUTPUT=None", NULL};
+    process.set_env(env);
+    const char* args[] = { "-cache_dir", "/tmp/", NULL };
+    process.set_args(args);
+    process.set_exit_handler(^(pid_t pid) {
+        int childStatus;
+        (void)wait4(pid, &childStatus, 0, NULL);
+        if (WIFEXITED(childStatus) == 0)
+            FAIL("update_dyld_shared_cache did not exit");
+        else if (WEXITSTATUS(childStatus) != 0)
+            FAIL("update_dyld_shared_cache failed");
+        else
+            PASS("Success");
+    });
+
+    process.launch();
+}
+
index b4921cd45d1b7af4c12e6fb0ad05eee7d7f85bfb..f677581dc4edda97727e1303527db68630500d35 100644 (file)
@@ -1,7 +1,9 @@
 
-// BUILD:  $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $TEMP_DIR/libbar.dylib
+// 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 main.c $TEMP_DIR/libbar.dylib -o $BUILD_DIR/missing-weak-def.exe
+// BUILD:  $CC main.c $BUILD_DIR/missing/libbar.dylib -o $BUILD_DIR/missing-weak-def.exe
+
+// BUILD: $SKIP_INSTALL $BUILD_DIR/missing/libbar.dylib
 
 // RUN:  ./missing-weak-def.exe
 
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "test_support.h"
+
 __attribute__((weak))
 __attribute__((weak_import))
 int bar();
 
-int main()
-{
-    printf("[BEGIN] missing-weak-def\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if (&bar) {
-       printf("[FAIL] missing-weak-def\n");
-       return 0;
+        FAIL("bar from libbar not bound");
     }
 
-    printf("[PASS] missing-weak-def\n");
-       return 0;
+    PASS("Success");
 }
 
index 940774096880aed7d612c1c71fbe4507e7a4d13e..1ce992e090dd7872a9119d87bf88f9b87e39bd96 100644 (file)
 #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()
-{
-    printf("[BEGIN] no-shared-cache\n");
+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 ) {
-        printf("[FAIL] no-shared-cache: _dyld_get_shared_cache_range() returned %p even though we are not using a dyld cache\n", cacheStart);
-        return 0;
+        FAIL("_dyld_get_shared_cache_range() returned %p even though we are not using a dyld cache", cacheStart);
     }
 
-    printf("[PASS] no-shared-cache\n");
-    return 0;
+    PASS("Success");
 }
 
index 1773a03486de1593476477bb9313f3e8f2ac8065..a6d0c4a296548187eb34774ac3340283eb8b86c6 100644 (file)
@@ -3,10 +3,9 @@
 
 // RUN:  ./operator-new.exe
 
-#include <stdio.h>
 #include <new>
 
-
+#import "test_support.h"
 
 //
 // This test case verifies that calling operator new[] in libstdc++.dylib
@@ -35,35 +34,28 @@ void operator delete(void* p) throw()
     ::free(p);
 }
 
-int main()
-{
-       printf("[BEGIN] operator-new\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // test that OS's operator new[] redirects to my operator new
     myLastNewAllocation = NULL;
     char* stuff = new char[24];
     if ( (void*)stuff != myLastNewAllocation ) {
-        printf("[FAIL] operator-new system array allocator not redirected through my operator new\n");
-        return 0;
+        FAIL("system array allocator not redirected through my operator new");
     }
 
     // test that program uses my operator new
     myLastNewAllocation = NULL;
     Foo* foo = new Foo();
     if ( (void*)foo != myLastNewAllocation ) {
-        printf("[FAIL] operator-new allocation not redirected though my operator new\n");
-        return 0;
+        FAIL("allocation not redirected though my operator new");
     }
 
     //
     delete foo;
     if ( (void*)foo != myLastDelete ) {
-        printf("[FAIL] operator-new deallocation not redirected though my operator delete\n");
-        return 0;
+        FAIL("deallocation not redirected though my operator delete");
     }
 
-    printf("[PASS] operator-new\n");
-       return 0;
+    PASS("Success");
 }
 
 
index 63e43ef8d276185eac622c85410fc332ebf9cda4..a4797eebf181a181831855f0f24bbba725307e26 100644 (file)
@@ -17,6 +17,8 @@
 #include <mach-o/dyld_priv.h>
 #include <mach-o/getsect.h>
 
+#include "test_support.h"
+
 extern bool isReadOnly(const void* addr);
 extern bool testLib();
 
@@ -24,13 +26,11 @@ const void* const funcs[] = { &malloc, &free, &strcmp, &printf };
 
 typedef bool (*TestFunc)(void);
 
-static bool sBadImage = false;
-
 static void notify(const struct mach_header* mh, intptr_t slide)
 {
     // only look at images not in dyld shared cache
     if ( (mh->flags & 0x80000000) == 0 ) {
-        //fprintf(stderr, "mh=%p flags=0x%08X\n", mh, mh->flags);
+        LOG("mh=%p flags=0x%08X", mh, mh->flags);
         const char* path = dyld_image_path_containing_address(mh);
         bool inTestDir = (strstr(path, RUN_DIR) != NULL);
         unsigned long size;
@@ -40,27 +40,21 @@ static void notify(const struct mach_header* mh, intptr_t slide)
         uint8_t* p = getsectiondata(mh, "__DATA_CONST", "__const", &size);
     #endif
         if ( (p != NULL) && inTestDir && !isReadOnly(p) ) {
-            printf("[FAIL]  read-only-data __DATA_CONST,__const section not read-only in %p %s\n", mh, path);
-            sBadImage = true;
+            FAIL("read-only-data __DATA_CONST,__const section not read-only in %p %s", mh, path);
         }
     }
 }
 
 
-int main()
-{
-    printf("[BEGIN] read-only-data\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // test __DATA_CONST in main is read-only
     if ( !isReadOnly(&funcs[2]) ) {
-        printf("[FAIL]  read-only-data main executable not read-only\n");
-        return 0;
+        FAIL("main executable not read-only");
     }
 
     // test __DATA_CONST in linked dylib is read-only
     if ( !testLib() ) {
-        printf("[FAIL]  read-only-data librotest.dylib not read-only\n");
-        return 0;
+        FAIL("librotest.dylib not read-only");
     }
 
     _dyld_register_func_for_add_image(&notify);
@@ -68,18 +62,13 @@ int main()
     // test __DATA_CONST in dlopen'ed bundle is read-only
     void* h = dlopen(RUN_DIR "/test.bundle", 0);
     if ( h == NULL ) {
-        printf("[FAIL]  read-only-data test.bundle not loaded\n");
-        return 0;
+        FAIL("test.bundle not loaded");
     }
     TestFunc tb = (TestFunc)dlsym(h, "testBundle");
     if ( !tb() ) {
-        printf("[FAIL]  read-only-data test.bundle not read-only\n");
-        return 0;
+        FAIL("test.bundle not read-only");
     }
 
-    if ( !sBadImage )
-        printf("[PASS]  read-only-data\n");
-
-       return 0;
+    PASS("Success");
 }
 
index e8355ca67cfb8b8bdd957997507546db877027f4..a00a77b4c47cb7880027aae074be54aca1fdfdbb 100644 (file)
@@ -1,10 +1,8 @@
 // BUILD_ONLY: MacOSX
 
-// BUILD:  mkdir $BUILD_DIR/lc
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/lc/libfoo.dylib -install_name /blah/libfoo.dylib
 // BUILD:  $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-find.exe    -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-find -DSHOULD_BE_FOUND=1
 // BUILD:  $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-no-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-no-find -sectcreate __RESTRICT __restrict /dev/null
-// BUILD:  mkdir $BUILD_DIR/rpath
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/rpath/libfoo.dylib -install_name @rpath/libfoo.dylib
 // BUILD:  $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-find.exe    -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-find -DSHOULD_BE_FOUND=1
 // BUILD:  $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-no-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-no-find -sectcreate __RESTRICT __restrict /dev/null
@@ -20,6 +18,8 @@
 #include <stdio.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
+
 // Two ways to find libfoo.dylb: @rpath or DYLD_LIBRARY_PATH (set via LC_DYLD_ENVIRONMENT)
 // These should work for non-restrictured programs.
 // These should fail for restricted programs.
@@ -33,20 +33,18 @@ extern int foo() __attribute__((weak_import));
 #define STRINGIFY(x) STRINGIFY2(x)
 
 
-int main(int argc, const char* argv[])
-{
-   printf("[BEGIN] restrict-search %s\n", STRINGIFY(MODE));
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
 #if SHOULD_BE_FOUND
     if ( &foo == NULL )
-        printf("[FAIL] restrict-search %s\n", STRINGIFY(MODE));
+        FAIL("Incorrectly found %s", STRINGIFY(MODE));
     else
-        printf("[PASS] restrict-search %s\n", STRINGIFY(MODE));
+        PASS("Incorrectly did not find %s", STRINGIFY(MODE));
 #else
     // dylib won't be found at runtime, so &foo should be NULL
     if ( &foo == NULL )
-        printf("[PASS] restrict-search %s\n", STRINGIFY(MODE));
+        PASS("Found %s", STRINGIFY(MODE));
     else
-        printf("[FAIL] restrict-search %s\n", STRINGIFY(MODE));
+        FAIL("Could not find %s", STRINGIFY(MODE));
 #endif
 
        return 0;
index f37c78e7ca01af57114b4d413b4930c707ebf3e5..93b2305ff6ffee3b696b0782665092878b0ad3b2 100644 (file)
 
 #include <stdio.h>
 
+#include "test_support.h"
+
 extern int foo;
 
 
-int main()
-{
-    printf("[BEGIN] rpath-absolute.exe\n");
-    printf("[PASS]  rpath-absolute.exe\n");
-       return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    PASS("Success");
 }
 
 
diff --git a/testing/test-cases/rpath-relative.dtest/foo.c b/testing/test-cases/rpath-relative.dtest/foo.c
new file mode 100644 (file)
index 0000000..723758f
--- /dev/null
@@ -0,0 +1 @@
+int foo = 42;
diff --git a/testing/test-cases/rpath-relative.dtest/main.c b/testing/test-cases/rpath-relative.dtest/main.c
new file mode 100644 (file)
index 0000000..2779b7b
--- /dev/null
@@ -0,0 +1,29 @@
+
+// BOOT_ARGS: dyld_flags=2
+
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/librel.dylib -install_name @rpath/librel.dylib
+// BUILD:  $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-executable.exe       -rpath @executable_path
+// BUILD:  $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-executable-slash.exe -rpath @executable_path/
+// BUILD:  $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-loader.exe           -rpath @loader_path
+// BUILD:  $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-loader-slash.exe     -rpath @loader_path/
+
+// RUN: ./rpath-executable.exe
+// RUN: ./rpath-executable-slash.exe
+// RUN: ./rpath-loader.exe
+// RUN: ./rpath-loader-slash.exe
+
+// main prog links with librel.dylib.  There are four variants of how LC_RPATH is set up.
+
+#include <stdio.h>
+
+#include "test_support.h"
+
+extern char* __progname;
+
+
+int main(int argc, const char* argv[])
+{
+    PASS("%s", __progname);
+}
+
+
index 4ed1aa6ab7033dc1c271b465f0db4d2b5c1da646..bfc258ed2c33a1015a341a7114efec8c5b406253 100644 (file)
@@ -1,10 +1,12 @@
 
 // BOOT_ARGS: dyld_flags=2
 
-// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/libmissing.dylib -install_name @rpath/libmissing.dylib
-// BUILD:  $CC foo.c -dynamiclib -Wl,-weak_library,$TEMP_DIR/libmissing.dylib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -rpath $RUN_DIR
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name @rpath/libmissing.dylib
+// BUILD:  $CC foo.c -dynamiclib -Wl,-weak_library,$BUILD_DIR/libmissing.dylib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -rpath $RUN_DIR
 // BUILD:  $CC main.c -o $BUILD_DIR/rpath-weak-missing.exe -DRUN_DIR="$RUN_DIR"
 
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libmissing.dylib
+
 // RUN:  ./rpath-weak-missing.exe
 // RUN:  DYLD_AMFI_FAKE=0 ./rpath-weak-missing.exe
 
 #include <stdio.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
 int main()
 {
-    printf("[BEGIN] rpath-weak-missing\n");
-
     void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  rpath-weak-missing dlopen(\"%s/libfoo.dylib\") - %s\n", RUN_DIR, dlerror());
-        return 0;
+        FAIL("rpath-weak-missing dlopen(\"%s/libfoo.dylib\") - %s", RUN_DIR, dlerror());
     }
 
-    printf("[PASS]  rpath-weak-missing\n");
-       return 0;
+    PASS("rpath-weak-missing");
 }
 
 
index 2327d3ded2968025b914da800f4eb3a2ec3f5ac2..a6058f6c53fabfe5efbe8c5d299c8079f51c6f89 100644 (file)
@@ -18,7 +18,7 @@
     #include <ptrauth.h>
 #endif
 
-
+#include "test_support.h"
 
 struct dyld_cache_header
 {
@@ -61,10 +61,7 @@ static void forEachCacheInDir(const char* dirPath, void (^handler)(const uuid_t
 }
 
 
-int main()
-{
-    printf("[BEGIN] shared_cache_iterate\n");
-
+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);
     uuid_t currentCacheUUID;
@@ -80,16 +77,13 @@ int main()
             ++count;
         });
         if ( result != 0 ) {
-            printf("[FAIL] shared_cache_iterate dyld_shared_cache_iterate_text() returned non-zero: %d\n", result);
-            return 0;
+            FAIL("dyld_shared_cache_iterate_text() returned non-zero: %d", result);
         }
         if ( count < 100 ) {
-            printf("[FAIL] shared_cache_iterate dyld_shared_cache_iterate_text() iterated over less than 100 images: %d\n", count);
-            return 0;
+            FAIL("dyld_shared_cache_iterate_text() iterated over less than 100 images: %d", count);
         }
         if ( badVersion ) {
-            printf("[FAIL] shared_cache_iterate dyld_shared_cache_iterate_text() some dyld_shared_cache_dylib_text_info was not 2\n");
-            return 0;
+            FAIL("dyld_shared_cache_iterate_text() some dyld_shared_cache_dylib_text_info was not 2");
         }
 
         // iterate current cache
@@ -102,16 +96,13 @@ int main()
             ++count;
         });
         if ( result != 0 ) {
-            printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() returned non-zero: %d\n", result);
-            return 0;
+            FAIL("dyld_shared_cache_find_iterate_text() returned non-zero: %d", result);
         }
         if ( count < 100 ) {
-            printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() iterated over less than 100 images: %d\n", count);
-            return 0;
+            FAIL("dyld_shared_cache_find_iterate_text() iterated over less than 100 images: %d", count);
         }
         if ( badVersion ) {
-            printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() some dyld_shared_cache_dylib_text_info was not 2\n");
-            return 0;
+            FAIL("dyld_shared_cache_find_iterate_text() some dyld_shared_cache_dylib_text_info was not 2");
         }
 
         // look for non-existent cache
@@ -123,26 +114,22 @@ int main()
             ++count;
         });
         if ( result == 0 ) {
-            printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() expected result to be nonzero: %d\n", result);
-            return 0;
+            FAIL("dyld_shared_cache_find_iterate_text() expected result to be nonzero: %d", result);
         }
         if ( count != 0 ) {
-            printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() expected iteration count is zero: %d\n", count);
-            return 0;
+            FAIL("dyld_shared_cache_find_iterate_text() expected iteration count is zero: %d", count);
         }
 
         // find other cache
         const char* curCachePath = dyld_shared_cache_file_path();
         if ( curCachePath == NULL ) {
-            printf("[FAIL] shared_cache_iterate dyld_shared_cache_file_path() returned NULL\n");
-            return 0;
+            FAIL("dyld_shared_cache_file_path() returned NULL");
         }
         char cacheDir[PATH_MAX];
         strlcpy(cacheDir, curCachePath, PATH_MAX);
         char* end = strrchr(cacheDir, '/');
         if ( end == NULL ) {
-            printf("[FAIL] shared_cache_iterate cache path has no '/'\n");
-            return 0;
+            FAIL("cache path has no '/'");
         }
         *end = '\0';
         forEachCacheInDir(cacheDir, ^(const uuid_t uuid) {
@@ -152,22 +139,19 @@ int main()
                     ++count;
                 });
                 if ( res != 0 ) {
-                    printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() expected result to be nonzero: %d\n", result);
-                    exit(0);
+                    FAIL("dyld_shared_cache_find_iterate_text() expected result to be nonzero: %d", result);
                 }
                 if ( count < 100 ) {
-                    printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() iterated over less than 100 images: %d\n", count);
-                    exit(0);
+                    FAIL("dyld_shared_cache_find_iterate_text() iterated over less than 100 images: %d", count);
                 }
             }
         });
     }
     else {
-        printf("no dyld cache\n");
+        LOG("no dyld cache");
     }
 
-    printf("[PASS] shared_cache_iterate\n");
-    return 0;
+    PASS("Success");
 }
 
 
index df44b5e6d2cd9d2b6760d0f48c97b87ebfd79745..2d8f4e69de4ce0458146b2c5ee65907c936caecc 100644 (file)
@@ -9,17 +9,13 @@
 #include <mach-o/dyld_priv.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
-int main()
-{
-    printf("[BEGIN] shared_cache_optimized\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // tests run on internal installs which use un-optimzed dyld cache
     if ( _dyld_shared_cache_optimized() )
-        printf("[FAIL] shared_cache_optimized unexpectedly returned true\n");
+        FAIL("unexpectedly returned true");
     else
-        printf("[PASS] shared_cache_optimized\n");
-
-    return 0;
+        PASS("Success");
 }
 
index 1e08b62365ce355ea15b105d9bca83626be4268f..89ddaf6b7915a5da53a733dc83403803c26884a4 100644 (file)
@@ -13,6 +13,8 @@
     #include <ptrauth.h>
 #endif
 
+#include "test_support.h"
+
 static const void *stripPointer(const void *ptr) {
 #if __has_feature(ptrauth_calls)
     return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
@@ -22,50 +24,41 @@ static const void *stripPointer(const void *ptr) {
 }
 
 
-int main()
-{
-    printf("[BEGIN] shared_cache_range\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // see if image containing malloc is in the dyld cache
     Dl_info info;
     if ( dladdr(&malloc, &info) == 0 ) {
-        printf("[FAIL] shared_cache_range: dladdr(&malloc, xx) failed");
-        return 0;
+        FAIL("shared_cache_range: dladdr(&malloc, xx) fail");
     }
     const struct mach_header* mh = (struct mach_header*)info.dli_fbase;
-    printf("image with malloc=%p\n", mh);
+    LOG("image with malloc=%p", mh);
     if ( mh == NULL ) {
-        printf("[FAIL] shared_cache_range: dladdr(&malloc, xx) => dli_fbase==NULL");
-        return 0;
+        FAIL("shared_cache_range: dladdr(&malloc, xx) => dli_fbase==NULL");
     }
     bool haveSharedCache = (mh->flags & 0x80000000);
-    printf("haveSharedCache=%d\n", haveSharedCache);
+    LOG("haveSharedCache=%d", haveSharedCache);
 
     size_t cacheLen;
     const void* cacheStart = _dyld_get_shared_cache_range(&cacheLen);
 
     if ( haveSharedCache ) {
         if ( cacheStart == NULL ) {
-            printf("[FAIL] _dyld_get_shared_cache_range() returned NULL even though we have a cache\n");
-            return 0;
+            FAIL("_dyld_get_shared_cache_range() returned NULL even though we have a cache");
         }
-        printf("shared cache start=%p, len=0x%0lX\n", cacheStart, cacheLen);
+        LOG("shared cache start=%p, len=0x%0lX", cacheStart, cacheLen);
         const void* cacheEnd = (char*)cacheStart + cacheLen;
 
         // verify malloc is in shared cache
         if ( (stripPointer((void*)&malloc) < cacheStart) || (stripPointer((void*)&malloc) > cacheEnd) ) {
-            printf("[FAIL] shared_cache_range: malloc is outside range of cache\n");
-            return 0;
+            FAIL("shared_cache_range: malloc is outside range of cache");
         }
     }
     else {
         if ( cacheStart != NULL ) {
-            printf("[FAIL] _dyld_get_shared_cache_range() returned non-NULL even though we don't have a cache\n");
-            return 0;
+            FAIL("returned non-NULL even though we don't have a cache");
         }
     }
 
-    printf("[PASS] shared_cache_range\n");
-    return 0;
+    PASS("Success");
 }
 
index 847dfefef4124657f0222d36bd324bde40adb986..388cb844794dfa8f8b437b09bc6f7e353a0a8cb6 100644 (file)
@@ -1,7 +1,8 @@
 #include <stddef.h>
-#include <stdio.h>
 #include <stdbool.h>
 
+#include "test_support.h"
+
 static bool mainCalled           = false;
 static bool libCalled            = false;
 static bool libCalledBeforeMain  = false;
@@ -23,12 +24,12 @@ static __attribute__((destructor))
 void myTerm()
 {
     if ( !mainCalled )
-        printf("[FAIL] static-terminators, main's terminator not called\n");
+        FAIL("main's terminator not called");
     else if ( !libCalled )
-        printf("[FAIL] static-terminators, libDynamic's terminator not called\n");
+        FAIL("libDynamic's terminator not called");
     else if ( !libCalledBeforeMain )
-        printf("[FAIL] static-terminators, libDynamic's terminator called out of order\n");
+        FAIL("libDynamic's terminator called out of order");
     else
-        printf("[PASS] static-terminators\n");
+        PASS("Success");
 }
 
index 4afcee7ed5556f7549282fafd5cdd60bf0e6dbde..5da79d50e0eaf515d6816d5deaca7e6f826b6cbc 100644 (file)
@@ -1,6 +1,7 @@
 #include <stddef.h>
 #include <stdio.h>
 
+#include "test_support.h"
 
 extern void libDynamicTerminated();
 
@@ -8,7 +9,7 @@ extern void libDynamicTerminated();
 static __attribute__((destructor))
 void myTerm()
 {
-    //fprintf(stderr, "foo static terminator\n");
+    LOG("foo static terminator");
     libDynamicTerminated();
 }
 
index 3e6d3ac9b945fa12186b59d6f9744b6357fe4ef0..7df67c872d9a14f2f0cfca2c5bd2dd5ac7ccc489 100644 (file)
@@ -10,6 +10,7 @@
 #include <dlfcn.h>
 #include <mach-o/dyld_priv.h>
 
+#include "test_support.h"
 
 // verify all static terminators run in proper order
 
@@ -19,24 +20,18 @@ extern void mainTerminated();
 static __attribute__((destructor))
 void myTerm()
 {
-    //fprintf(stderr, "main's static terminator\n");
+    LOG("main's static terminator");
     mainTerminated();
 }
 
 
-int main()
-{
-    printf("[BEGIN] static-terminators\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // load dylib
     void* handle = dlopen(RUN_DIR "/libdynamic.dylib", RTLD_LAZY);
     if ( handle == NULL ) {
-        printf("[FAIL]  dlclose-static-terminator: libdynamic.dylib could not be loaded, %s\n", dlerror());
-        return 0;
+        FAIL("libdynamic.dylib could not be loaded, %s", dlerror());
     }
 
     // PASS is printed in libbase.dylib terminator
-
-       return 0;
 }
 
index cebb5291bc370e4163b2895986cbe5fddd504ef1..17af5fa094c35ef221dd19f991487ebd501844fb 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "test_support.h"
+
 extern int foo();
 extern int fooPlusOne();
 
 
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
 #if TEN
     if ( foo() != 10 )
-        printf("[FAIL] symbol-resolver-basic: foo() != 10\n");
+        FAIL("foo() != 10");
     else if ( fooPlusOne() != 11 )
-        printf("[FAIL] symbol-resolver-basic: fooPlusOne() != 11\n");
+        FAIL("fooPlusOne() != 11");
     else
-        printf("[PASS] symbol-resolver-basic\n");
+        PASS("Success");
 #else
     if ( foo() != 0 )
-        printf("[FAIL] symbol-resolver-basic: foo() != 0\n");
+        FAIL("foo() != 0");
     else if ( fooPlusOne() != 1 )
-        printf("[FAIL] symbol-resolver-basic: fooPlusOne() != 1\n");
+        FAIL("fooPlusOne() != 1");
     else
-        printf("[PASS] symbol-resolver-basic\n");
+        PASS("Success");
 #endif
   
        return 0;
index 598911551b8b35279f50b0f67c3242eb31e3904b..dcc3c40b3766a76b5a38cf3c8111aafb84754be0 100644 (file)
@@ -8,6 +8,8 @@
 #include <stdlib.h>
 #include <pthread.h>
 
+#include "test_support.h"
+
 // We create an A and a B.
 // While destroying B we create a C
 // Given that tlv_finalize has "destroy in reverse order of construction", we
@@ -43,15 +45,14 @@ State state;
 
 A::A() {
     if ( state != None ) {
-        printf("[FAIL] thread-local-atexit-macOS: should be in the 'None' state\n");
+        FAIL("should be in the 'None' state");
     }
     state = ConstructedA;
 }
 
 B::B() {
     if ( state != ConstructedA ) {
-        printf("[FAIL] thread-local-atexit-macOS: should be in the 'ConstructedA' state\n");
-        _Exit(0);
+        FAIL("should be in the 'ConstructedA' state");
     }
     state = ConstructedB;
 }
@@ -59,8 +60,7 @@ B::B() {
 C::C() {
     // We construct C during B's destructor
     if ( state != DestroyingB ) {
-        printf("[FAIL] thread-local-atexit-macOS: should be in the 'DestroyingB' state\n");
-        _Exit(0);
+        FAIL("should be in the 'DestroyingB' state");
     }
     state = ConstructedC;
 }
@@ -68,14 +68,12 @@ C::C() {
 // We destroy B first
 B::~B() {
     if ( state != ConstructedB ) {
-        printf("[FAIL] thread-local-atexit-macOS: should be in the 'ConstructedB' state\n");
-        _Exit(0);
+        FAIL("should be in the 'ConstructedB' state");
     }
     state = DestroyingB;
     static thread_local C c;
     if ( state != ConstructedC ) {
-        printf("[FAIL] thread-local-atexit-macOS: should be in the 'ConstructedC' state\n");
-        _Exit(0);
+        FAIL("should be in the 'ConstructedC' state");
     }
     state = DestroyedB;
 }
@@ -83,8 +81,7 @@ B::~B() {
 // Then we destroy C
 C::~C() {
     if ( state != DestroyedB ) {
-        printf("[FAIL] thread-local-atexit-macOS: should be in the 'DestroyedB' state\n");
-        _Exit(0);
+        FAIL("should be in the 'DestroyedB' state");
     }
     state = DestroyedC;
 }
@@ -92,11 +89,10 @@ C::~C() {
 // And finally destroy A
 A::~A() {
     if ( state != DestroyedC ) {
-        printf("[FAIL] thread-local-atexit-macOS: should be in the 'DestroyedC' state\n");
-        _Exit(0);
+        FAIL("should be in the 'DestroyedC' state");
     }
     state = DestroyedA;
-    printf("[PASS] thread-local-atexit-macOS\n");
+    PASS("[Success");
 }
 
 static void work()
@@ -105,11 +101,8 @@ static void work()
     thread_local B b = {};
 }
 
-int main() {
-    printf("[BEGIN] thread-local-atexit-macOS\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     work();
-
     return 0;
 }
 
index d86dcabda8983f844907f009ef56a0302772b7ca..950fb1126c8fd5f6b6998c5fd7d1959e0926d730 100644 (file)
@@ -7,6 +7,8 @@
 #include <stdlib.h>
 #include <pthread.h>
 
+#include "test_support.h"
+
 // We create an A and a B.
 // While destroying B we create a C
 // Given that tlv_finalize has "destroy in reverse order of construction", we
@@ -42,15 +44,14 @@ State state;
 
 A::A() {
     if ( state != None ) {
-        printf("[FAIL] thread-local-atexit: should be in the 'None' state\n");
+        FAIL("Should be in the 'None' state");
     }
     state = ConstructedA;
 }
 
 B::B() {
     if ( state != ConstructedA ) {
-        printf("[FAIL] thread-local-atexit: should be in the 'ConstructedA' state\n");
-        _Exit(0);
+        FAIL("Should be in the 'ConstructedA' state");
     }
     state = ConstructedB;
 }
@@ -58,8 +59,7 @@ B::B() {
 C::C() {
     // We construct C during B's destructor
     if ( state != DestroyingB ) {
-        printf("[FAIL] thread-local-atexit: should be in the 'DestroyingB' state\n");
-        _Exit(0);
+        FAIL("Should be in the 'DestroyingB' state");
     }
     state = ConstructedC;
 }
@@ -67,14 +67,12 @@ C::C() {
 // We destroy B first
 B::~B() {
     if ( state != ConstructedB ) {
-        printf("[FAIL] thread-local-atexit: should be in the 'ConstructedB' state\n");
-        _Exit(0);
+        FAIL("Should be in the 'ConstructedB' state");
     }
     state = DestroyingB;
     static thread_local C c;
     if ( state != ConstructedC ) {
-        printf("[FAIL] thread-local-atexit: should be in the 'ConstructedC' state\n");
-        _Exit(0);
+        FAIL("Should be in the 'ConstructedC' state");
     }
     state = DestroyedB;
 }
@@ -82,8 +80,7 @@ B::~B() {
 // Then we destroy C
 C::~C() {
     if ( state != DestroyedB ) {
-        printf("[FAIL] thread-local-atexit: should be in the 'DestroyedB' state\n");
-        _Exit(0);
+        FAIL("Should be in the 'DestroyedB' state");
     }
     state = DestroyedC;
 }
@@ -91,11 +88,10 @@ C::~C() {
 // And finally destroy A
 A::~A() {
     if ( state != DestroyedC ) {
-        printf("[FAIL] thread-local-atexit: should be in the 'DestroyedC' state\n");
-        _Exit(0);
+        FAIL("Should be in the 'DestroyedC' state");
     }
     state = DestroyedA;
-    printf("[PASS] thread-local-atexit\n");
+    PASS("Success");
 }
 
 static void* work(void* arg)
@@ -106,13 +102,10 @@ static void* work(void* arg)
     return NULL;
 }
 
-int main() {
-    printf("[BEGIN] thread-local-atexit\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     pthread_t worker;
     if ( pthread_create(&worker, NULL, work, NULL) != 0 ) {
-        printf("[FAIL] thread-local-atexit, pthread_create\n");
-        return 0;
+        FAIL("pthread_create");
     }
 
     void* dummy;
index e30179273f3cacd1d7c10ef7b1f0c3a53ffad6df..b7fcf179eabe953615d1d6a45c3f3cc193faa28d 100644 (file)
@@ -7,30 +7,21 @@
 #include <stdio.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
 
-
-
-
-int main()
-{
-    printf("[BEGIN] thread-local-cleanup\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     for (int i=0; i < 1000; ++i) {
         void* handle = dlopen(RUN_DIR "/libtlv.dylib", RTLD_FIRST);
         if ( handle == NULL ) {
-            printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror());
-            return 0;
+            FAIL("dlopen error: iteration %d %s", i, dlerror());
         }
 
         int result = dlclose(handle);
         if ( result != 0 ) {
-            printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror());
-            return 0;
+            FAIL("dlclose error: iteration %d %s", i, dlerror());
         }
     }
-    
-    printf("[PASS] thread-local-cleanup\n");
 
-       return 0;
+    PASS("Success");
 }
 
index 1c43d27f9655b844fe977647644f00567f301023..0386e4125766f5e228344bc528142b29c30607a9 100644 (file)
@@ -16,6 +16,8 @@
 
 #include <TargetConditionals.h>
 
+#include "test_support.h"
+
 static pthread_t sMainThread;
 static pthread_t sWorker1;
 static pthread_t sWorker2;
@@ -136,8 +138,7 @@ static void* work1(void* arg)
     s.doWork();
 
        if ( pthread_create(&sWorker2, NULL, work2, NULL) != 0 ) {
-        printf("[FAIL] thread-local-destructors, pthread_create\n");
-               exit(0);
+        FAIL("pthread_create");
        }
        void* dummy;
        pthread_join(sWorker2, &dummy);
@@ -157,17 +158,14 @@ void checkMainThreadFinalizer() {
     bool shouldFinalize = false;
 #endif
     if ( sMainThreadFinalized != shouldFinalize )
-        printf("[FAIL] thread-local-destructors, main thread finalisation not as expected\n");
+        FAIL("Main thread finalisation not as expected");
     else if ( sMainThreadFinalized_Another != shouldFinalize )
-        printf("[FAIL] thread-local-destructors, main thread other struct finalisation not as expected\n");
+        FAIL("Main thread other struct finalisation not as expected");
     else
-        printf("[PASS] thread-local-destructors\n");
+        PASS("Success");
 }
 
-int main()
-{
-    printf("[BEGIN] thread-local-destructors\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     sMainThread = pthread_self();
     s.doWork();
 
@@ -178,8 +176,7 @@ int main()
     atexit(&checkMainThreadFinalizer);
 
        if ( pthread_create(&sWorker1, NULL, work1, NULL) != 0 ) {
-        printf("[FAIL] thread-local-destructors, pthread_create\n");
-               return 0;
+        FAIL("pthread_create");
        }
 
        void* dummy;
@@ -187,25 +184,25 @@ int main()
 
     // validate each thread had different addresses for all TLVs
     if ( !sMainThreadInitialized )
-        printf("[FAIL] thread-local-destructors, main thread was not initialized\n");
+        FAIL("Main thread was not initialized");
     else if ( !sWorker1Initialized )
-        printf("[FAIL] thread-local-destructors, thread 1 was not initialized\n");
+        FAIL("Thread 1 was not initialized");
     else if ( !sWorker2Initialized )
-        printf("[FAIL] thread-local-destructors, thread 2 was not initialized\n");
+        FAIL("Thread 2 was not initialized");
     else if ( !sWorker1Finalized )
-        printf("[FAIL] thread-local-destructors, thread 1 was not finalised\n");
+        FAIL("Thread 1 was not finalised");
     else if ( !sWorker2Finalized )
-        printf("[FAIL] thread-local-destructors, thread 2 was not finalised\n");
+        FAIL("Thread 2 was not finalised");
     else if ( !sMainThreadInitialized_Another )
-        printf("[FAIL] thread-local-destructors, main thread other variable was not initialized\n");
+        FAIL("Main thread other variable was not initialized");
     else if ( !sWorker1Initialized_Another )
-        printf("[FAIL] thread-local-destructors, thread 1 other variable was not initialized\n");
+        FAIL("Thread 1 other variable was not initialized");
     else if ( !sWorker2Initialized_Another )
-        printf("[FAIL] thread-local-destructors, thread 2 other variable was not initialized\n");
+        FAIL("Thread 2 other variable was not initialized");
     else if ( !sWorker1Finalized_Another )
-        printf("[FAIL] thread-local-destructors, thread 1 other variable was not finalised\n");
+        FAIL("Thread 1 other variable was not finalised");
     else if ( !sWorker2Finalized_Another )
-        printf("[FAIL] thread-local-destructors, thread 2 other variable was not finalised\n");
+        FAIL("Thread 2 other variable was not finalised");
     else
         passedChecksInMain = true;
 
index d7c73b2f874fc474ed882992ef28ccb6702efb02..272f14224cbd2add708d371213c9cf7310b8d90f 100644 (file)
@@ -14,6 +14,7 @@
 #include <assert.h>
 #include <unistd.h>
 
+#include "test_support.h"
 
 extern __thread int a;
 extern __thread int b;
@@ -61,8 +62,7 @@ static void* work1(void* arg)
     checkValues();
 
        if ( pthread_create(&sWorker2, NULL, work2, NULL) != 0 ) {
-        printf("[FAIL] thread-local-variables, pthread_create\n");
-               exit(0);
+        FAIL("pthread_create");
        }
        void* dummy;
        pthread_join(sWorker2, &dummy);
@@ -83,15 +83,11 @@ static bool someMatch(int* t1, int* t2, int* t3)
     return false;
 }
 
-int main()
-{
-    printf("[BEGIN] thread-local-variables\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     checkValues();
 
        if ( pthread_create(&sWorker1, NULL, work1, NULL) != 0 ) {
-        printf("[FAIL] thread-local-variables, pthread_create\n");
-               return 0;
+        FAIL("pthread_create");
        }
 
     getAddresses(sAddr3);
@@ -101,15 +97,14 @@ int main()
 
     // validate each thread had different addresses for all TLVs
     if ( someMatch(sAddr1[0], sAddr2[0], sAddr3[0]) )
-        printf("[FAIL] thread-local-variables, &a is same across some threads\n");
+        FAIL("&a is same across some threads");
     else if ( someMatch(sAddr1[1], sAddr2[1], sAddr3[1]) )
-        printf("[FAIL] thread-local-variables, &b is same across some threads\n");
+        FAIL("&b is same across some threads");
     else if ( someMatch(sAddr1[2], sAddr2[2], sAddr3[2]) )
-        printf("[FAIL] thread-local-variables, &c is same across some threads\n");
+        FAIL("&c is same across some threads");
     else if ( someMatch(sAddr1[3], sAddr2[3], sAddr3[3]) )
-        printf("[FAIL] thread-local-variables, &d is same across some threads\n");
+        FAIL("&d is same across some threads");
     else
-        printf("[PASS] thread-local-variables\n");
-       return 0;
+        PASS("Success");
 }
 
index a6540f83317e629bd2c06f659298395d6ce26e6d..48da96e2d4d656eeeddaee705bf940a45a7550d3 100644 (file)
@@ -4,7 +4,9 @@
 // BUILD_ONLY: MacOSX
 
 // BUILD:  $CC main.c -o $BUILD_DIR/unix-conformance.exe -D_XOPEN_SOURCE=600
-// BUILD:  $CC main.c -o $TEMP_DIR/scratch.exe -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112
+// 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
 
 // RUN:  ./unix-conformance.exe
 
 #include <string.h> 
 #include <dlfcn.h> 
 
-int main()
-{
-    printf("[BEGIN] unix-conformance.dtest\n");
+#include "test_support.h"
 
-    printf("[PASS] unix-conformance.dtest\n");
-    return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    PASS("Success");
 }
 
index f4fdad16a8a768386818471a499ed293076be740..3f7d1ef860b0a49da7d77a6db4def2a5895f1524 100644 (file)
@@ -10,6 +10,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "test_support.h"
+
 extern void foo();
 
 // We have our own copy of operator new.  Make sure that we call our version, but that foo calls the one from the inserted bar dylib
@@ -32,23 +34,18 @@ void operator delete(void* ptr)
     free(ptr);
 }
 
-int main()
-{
-    printf("[BEGIN] weak-coalesce-inserted-dylibs\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // First make sure we do use our versions of new and delete.
     enableTracking = true;
 
     int* v = new int(1);
     if (!calledMainNew) {
-        printf("[FAIL] weak-coalesce-inserted-dylibs, didn't call executable operator new\n");
-        return 1;
+        FAIL("Didn't call executable operator new");
     }
 
     delete v;
     if (!calledMainDelete) {
-        printf("[FAIL] weak-coalesce-inserted-dylibs, didn't call executable operator delete\n");
-        return 1;
+        FAIL("Didn't call executable operator delete");
     }
 
     // Now make foo do the same and make sure we got the new/delete from bar
@@ -57,17 +54,13 @@ int main()
     foo();
 
     if (calledMainNew) {
-        printf("[FAIL] weak-coalesce-inserted-dylibs, didn't call bar operator new\n");
-        return 1;
+        FAIL("Didn't call bar operator new");
     }
 
     if (calledMainDelete) {
-        printf("[FAIL] weak-coalesce-inserted-dylibs, didn't call bar operator delete\n");
-        return 1;
+        FAIL("Didn't call bar operator delete");
     }
 
-    printf("[PASS] weak-coalesce-inserted-dylibs\n");
-
-    return 0;
+    PASS("Success");
 }
 
index dc98c472f214b1d32255de39ade260dc2efc2df5..dc19b9d14dd5c22a161386364edd2cdce98d816f 100644 (file)
 #include <stdlib.h>
 #include <dlfcn.h>
 
+#include "test_support.h"
+
 extern int foo();
 extern void* fooPtr();
 
 int main()
 {
-    printf("[BEGIN] weak-coalesce-unload\n");
-
        // dlopen foo1 which defines "foo"
        void* handle1 = dlopen(RUN_DIR "/libfoo1.dylib", RTLD_FIRST);
     if ( handle1 == NULL ) {
-        printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo1.dylib", dlerror());
-        return 0;
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo1.dylib", dlerror());
     }
 
     const void* symFoo1 = dlsym(handle1, "foo");
     if ( symFoo1 == NULL ) {
-        printf("[FAIL] dlsym(handle1, foo) failed\n");
-        return 0;
+        FAIL("dlsym(handle1, foo) failed");
     }
 
     const void* symFooPtr1 = dlsym(handle1, "fooPtr");
     if ( symFooPtr1 == NULL ) {
-        printf("[FAIL] dlsym(handle1, fooPtr) failed\n");
-        return 0;
+        FAIL("dlsym(handle1, fooPtr) failed");
     }
     void* fooptr1 = ((__typeof(&fooPtr))symFooPtr1)();
 
     int close1 = dlclose(handle1);
     if ( close1 != 0 ) {
-        printf("[FAIL] dlclose(handle1) failed with: %s\n", dlerror());
-        return 0;
+        FAIL("dlclose(handle1) failed with: %s", dlerror());
     }
 
     // Now dlopen foo2 and get the value it finds for foo
     void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST);
     if ( handle2 == NULL ) {
-        printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo2.dylib", dlerror());
-        return 0;
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo2.dylib", dlerror());
     }
 
     const void* symFoo2 = dlsym(handle2, "foo");
     if ( symFoo2 == NULL ) {
-        printf("[FAIL] dlsym(handle2, foo) failed\n");
-        return 0;
+        FAIL("dlsym(handle2, foo) failed");
     }
 
     const void* symFooPtr2 = dlsym(handle2, "fooPtr");
     if ( symFooPtr2 == NULL ) {
-        printf("[FAIL] dlsym(handle2, fooPtr) failed\n");
-        return 0;
+        FAIL("dlsym(handle2, fooPtr) failed");
     }
     void* fooptr2 = ((__typeof(&fooPtr))symFooPtr2)();
 
     // Don't close foo2, but instead open foo3
     void* handle3 = dlopen(RUN_DIR "/libfoo3.dylib", RTLD_FIRST);
     if ( handle3 == NULL ) {
-        printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo3.dylib", dlerror());
-        return 0;
+        FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo3.dylib", dlerror());
     }
 
     const void* symFoo3 = dlsym(handle3, "foo");
     if ( symFoo3 == NULL ) {
-        printf("[FAIL] dlsym(handle3, foo) failed\n");
-        return 0;
+        FAIL("dlsym(handle3, foo) failed");
     }
 
     const void* symFooPtr3 = dlsym(handle3, "fooPtr");
     if ( symFooPtr3 == NULL ) {
-        printf("[FAIL] dlsym(handle3, fooPtr) failed\n");
-        return 0;
+        FAIL("dlsym(handle3, fooPtr) failed");
     }
     void* fooptr3 = ((__typeof(&fooPtr))symFooPtr3)();
 
     // No-one should point to libfoo1.dylib
     if ( symFoo1 == symFoo2 ) {
-       printf("[FAIL] foo1 == foo2\n");
-        return 0;
+       FAIL("foo1 == foo2");
     }
     if ( symFoo1 == symFoo3 ) {
-       printf("[FAIL] foo1 == foo3\n");
-        return 0;
+       FAIL("foo1 == foo3");
     }
 
     // foo2 and foo3 should be different
     if ( symFoo2 == symFoo3 ) {
-       printf("[FAIL] foo2 != foo3\n");
-        return 0;
+       FAIL("foo2 != foo3");
     }
 
     // But their coalesced values should be the same
     if ( fooptr1 == fooptr2 ) {
-       printf("[FAIL] fooptr1 == fooptr2\n");
-        return 0;
+       FAIL("fooptr1 == fooptr2");
     }
     if ( fooptr2 != fooptr3 ) {
-       printf("[FAIL] fooptr2 != fooptr3\n");
-        return 0;
+       FAIL("fooptr2 != fooptr3");
     }
 
     // foo should return the value from foo2, not the value from foo3
     // Also calling this would explode if we somehow pointed at foo1
     if ( ((__typeof(&foo))fooptr2)() != 2 ) {
-       printf("[FAIL] foo2 != 2\n");
-        return 0;      
+       FAIL("foo2 != 2");
     }
     if ( ((__typeof(&foo))fooptr3)() != 2 ) {
-       printf("[FAIL] foo3 != 2\n");
-        return 0;      
+       FAIL("foo3 != 2");
     }
 
-    printf("[PASS] weak-coalesce-unload\n");
-       return 0;
+    PASS("weak-coalesce-unload");
 }
 
diff --git a/testing/test-cases/weak-coalesce.dtest/Makefile b/testing/test-cases/weak-coalesce.dtest/Makefile
deleted file mode 100644 (file)
index 543a5be..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-##
-# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
-#
-# @APPLE_LICENSE_HEADER_START@
-# 
-# This file contains Original Code and/or Modifications of Original Code
-# as defined in and that are subject to the Apple Public Source License
-# Version 2.0 (the 'License'). You may not use this file except in
-# compliance with the License. Please obtain a copy of the License at
-# http://www.opensource.apple.com/apsl/ and read it before using this
-# file.
-# 
-# The Original Code and all software distributed under the License are
-# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
-# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
-# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
-# Please see the License for the specific language governing rights and
-# limitations under the License.
-# 
-# @APPLE_LICENSE_HEADER_END@
-##
-TESTROOT = ../..
-include ${TESTROOT}/include/common.makefile
-
-all-check: all check
-
-check:
-       ./main
-
-all: main
-
-main: main.c libfoo1.dylib libbase.dylib
-       ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo1.dylib libbase.dylib 
-
-libfoo1.dylib: foo1.c libfoo2.dylib libbase.dylib
-       ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo1.dylib foo1.c libfoo2.dylib libbase.dylib 
-
-libfoo2.dylib: foo2.c libfoo3.dylib libbase.dylib
-       ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo2.dylib foo2.c libfoo3.dylib libbase.dylib 
-
-libfoo3.dylib: foo3.c libbase.dylib
-       ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo3.dylib foo3.c libbase.dylib -prebind -seg1addr 20000 
-
-libbase.dylib: base.c
-       ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libbase.dylib base.c -prebind -seg1addr 10000 
-
-
-
-clean:
-       ${RM} ${RMFLAGS} *~ main  libfoo1.dylib libfoo2.dylib libfoo3.dylib libbase.dylib
-
index b8a04c970888603c4b4e844e905f11251db65ac4..d0618b82cbe0d01a3bbd2414f92a674286f0db7b 100644 (file)
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <stdbool.h>
 
+#include "test_support.h"
 #include "base.h"
 
 static bool         wasProblem        = false;
@@ -10,19 +11,19 @@ static int                  checkInCountCoal1 = 0;
 
 void baseVerifyCoal1(const char* where, int* addr)
 {
-       //fprintf(stderr, "baseVerifyCoal1(%s, %p)\n", where, addr);
-       ++checkInCountCoal1;
-       if ( coal1Where == NULL ) {
-               coal1Where = where;
-               coal1Addr = addr;
-       }
-       else {
-               if ( addr != coal1Addr ) {
-                       fprintf(stderr, "coal1 resolved to different locations.  %p in %s and %p in %s\n", 
-                               coal1Addr, coal1Where, addr, where);
-                       wasProblem = true;
-               }       
-       }
+    LOG("baseVerifyCoal1(%s, %p)", where, addr);
+    ++checkInCountCoal1;
+    if ( coal1Where == NULL ) {
+        coal1Where = where;
+        coal1Addr = addr;
+    }
+    else {
+        if ( addr != coal1Addr ) {
+            LOG("coal1 resolved to different locations.  %p in %s and %p in %s",
+                coal1Addr, coal1Where, addr, where);
+            wasProblem = true;
+        }
+    }
 }
 
 
@@ -32,32 +33,32 @@ static int                  checkInCountCoal2 = 0;
 
 void baseVerifyCoal2(const char* where, int* addr)
 {
-       //fprintf(stderr, "baseVerifyCoal2(%s, %p)\n", where, addr);
-       ++checkInCountCoal2;
-       if ( coal2Where == NULL ) {
-               coal2Where = where;
-               coal2Addr = addr;
-       }
-       else {
-               if ( addr != coal2Addr ) {
-                       fprintf(stderr, "coal2 resolved to different locations.  %p in %s and %p in %s\n", 
-                               coal2Addr, coal2Where, addr, where);
-                       wasProblem = true;
-               }       
-       }
+    LOG("baseVerifyCoal2(%s, %p)", where, addr);
+    ++checkInCountCoal2;
+    if ( coal2Where == NULL ) {
+        coal2Where = where;
+        coal2Addr = addr;
+    }
+    else {
+        if ( addr != coal2Addr ) {
+            LOG("coal2 resolved to different locations.  %p in %s and %p in %s",
+                coal2Addr, coal2Where, addr, where);
+            wasProblem = true;
+        }
+    }
 }
 
 
 
 void baseCheck()
 {
-       if ( wasProblem )
-        printf("[FAIL] weak-coalesce: was problem\n");
+    if ( wasProblem )
+        FAIL("was problem");
     else if ( checkInCountCoal1 != 4 )
-        printf("[FAIL] weak-coalesce: checkInCountCoal1 != 4\n");
+        FAIL("checkInCountCoal1 != 4");
     else if ( checkInCountCoal2 != 4 )
-        printf("[FAIL] weak-coalesce: checkInCountCoal2 != 2\n");
-       else
-        printf("[PASS] weak-coalesce\n");
+        FAIL("checkInCountCoal2 != 2");
+    else
+        PASS("Success");
 }
 
index 0707e0de82986211c1151f351f62a34309cb5314..10bb3ddca0e287f050be6e1ea2b27ddf6b626f0f 100644 (file)
@@ -21,6 +21,9 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 #include <stdio.h>
+
+#include "test_support.h"
+
 #include "base.h"
 
 
@@ -29,11 +32,12 @@ int __attribute__((weak)) coal1 = 1;
 int __attribute__((weak)) coal2 = 1;
 
 
-static __attribute__((constructor)) void myinit() 
+static __attribute__((constructor))
+void myinit(int argc, const char* argv[], const char* envp[], const char* apple[])
 {
-       //fprintf(stderr, "myinit() in foo1.c\n");
-       baseVerifyCoal1("in foo1", &coal1);
-       baseVerifyCoal2("in foo1", &coal2);
+    LOG("myinit() in foo1.c");
+    baseVerifyCoal1("in foo1", &coal1);
+    baseVerifyCoal2("in foo1", &coal2);
 }
 
 
index 74d884b5509215d8a48d74c84fac939c32392bff..37adf6a29625254a7b069cf16ee619011ef54d0a 100644 (file)
@@ -1,14 +1,16 @@
 #include <stdio.h>
 
+#include "test_support.h"
+
 #include "base.h"
 
 int                                                    coal1 = 2;  // note: this is not weak and therefore should win
 int __attribute__((weak))      coal2 = 2;
 
 static __attribute__((constructor))
-void myinit()
+void myinit(int argc, const char* argv[], const char* envp[], const char* apple[])
 {
-       //fprintf(stderr, "myinit() in foo1.c\n");
-       baseVerifyCoal1("in foo2", &coal1);
-       baseVerifyCoal2("in foo2", &coal2);
+    LOG("myinit() in foo1.c");
+    baseVerifyCoal1("in foo2", &coal1);
+    baseVerifyCoal2("in foo2", &coal2);
 }
index e086d5cebc91dcfc66998fcf40c534898ad0a27a..190e2feb363aa7f8b8cba12d9ef25369e895264d 100644 (file)
@@ -1,4 +1,4 @@
-#include <stdio.h>
+#include "test_support.h"
 
 #include "base.h"
 
@@ -6,10 +6,10 @@ int __attribute__((weak))     coal1 = 3;
 int __attribute__((weak))      coal2 = 2;
 
 static __attribute__((constructor))
-void myinit(
+void myinit(int argc, const char* argv[], const char* envp[], const char* apple[])
 {
-       //fprintf(stderr, "myinit() in foo1.c\n");
-       baseVerifyCoal1("in foo3", &coal1);
-       baseVerifyCoal2("in foo3", &coal2);
+    LOG("myinit() in foo1.c");
+    baseVerifyCoal1("in foo3", &coal1);
+    baseVerifyCoal2("in foo3", &coal2);
 }
 
index 4d09091eaa5983618419d67084e3bf9cc9216ed2..3e19d5dc9a375aac920fbe35f7770dd187cb1708 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "test_support.h"
 #include "base.h"
 
-int main()
-{
-    printf("[BEGIN] weak-coalesce\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    baseVerifyCoal1("in main", &coal1);
+    baseVerifyCoal2("in main", &coal2);
 
-       baseVerifyCoal1("in main", &coal1);
-       baseVerifyCoal2("in main", &coal2);
-
-       baseCheck();
-       return 0;
+    baseCheck();
 }
 
diff --git a/testing/test-cases/weak-def-bind-old-format.dtest/bar.c b/testing/test-cases/weak-def-bind-old-format.dtest/bar.c
new file mode 100644 (file)
index 0000000..82f5a4f
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((weak))
+int weakTestValue = 42;
+
+int bar() {
+       return weakTestValue;
+}
\ No newline at end of file
diff --git a/testing/test-cases/weak-def-bind-old-format.dtest/foo.c b/testing/test-cases/weak-def-bind-old-format.dtest/foo.c
new file mode 100644 (file)
index 0000000..172a9c1
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((weak))
+int weakTestValue = 1;
+
+int foo() {
+       return weakTestValue;
+}
\ No newline at end of file
diff --git a/testing/test-cases/weak-def-bind-old-format.dtest/main.c b/testing/test-cases/weak-def-bind-old-format.dtest/main.c
new file mode 100644 (file)
index 0000000..780d087
--- /dev/null
@@ -0,0 +1,29 @@
+// 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
+
+// RUN:  ./weak-def-bind-old-format.exe
+
+
+#include <stdio.h>
+
+#include "test_support.h"
+
+extern int foo();
+extern int bar();
+
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    if ( foo() != 42 ) {
+        FAIL("weak-def-bind-old-format, wrong value");
+       }
+       if ( bar() != 42 ) {
+        FAIL("weak-def-bind-old-format, wrong value");
+       }
+
+    PASS("weak-def-bind-old-format");
+}
+
+
index 7b9b7e67efd1942848a536e6d282bc1933b5cbeb..a7aac22faa0bd6b233772207caffe4562f57f5e1 100644 (file)
@@ -1,24 +1,26 @@
 
 
-// BUILD:  $CC bar.c -dynamiclib -o $TEMP_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib
-// BUILD:  $CC foo.c -dynamiclib -L$TEMP_DIR -weak-lbar -Wl,-reexported_symbols_list,symbols.txt -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib
+// BUILD:  $CC foo.c -dynamiclib -L$BUILD_DIR -weak-lbar -Wl,-reexported_symbols_list,$SRC_DIR/symbols.txt -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
 // BUILD:  $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR
 
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libbar.dylib
+
 // RUN:  ./dylib-re-export.exe
 
 
 #include <stdio.h>
 
+#include "test_support.h"
+
 __attribute__((weak_import))
 extern int bar();
 
-int main()
-{
-    printf("[BEGIN] dylib-re-export\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if ( &bar == 0 )
-        printf("[PASS] dylib-re-export\n");
+        PASS("SUCCESS");
     else
-        printf("[FAIL] dylib-re-export, wrong value\n");
+        FAIL("wrong value");
 
        return 0;
 }