From 16b475fcb248267b8b51f759bc62a49ec2afa88d Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 1 May 2020 18:20:25 +0000 Subject: [PATCH] dyld-750.5.tar.gz --- chroot_util.cpp | 210 + configs/libdyld.xcconfig | 2 + doc/man/man3/dlsym.3 | 1 - dyld.xcodeproj/ContainerizedTestRunner.xctestplan | 25 + dyld.xcodeproj/project.pbxproj | 646 +++- dyld3/APIs.cpp | 56 +- dyld3/Closure.cpp | 12 + dyld3/Closure.h | 11 +- dyld3/ClosureBuilder.cpp | 70 +- dyld3/ClosurePrinter.cpp | 7 + dyld3/ClosureWriter.cpp | 6 + dyld3/ClosureWriter.h | 1 + dyld3/MachOAnalyzer.cpp | 393 +- dyld3/MachOAnalyzer.h | 24 +- dyld3/MachOFile.cpp | 6 + dyld3/MachOLoaded.cpp | 110 +- dyld3/MachOLoaded.h | 8 +- dyld3/SharedCacheRuntime.cpp | 2 +- dyld3/libdyldEntryVector.cpp | 9 +- dyld3/libdyldEntryVector.h | 6 +- dyld3/shared-cache/AdjustDylibSegments.cpp | 218 +- dyld3/shared-cache/BuilderUtils.h | 33 - dyld3/shared-cache/BuilderUtils.mm | 317 -- dyld3/shared-cache/CacheBuilder.cpp | 3994 ++------------------ dyld3/shared-cache/CacheBuilder.h | 144 +- dyld3/shared-cache/DyldSharedCache.cpp | 6 +- dyld3/shared-cache/MachOFileAbstraction.hpp | 8 +- dyld3/shared-cache/Manifest.h | 264 -- dyld3/shared-cache/Manifest.mm | 1227 ------ dyld3/shared-cache/ObjC2Abstraction.hpp | 1 + dyld3/shared-cache/OptimizerBranches.cpp | 24 +- dyld3/shared-cache/OptimizerLinkedit.cpp | 358 +- dyld3/shared-cache/OptimizerObjC.cpp | 25 +- .../{CacheBuilder.cpp => SharedCacheBuilder.cpp} | 3676 +++++++++--------- .../{CacheBuilder.h => SharedCacheBuilder.h} | 234 +- dyld3/shared-cache/dyld_shared_cache_builder.mm | 506 +-- dyld3/shared-cache/dyldinfo.cpp | 542 ++- dyld3/shared-cache/make_ios_dyld_cache.cpp | 358 -- dyld3/shared-cache/mrm_shared_cache_builder.cpp | 50 +- dyld3/shared-cache/mrm_shared_cache_builder.h | 20 +- .../multi_dyld_shared_cache_builder.mm | 282 -- include/mach-o/fixup-chains.h | 48 +- launch-cache/MachOFileAbstraction.hpp | 8 +- launch-cache/dyld_shared_cache_util.cpp | 481 +++ local_test_runner/ContainerizedTestRunner.mm | 199 + local_test_runner/Info.plist | 22 + src/ImageLoader.cpp | 2 - src/ImageLoader.h | 2 +- src/ImageLoaderMachO.cpp | 11 +- src/ImageLoaderMachOCompressed.cpp | 22 +- src/dyld2.cpp | 58 +- src/dyldSyscallInterface.h | 271 +- src/glue.c | 8 +- testing/README.txt | 39 +- testing/build_ninja.py | 512 +++ testing/build_tests.py | 308 -- testing/include/dyld_test.h | 121 - testing/include/test_support.h | 153 +- testing/{nocr => lib}/execserver.defs | 1 + testing/lib/test_support.cpp | 721 ++++ testing/lib/test_support.exp | 6 + testing/nocr/nocr.c | 189 - testing/nocr/nocr.cpp | 35 + .../LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c | 14 +- testing/test-cases/NSAddImage-basic.dtest/main.c | 11 +- testing/test-cases/NSAddImage-fail.dtest/main.c | 35 - testing/test-cases/NSAddImage-fail.dtest/main.cpp | 93 + testing/test-cases/NSAddImage-loaded.dtest/main.c | 14 +- .../NSAddressOfSymbol-basic.dtest/main.c | 19 +- .../main.c | 129 +- .../main.cpp | 150 +- .../main.c | 47 +- .../NSLookupSymbolInImage-basic.dtest/main.c | 16 +- .../main.m | 119 +- .../main.mm | 83 +- .../main.mm | 78 +- .../_dyld_for_each_objc_class.dtest/main.m | 60 +- .../_dyld_for_each_objc_protocol.dtest/main.m | 58 +- .../test-cases/_dyld_get_image_slide.dtest/main.c | 18 +- .../_dyld_get_objc_selector-chained.dtest/main.m | 137 +- .../main.c | 18 +- .../_dyld_get_objc_selector.dtest/main.m | 129 +- .../_dyld_images_for_addresses.dtest/main.c | 51 +- .../_dyld_is_memory_immutable-lock.dtest/main.c | 16 +- .../_dyld_is_memory_immutable.dtest/main.c | 41 +- .../bar.c | 9 +- .../main.cxx | 46 +- .../_dyld_register_for_image_loads.dtest/bar.c | 6 +- .../_dyld_register_for_image_loads.dtest/main.cxx | 48 +- .../main.cxx | 48 +- .../main.c | 10 +- .../amfi-hardened-dlopen-leaf.dtest/main.c | 19 +- testing/test-cases/bind-addend.dtest/main.c | 16 +- testing/test-cases/bind-rebase.dtest/main.c | 9 +- .../chained-fixups-many-binds.dtest/main.c | 10 +- .../crt-old-mac10.5-vars-libSystem.dtest/main.c | 88 + .../crt-old-mac10.6-vars-libSystem.dtest/main.c | 88 + testing/test-cases/crt-vars-libSystem.dtest/main.c | 45 +- testing/test-cases/cwd-relative-load.dtest/main.c | 12 +- .../test-cases/dladdr-basic.dtest/main-no-syms.c | 18 +- testing/test-cases/dladdr-basic.dtest/main.c | 86 +- testing/test-cases/dladdr-dylib.dtest/foo.c | 50 +- testing/test-cases/dladdr-dylib.dtest/main.c | 57 +- .../dlclose-static-terminator.dtest/main.c | 18 +- .../dlopen-DYLD_LIBRARY_PATH.dtest/main.c | 65 +- .../dlopen-RTLD_LOCAL-coalesce.dtest/main.c | 53 +- .../dlopen-RTLD_LOCAL-hides.dtest/main.c | 35 +- .../test-cases/dlopen-RTLD_NODELETE.dtest/main.c | 37 +- .../test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c | 6 +- .../dlopen-RTLD_NOLOAD.dtest/init-main.c | 14 +- testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c | 27 +- testing/test-cases/dlopen-RTLD_NOW.dtest/main.c | 26 +- .../dlopen-atpath-restricted.dtest/main.c | 35 +- testing/test-cases/dlopen-bad-file.dtest/main.c | 83 +- testing/test-cases/dlopen-basic.dtest/main.c | 86 +- testing/test-cases/dlopen-empty-data.dtest/main.c | 13 +- .../test-cases/dlopen-fail-cleanly.dtest/main.c | 24 +- testing/test-cases/dlopen-flat.dtest/main.c | 168 +- .../dlopen-framework-fallback.dtest/main.c | 63 +- testing/test-cases/dlopen-haswell.dtest/main.c | 21 +- testing/test-cases/dlopen-in-init.dtest/foo.c | 17 +- testing/test-cases/dlopen-in-init.dtest/main.c | 10 +- testing/test-cases/dlopen-in-init2.dtest/bar.c | 9 +- testing/test-cases/dlopen-in-init2.dtest/foo.c | 30 +- testing/test-cases/dlopen-in-init2.dtest/main.c | 19 +- testing/test-cases/dlopen-in-init3.dtest/bar.c | 33 +- testing/test-cases/dlopen-in-init3.dtest/foo.c | 8 +- testing/test-cases/dlopen-in-init3.dtest/main.c | 29 +- .../dlopen-indirect-groupNum.dtest/main.c | 61 +- testing/test-cases/dlopen-intertwined.dtest/base.c | 7 +- testing/test-cases/dlopen-intertwined.dtest/main.c | 22 +- .../dlopen-long-error-message.dtest/main.c | 13 +- .../dlopen-prebuilt-dlopen-closure.dtest/main.c | 23 +- testing/test-cases/dlopen-race.dtest/foo.c | 2 +- testing/test-cases/dlopen-race.dtest/main.c | 12 +- testing/test-cases/dlopen-realpath.dtest/main.c | 41 +- testing/test-cases/dlopen-recurse.dtest/bar.c | 2 +- testing/test-cases/dlopen-recurse.dtest/main.c | 10 +- .../test-cases/dlopen-rpath-from-dylib.dtest/bar.c | 2 +- .../dlopen-rpath-from-dylib.dtest/main.c | 17 +- .../dlopen-rpath-from-dylib.dtest/test.c | 11 +- .../test-cases/dlopen-rpath-implicit.dtest/foo.c | 2 +- .../test-cases/dlopen-rpath-implicit.dtest/main.c | 15 +- .../dlopen-rpath-prev-override.dtest/bad.c | 9 +- .../dlopen-rpath-prev-override.dtest/dyn.c | 2 +- .../dlopen-rpath-prev-override.dtest/foo.c | 2 +- .../dlopen-rpath-prev-override.dtest/good.c | 2 +- .../dlopen-rpath-prev-override.dtest/main.c | 19 +- testing/test-cases/dlopen-rpath-prev.dtest/foo.c | 2 +- testing/test-cases/dlopen-rpath-prev.dtest/main.c | 19 +- testing/test-cases/dlopen-rpath-prev.dtest/sub1.c | 2 +- testing/test-cases/dlopen-rpath-prev.dtest/sub2.c | 2 +- testing/test-cases/dlopen-signing.dtest/main.c | 25 +- testing/test-cases/dlopen-symlink.dtest/main.c | 26 +- testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c | 31 +- .../test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c | 27 +- testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c | 27 +- testing/test-cases/dlsym-RTLD_SELF.dtest/main.c | 27 +- testing/test-cases/dlsym-handle.dtest/main.c | 45 +- .../dlsym-in-interposed-malloc.dtest/interposer.c | 11 +- .../dlsym-in-interposed-malloc.dtest/main.c | 10 +- testing/test-cases/dlsym-re-export.dtest/main.c | 46 +- testing/test-cases/dtrace.dtest/main.c | 20 +- .../dyld-insert-library-double.dtest/main.cpp | 15 +- .../dyld-insert-library-rpath.dtest/main.cpp | 23 +- testing/test-cases/dyld_abort_payload.dtest/main.c | 178 - .../test-cases/dyld_abort_payload.dtest/main.cpp | 118 + testing/test-cases/dyld_fork-locks.dest/main.c | 14 +- .../dyld_get_image_versions.dtest/main.c | 15 +- .../test-cases/dyld_get_sdk_version.dtest/bad.txt | 1 - .../test-cases/dyld_get_sdk_version.dtest/main.c | 22 +- .../main.c | 12 +- .../main.c | 12 +- testing/test-cases/dyld_need_closure.dtest/main.c | 16 +- .../dyld_process_info.dtest/linksWithCF.c | 20 +- testing/test-cases/dyld_process_info.dtest/main.c | 107 - .../test-cases/dyld_process_info.dtest/main.cpp | 162 + .../dyld_process_info_notify.dtest/main.c | 377 -- .../dyld_process_info_notify.dtest/main.cpp | 277 ++ .../dyld_process_info_notify.dtest/target.c | 39 +- .../dyld_process_info_unload.dtest/main.c | 139 - .../dyld_process_info_unload.dtest/main.cpp | 60 + .../dyld_process_info_unload.dtest/target.c | 57 +- .../main.c | 76 +- testing/test-cases/dyld_version_spis.dtest/main.c | 48 +- .../dylib-re-export-old-format.dtest/main.c | 18 +- testing/test-cases/dylib-re-export.dtest/main.c | 10 +- testing/test-cases/dylib-static-link.dtest/main.c | 23 + .../test-cases/dylib-static-link.dtest/missing.c | 12 - .../test-cases/dylib-static-link.dtest/present.c | 28 - .../dylib-static-weak-link.dtest/missing.c | 33 +- .../dylib-static-weak-link.dtest/present.c | 20 +- .../env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c | 22 +- .../env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c | 22 +- .../env-DYLD_FORCE_PLATFORM.dtest/main.c | 80 +- .../env-DYLD_FRAMEWORK_PATH.dtest/main.c | 14 +- .../test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c | 27 +- .../env-DYLD_LIBRARY_PATH-cache.dtest/main.c | 15 +- .../test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c | 14 +- .../env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/main.c | 43 +- .../env-DYLD_VERSIONED_LIBRARY_PATH.dtest/main.c | 23 +- .../flat-namespace-absolute-symbol.dtest/main.c | 13 +- testing/test-cases/flat-namespace.dtest/main.c | 11 +- testing/test-cases/image_infos-uuids.dtest/main.c | 35 +- testing/test-cases/init-term-segments.dtest/main.c | 25 +- testing/test-cases/interpose-malloc.dtest/main.c | 24 +- testing/test-cases/interpose-resolver.dtest/main.c | 20 +- .../test-cases/interpose-then-dlopen.dtest/main.c | 45 +- testing/test-cases/interpose-weak.dtest/main.c | 34 +- testing/test-cases/launch-image-cache.dtest/main.c | 80 +- .../test-cases/lazy-symbol-missing.dtest/main.c | 22 +- .../test-cases/lazy-symbol-missing.dtest/runner.c | 101 - .../lazy-symbol-missing.dtest/runner.cpp | 55 + .../test-cases/macOS-cache-rebuild.dtest/main.c | 43 - .../test-cases/macOS-cache-rebuild.dtest/main.cpp | 43 + testing/test-cases/missing-weak-def.dtest/main.c | 19 +- testing/test-cases/no-shared-cache.dtest/main.c | 11 +- testing/test-cases/operator-new.dtest/main.cxx | 20 +- testing/test-cases/read-only-data.dtest/main.c | 31 +- testing/test-cases/restrict-search.dtest/main.c | 16 +- testing/test-cases/rpath-absolute.dtest/main.c | 9 +- testing/test-cases/rpath-relative.dtest/foo.c | 1 + testing/test-cases/rpath-relative.dtest/main.c | 29 + testing/test-cases/rpath-weak-missing.dtest/main.c | 15 +- .../test-cases/shared_cache_iterate.dtest/main.c | 48 +- .../test-cases/shared_cache_optimized.dtest/main.c | 12 +- testing/test-cases/shared_cache_range.dtest/main.c | 31 +- testing/test-cases/static-terminators.dtest/base.c | 11 +- testing/test-cases/static-terminators.dtest/foo.c | 3 +- testing/test-cases/static-terminators.dtest/main.c | 13 +- .../test-cases/symbol-resolver-basic.dtest/main.c | 17 +- .../thread-local-atexit-macOS.dtest/main.cpp | 29 +- .../test-cases/thread-local-atexit.dtest/main.cpp | 31 +- .../test-cases/thread-local-cleanup.dtest/main.c | 19 +- .../thread-local-destructors.dtest/main.cpp | 39 +- .../test-cases/thread-local-variables.dtest/main.c | 23 +- testing/test-cases/unix-conformance.dtest/main.c | 12 +- .../weak-coalesce-inserted-dylibs.dtest/main.cpp | 23 +- .../test-cases/weak-coalesce-unload.dtest/main.c | 58 +- testing/test-cases/weak-coalesce.dtest/Makefile | 52 - testing/test-cases/weak-coalesce.dtest/base.c | 127 +- testing/test-cases/weak-coalesce.dtest/foo1.c | 12 +- testing/test-cases/weak-coalesce.dtest/foo2.c | 10 +- testing/test-cases/weak-coalesce.dtest/foo3.c | 10 +- testing/test-cases/weak-coalesce.dtest/main.c | 13 +- .../weak-def-bind-old-format.dtest/bar.c | 7 + .../weak-def-bind-old-format.dtest/foo.c | 7 + .../weak-def-bind-old-format.dtest/main.c | 29 + .../test-cases/weak-dylib-re-export.dtest/main.c | 54 +- 249 files changed, 9690 insertions(+), 14527 deletions(-) create mode 100644 chroot_util.cpp create mode 100644 dyld.xcodeproj/ContainerizedTestRunner.xctestplan delete mode 100644 dyld3/shared-cache/BuilderUtils.h delete mode 100644 dyld3/shared-cache/BuilderUtils.mm rewrite dyld3/shared-cache/CacheBuilder.cpp (93%) delete mode 100644 dyld3/shared-cache/Manifest.h delete mode 100644 dyld3/shared-cache/Manifest.mm copy dyld3/shared-cache/{CacheBuilder.cpp => SharedCacheBuilder.cpp} (89%) copy dyld3/shared-cache/{CacheBuilder.h => SharedCacheBuilder.h} (53%) delete mode 100644 dyld3/shared-cache/make_ios_dyld_cache.cpp delete mode 100644 dyld3/shared-cache/multi_dyld_shared_cache_builder.mm create mode 100644 local_test_runner/ContainerizedTestRunner.mm create mode 100644 local_test_runner/Info.plist rewrite src/dyldSyscallInterface.h (67%) create mode 100755 testing/build_ninja.py delete mode 100755 testing/build_tests.py delete mode 100644 testing/include/dyld_test.h rewrite testing/include/test_support.h (98%) rename testing/{nocr => lib}/execserver.defs (96%) create mode 100644 testing/lib/test_support.cpp create mode 100644 testing/lib/test_support.exp delete mode 100644 testing/nocr/nocr.c create mode 100644 testing/nocr/nocr.cpp delete mode 100644 testing/test-cases/NSAddImage-fail.dtest/main.c create mode 100644 testing/test-cases/NSAddImage-fail.dtest/main.cpp rewrite testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c (68%) rewrite testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp (75%) copy testing/test-cases/{_dyld_for_each_objc_class-missing-weak.dtest => _dyld_for_each_objc_class-missing-weak-chained.dtest}/main.mm (61%) rewrite testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m (61%) rewrite testing/test-cases/_dyld_get_objc_selector.dtest/main.m (71%) create mode 100644 testing/test-cases/crt-old-mac10.5-vars-libSystem.dtest/main.c create mode 100644 testing/test-cases/crt-old-mac10.6-vars-libSystem.dtest/main.c rewrite testing/test-cases/dlopen-bad-file.dtest/main.c (64%) rewrite testing/test-cases/dlopen-basic.dtest/main.c (68%) rewrite testing/test-cases/dlopen-flat.dtest/main.c (75%) rewrite testing/test-cases/dlopen-framework-fallback.dtest/main.c (69%) delete mode 100644 testing/test-cases/dyld_abort_payload.dtest/main.c create mode 100644 testing/test-cases/dyld_abort_payload.dtest/main.cpp delete mode 100644 testing/test-cases/dyld_get_sdk_version.dtest/bad.txt delete mode 100644 testing/test-cases/dyld_process_info.dtest/main.c create mode 100644 testing/test-cases/dyld_process_info.dtest/main.cpp delete mode 100644 testing/test-cases/dyld_process_info_notify.dtest/main.c create mode 100644 testing/test-cases/dyld_process_info_notify.dtest/main.cpp delete mode 100644 testing/test-cases/dyld_process_info_unload.dtest/main.c create mode 100644 testing/test-cases/dyld_process_info_unload.dtest/main.cpp rewrite testing/test-cases/dyld_process_info_unload.dtest/target.c (77%) create mode 100644 testing/test-cases/dylib-static-link.dtest/main.c delete mode 100644 testing/test-cases/dylib-static-link.dtest/missing.c delete mode 100644 testing/test-cases/dylib-static-link.dtest/present.c rewrite testing/test-cases/dylib-static-weak-link.dtest/missing.c (63%) rewrite testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c (72%) delete mode 100644 testing/test-cases/lazy-symbol-missing.dtest/runner.c create mode 100644 testing/test-cases/lazy-symbol-missing.dtest/runner.cpp delete mode 100644 testing/test-cases/macOS-cache-rebuild.dtest/main.c create mode 100644 testing/test-cases/macOS-cache-rebuild.dtest/main.cpp create mode 100644 testing/test-cases/rpath-relative.dtest/foo.c create mode 100644 testing/test-cases/rpath-relative.dtest/main.c delete mode 100644 testing/test-cases/weak-coalesce.dtest/Makefile rewrite testing/test-cases/weak-coalesce.dtest/base.c (62%) create mode 100644 testing/test-cases/weak-def-bind-old-format.dtest/bar.c create mode 100644 testing/test-cases/weak-def-bind-old-format.dtest/foo.c create mode 100644 testing/test-cases/weak-def-bind-old-format.dtest/main.c rewrite testing/test-cases/weak-dylib-re-export.dtest/main.c (62%) diff --git a/chroot_util.cpp b/chroot_util.cpp new file mode 100644 index 0000000..5c5fcc3 --- /dev/null +++ b/chroot_util.cpp @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "StringUtils.h" +#include "MachOFile.h" + +std::set scanForDependencies(const std::string& path) { + __block std::set 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 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& 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 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 binaries; + std::vector 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 \n"); + exit(-1); + } + if (fallback.length() == 0) { + fprintf(stderr, "No -fallback \n"); + exit(-1); + } + buildChroot(chroot, fallback, binaries); + // insert code here... + return 0; +} diff --git a/configs/libdyld.xcconfig b/configs/libdyld.xcconfig index 69f4fb4..6917378 100644 --- a/configs/libdyld.xcconfig +++ b/configs/libdyld.xcconfig @@ -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 diff --git a/doc/man/man3/dlsym.3 b/doc/man/man3/dlsym.3 index b981ad6..40d1fdf 100644 --- a/doc/man/man3/dlsym.3 +++ b/doc/man/man3/dlsym.3 @@ -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 index 0000000..92776a4 --- /dev/null +++ b/dyld.xcodeproj/ContainerizedTestRunner.xctestplan @@ -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 +} diff --git a/dyld.xcodeproj/project.pbxproj b/dyld.xcodeproj/project.pbxproj index 5bdd191..c8b6788 100644 --- a/dyld.xcodeproj/project.pbxproj +++ b/dyld.xcodeproj/project.pbxproj @@ -56,9 +56,10 @@ 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; @@ -66,43 +67,40 @@ /* 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 */; }; @@ -116,7 +114,6 @@ 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 */; }; @@ -132,9 +129,7 @@ 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 */; }; @@ -194,19 +189,12 @@ 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 */; }; @@ -241,7 +229,7 @@ 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 */; }; @@ -405,6 +393,20 @@ /* 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 */; @@ -468,13 +470,6 @@ 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 */; @@ -515,7 +510,7 @@ ); runOnlyForDeploymentPostprocessing = 1; }; - 377685FE1AC4B27D00026E6C /* CopyFiles */ = { + 3721B6A62321A75B006F6AB7 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; @@ -663,27 +658,32 @@ /* 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 = ""; }; + 3715A302232320BD0059433D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 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 = ""; }; 373C58EF219CE478003442D5 /* BootArgs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = BootArgs.cpp; path = dyld3/BootArgs.cpp; sourceTree = ""; }; 373C58F0219CE478003442D5 /* BootArgs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BootArgs.h; path = dyld3/BootArgs.h; sourceTree = ""; }; + 376AA37C23305CE10070C28C /* ContainerizedTestRunner.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = ContainerizedTestRunner.xctestplan; path = dyld.xcodeproj/ContainerizedTestRunner.xctestplan; sourceTree = ""; }; 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 = ""; }; - 37908A281E3A853E009613FA /* Manifest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Manifest.mm; path = "dyld3/shared-cache/Manifest.mm"; sourceTree = ""; }; - 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 = ""; }; 37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = "dyld3/shared-cache/FileAbstraction.hpp"; sourceTree = ""; }; 37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = "dyld3/shared-cache/MachOFileAbstraction.hpp"; sourceTree = ""; }; - 37908A2C1E3A85A4009613FA /* Manifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Manifest.h; path = "dyld3/shared-cache/Manifest.h"; sourceTree = ""; }; 37908A2D1E3A85A4009613FA /* Trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Trie.hpp; path = "dyld3/shared-cache/Trie.hpp"; sourceTree = ""; }; 37918AC0205890D700F39A77 /* dyld.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = dyld.plist; sourceTree = ""; }; 37918AC42058913800F39A77 /* dyld.codes */ = {isa = PBXFileReference; lastKnownFileType = text; path = dyld.codes; sourceTree = ""; }; - 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BuilderUtils.mm; path = "dyld3/shared-cache/BuilderUtils.mm"; sourceTree = ""; }; - 37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuilderUtils.h; path = "dyld3/shared-cache/BuilderUtils.h"; sourceTree = ""; }; + 37CE9D192321A7EB001FBA91 /* chroot_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = chroot_util.cpp; sourceTree = ""; }; 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tracing.cpp; path = dyld3/Tracing.cpp; sourceTree = ""; }; 37D7DAFF1E96F0ED00D52CEA /* Tracing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Tracing.h; path = dyld3/Tracing.h; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; + C11ECA8F233C307C0011726F /* SharedCacheBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SharedCacheBuilder.h; path = "dyld3/shared-cache/SharedCacheBuilder.h"; sourceTree = ""; }; 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 = ""; }; C18A75F6209A18AC00DC01BB /* JSONReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JSONReader.h; path = dyld3/JSONReader.h; sourceTree = ""; }; @@ -723,7 +723,7 @@ F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; sourceTree = ""; }; F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = ""; }; F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = ""; }; - F93F46511FA420630060D9F9 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; sourceTree = ""; }; + F93F46511FA420630060D9F9 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/lib/execserver.defs; sourceTree = ""; }; F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_sim_shared_cache.xcconfig; sourceTree = ""; }; F94942B21E6796D40019AE08 /* closured.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = closured.1; sourceTree = ""; }; F94C22241E513CA90079E5DD /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; @@ -757,8 +757,7 @@ F97C61A01D9CA6B800A84CD7 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = dyld3/Logging.cpp; sourceTree = ""; usesTabs = 0; }; F97C61A11D9CA6B800A84CD7 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = dyld3/Logging.h; sourceTree = ""; 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 = ""; }; + F97FF35F1C236402000ACDD2 /* nocr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = nocr.cpp; path = testing/nocr/nocr.cpp; sourceTree = ""; }; F97FF3631C237F5C000ACDD2 /* nocr.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = nocr.1; path = ../../../testing/nocr/nocr.1; sourceTree = ""; }; F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.h; sourceTree = ""; }; F98692001DC3EF4800CBEDE6 /* Diagnostics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Diagnostics.h; path = dyld3/Diagnostics.h; sourceTree = ""; usesTabs = 0; }; @@ -797,7 +796,6 @@ F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; sourceTree = ""; }; 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 = ""; }; F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs_macOS.cpp; path = dyld3/APIs_macOS.cpp; sourceTree = ""; }; F9C275581DA71A13007A5D8A /* Loading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Loading.cpp; path = dyld3/Loading.cpp; sourceTree = ""; usesTabs = 0; }; F9C275591DA71A13007A5D8A /* Loading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Loading.h; path = dyld3/Loading.h; sourceTree = ""; usesTabs = 0; }; @@ -852,17 +850,22 @@ 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; }; @@ -915,6 +918,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 37065AA82310889D00A20034 /* libtest_support.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -951,6 +955,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3715A2FF232320BC0059433D /* local_test_runner */ = { + isa = PBXGroup; + children = ( + 3715A300232320BD0059433D /* ContainerizedTestRunner.mm */, + 3715A302232320BD0059433D /* Info.plist */, + ); + path = local_test_runner; + sourceTree = ""; + }; 37918ABF2058908000F39A77 /* tracing */ = { isa = PBXGroup; children = ( @@ -1151,27 +1164,23 @@ 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 */, @@ -1185,11 +1194,13 @@ 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 */, @@ -1209,9 +1220,7 @@ 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 */, @@ -1219,6 +1228,10 @@ 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 = ""; @@ -1226,9 +1239,11 @@ 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 */, @@ -1295,6 +1310,13 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 3721A631230CABAF00594066 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F92C7E0221E59840000D12B5 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1339,22 +1361,56 @@ 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 */ = { @@ -1496,7 +1552,7 @@ ); name = nocr; productName = nocr; - productReference = F97FF3561C23638F000ACDD2 /* nocr */; + productReference = 37065AA72310856E00A20034 /* nocr */; productType = "com.apple.product-type.tool"; }; F98E37762332D048003706B4 /* update_dyld_shared_cache_root_mode_tool */ = { @@ -1622,8 +1678,20 @@ 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; }; @@ -1649,13 +1717,11 @@ }; 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 */; @@ -1669,7 +1735,6 @@ 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 */, @@ -1682,10 +1747,23 @@ 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; @@ -1703,6 +1781,25 @@ 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; @@ -1718,20 +1815,23 @@ 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 */ = { @@ -1797,7 +1897,7 @@ ); 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 */ = { @@ -1955,7 +2055,7 @@ ); 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 */ = { @@ -2001,7 +2101,7 @@ ); 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 */ @@ -2020,46 +2120,45 @@ 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; }; @@ -2081,7 +2180,6 @@ 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 */, @@ -2091,6 +2189,7 @@ 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 */, ); @@ -2145,6 +2244,7 @@ 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 */, @@ -2183,6 +2283,7 @@ 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 */, @@ -2213,8 +2314,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F93F46521FA420850060D9F9 /* execserver.defs in Sources */, - F97FF3611C23640C000ACDD2 /* nocr.c in Sources */, + F97FF3611C23640C000ACDD2 /* nocr.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2237,6 +2337,7 @@ 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 */, @@ -2247,6 +2348,7 @@ 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 */, @@ -2348,6 +2450,16 @@ /* 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 */; @@ -2393,11 +2505,6 @@ 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 */; @@ -2536,114 +2643,232 @@ }; 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; }; @@ -3082,6 +3307,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", + "BUILDING_DYLDINFO=1", "$(inherited)", ); GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -3113,6 +3339,7 @@ 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; @@ -3316,6 +3543,7 @@ MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx.internal; + USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include"; }; name = Debug; }; @@ -3358,6 +3586,7 @@ 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; @@ -3461,6 +3690,7 @@ 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"; @@ -3476,6 +3706,7 @@ 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; @@ -3678,6 +3909,7 @@ ); OTHER_LDFLAGS = ( "-Wl,-no_inits", + "$(USE_CHAINED_FIXUPS)", "-nostdlib", "-lCrashReporterClient", "$(LIBSYSTEM_LIBS)", @@ -3737,6 +3969,7 @@ ); OTHER_LDFLAGS = ( "-Wl,-no_inits", + "$(USE_CHAINED_FIXUPS)", "-nostdlib", "-lCrashReporterClient", "$(LIBSYSTEM_LIBS)", @@ -3819,6 +4052,7 @@ 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"; }; @@ -3924,6 +4158,8 @@ 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; }; @@ -3932,6 +4168,8 @@ 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; }; @@ -3947,11 +4185,29 @@ 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; diff --git a/dyld3/APIs.cpp b/dyld3/APIs.cpp index 476c23a..8ed4a6d 100644 --- a/dyld3/APIs.cpp +++ b/dyld3/APIs.cpp @@ -58,6 +58,7 @@ #include #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 = ""; + allImageInfos->errorTargetDylibPath = ""; + allImageInfos->errorSymbol = ""; + + halt("missing lazy symbol called"); } const char* _dyld_get_objc_selector(const char* selName) diff --git a/dyld3/Closure.cpp b/dyld3/Closure.cpp index 21b0c2e..ef1fffe 100644 --- a/dyld3/Closure.cpp +++ b/dyld3/Closure.cpp @@ -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; diff --git a/dyld3/Closure.h b/dyld3/Closure.h index ab1a15f..ace36a5 100644 --- a/dyld3/Closure.h +++ b/dyld3/Closure.h @@ -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; }; diff --git a/dyld3/ClosureBuilder.cpp b/dyld3/ClosureBuilder.cpp index 10da1bf..2d83fc5 100644 --- a/dyld3/ClosureBuilder.cpp +++ b/dyld3/ClosureBuilder.cpp @@ -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) ) { + // 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) ) { + // 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& 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) { diff --git a/dyld3/ClosurePrinter.cpp b/dyld3/ClosurePrinter.cpp index 48d28b4..9df3d40 100644 --- a/dyld3/ClosurePrinter.cpp +++ b/dyld3/ClosurePrinter.cpp @@ -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 ArrayhasProgramVars(progVarsOffset) ) + root.map["program-vars-offset"].value = hex8(progVarsOffset); + #if 0 diff --git a/dyld3/ClosureWriter.cpp b/dyld3/ClosureWriter.cpp index f4911bc..2501f87 100644 --- a/dyld3/ClosureWriter.cpp +++ b/dyld3/ClosureWriter.cpp @@ -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& hashTable, const Array& hashTableImages) { uint32_t count = (uint32_t)hashTableImages.count(); uint32_t totalSize = (uint32_t)(sizeof(count) + (sizeof(Image::ObjCSelectorImage) * count) + hashTable.count()); diff --git a/dyld3/ClosureWriter.h b/dyld3/ClosureWriter.h index 2774318..d190dee 100644 --- a/dyld3/ClosureWriter.h +++ b/dyld3/ClosureWriter.h @@ -166,6 +166,7 @@ public: void setObjCClassAndProtocolInfo(const Array& classHashTable, const Array& protocolHashTable, const Array& hashTableImages); void setObjCDuplicateClassesInfo(const Array& hashTable); + void setHasProgramVars(uint32_t offset); private: LaunchClosure::Flags& getFlags(); diff --git a/dyld3/MachOAnalyzer.cpp b/dyld3/MachOAnalyzer.cpp index ec3b8d7..8279c5c 100644 --- a/dyld3/MachOAnalyzer.cpp +++ b/dyld3/MachOAnalyzer.cpp @@ -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,9 +1893,159 @@ 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; parseOrgArm64eChainedFixups(diag, @@ -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(); @@ -2992,36 +3222,51 @@ 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 { diff --git a/dyld3/MachOAnalyzer.h b/dyld3/MachOAnalyzer.h index 757ab6e..62a3225 100644 --- a/dyld3/MachOAnalyzer.h +++ b/dyld3/MachOAnalyzer.h @@ -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; diff --git a/dyld3/MachOFile.cpp b/dyld3/MachOFile.cpp index ce8b8f6..7c26440 100644 --- a/dyld3/MachOFile.cpp +++ b/dyld3/MachOFile.cpp @@ -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; } diff --git a/dyld3/MachOLoaded.cpp b/dyld3/MachOLoaded.cpp index 74c845c..35c61ac 100644 --- a/dyld3/MachOLoaded.cpp +++ b/dyld3/MachOLoaded.cpp @@ -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 diff --git a/dyld3/MachOLoaded.h b/dyld3/MachOLoaded.h index 45770aa..e23f890 100644 --- a/dyld3/MachOLoaded.h +++ b/dyld3/MachOLoaded.h @@ -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; diff --git a/dyld3/SharedCacheRuntime.cpp b/dyld3/SharedCacheRuntime.cpp index 0c1a14a..3adaf48 100644 --- a/dyld3/SharedCacheRuntime.cpp +++ b/dyld3/SharedCacheRuntime.cpp @@ -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; diff --git a/dyld3/libdyldEntryVector.cpp b/dyld3/libdyldEntryVector.cpp index 8879971..a35ffdd 100644 --- a/dyld3/libdyldEntryVector.cpp +++ b/dyld3/libdyldEntryVector.cpp @@ -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() diff --git a/dyld3/libdyldEntryVector.h b/dyld3/libdyldEntryVector.h index bcef2fc..d6a8529 100644 --- a/dyld3/libdyldEntryVector.h +++ b/dyld3/libdyldEntryVector.h @@ -33,13 +33,14 @@ 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; diff --git a/dyld3/shared-cache/AdjustDylibSegments.cpp b/dyld3/shared-cache/AdjustDylibSegments.cpp index c01f2c9..22f39d8 100644 --- a/dyld3/shared-cache/AdjustDylibSegments.cpp +++ b/dyld3/shared-cache/AdjustDylibSegments.cpp @@ -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

* _dataInCodeCmd = nullptr; macho_linkedit_data_command

* _exportTrieCmd = nullptr; macho_linkedit_data_command

* _chainedFixupsCmd = nullptr; + uint16_t _chainedFixupsFormat = 0; std::vector _segOrigStartAddresses; std::vector _segSlides; std::vector*> _segCmds; @@ -140,6 +146,7 @@ Adjustor

::Adjustor(DyldSharedCache* cacheBuffer, macho_header

* mh, const s break; case LC_DYLD_CHAINED_FIXUPS: _chainedFixupsCmd = (macho_linkedit_data_command

*)cmd; + _chainedFixupsFormat = dyld3::MachOAnalyzer::chainedPointerFormat((dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff()]); break; case LC_DYLD_EXPORTS_TRIE: _exportTrieCmd = (macho_linkedit_data_command

*)cmd; @@ -178,6 +185,13 @@ void Adjustor

::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

::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 +void Adjustor

::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 +void Adjustor

::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 void Adjustor

::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

::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

::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

::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

::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTr } + +template +void Adjustor

::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 void Adjustor

::adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker) { @@ -1231,7 +1395,7 @@ void Adjustor

::adjustExportsTrie(std::vector& newTrieBytes) void CacheBuilder::adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const { DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; - if ( _archLayout->is64 ) { + if ( _is64 ) { Adjustor> adjustor64(cache, (macho_header>*)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 index dc2c5e3..0000000 --- a/dyld3/shared-cache/BuilderUtils.h +++ /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 index 7d76766..0000000 --- a/dyld3/shared-cache/BuilderUtils.mm +++ /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 -#include -#include -#include -#include // std::setfill, std::setw -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#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 components; - std::vector 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 prodBomPaths; - std::vector 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> 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 temp; - temp.insert(configName); - dedupedCacheSets.push_back(temp); - } - }); - } else { - manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) { - std::set temp; - temp.insert(configName); - dedupedCacheSets.push_back(temp); - }); - } - - std::vector 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 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 warnings; - __block std::set 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; -} diff --git a/dyld3/shared-cache/CacheBuilder.cpp b/dyld3/shared-cache/CacheBuilder.cpp dissimilarity index 93% index 0666b0c..2e0d5e1 100644 --- a/dyld3/shared-cache/CacheBuilder.cpp +++ b/dyld3/shared-cache/CacheBuilder.cpp @@ -1,3657 +1,337 @@ -/* -*- 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#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", - // 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 CacheBuilder::warnings() -{ - return _diagnostics.warnings(); -} - -const std::set 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& dylibs, const std::unordered_map 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& overflowDylibs) -{ - // build a reverse map of all dylib dependencies - __block std::map> references; - std::map>* 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(); - }; - 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 dylibsToSort; - std::vector 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& inputFiles, - std::vector& dylibsToCache, - std::vector& otherDylibs, - std::vector& executables, - std::vector& couldNotLoadFiles) { - - std::map 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& dylibsToCache, - std::vector& otherDylibs, - std::vector& couldNotLoadFiles) -{ - // build map of dylibs - __block std::map knownDylibs; - __block std::map 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> 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 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 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& inputFiles, - std::vector& aliases) { - // First filter down to files which are actually MachO's - CacheInputBuilder cacheInputBuilder(_fileSystem, *_options.archs, _options.platform); - - std::vector dylibsToCache; - std::vector otherDylibs; - std::vector executables; - std::vector 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& dylibs, - const std::vector& otherOsDylibsInput, - const std::vector& osExecutables, - std::vector& aliases) { - - std::vector dylibsToCache; - std::vector otherDylibs; - std::vector 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& dylibs, - const std::vector& otherOsDylibsInput, - const std::vector& osExecutables, - std::vector& aliases) -{ - // 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 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 { - // 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>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount()); -#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k - else if ( _archLayout->pointerDeltaMask == 0xC0000000 ) - writeSlideInfoV4>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount()); -#endif - else - writeSlideInfoV2>(_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 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& 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 §Info, 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 -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 -void CacheBuilder::addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info, - std::vector& pageStarts, std::vector& 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

(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 -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 pageStarts; - std::vector 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

(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 -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 -void CacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info, - std::vector& pageStarts, std::vector& 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

(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 -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 pageStarts; - std::vector pageExtras; - pageStarts.reserve(dataPageCount); - uint8_t* pageContent = _readWriteRegion.buffer; - const bool* bitmapForPage = bitmap; - for (unsigned i=0; i < dataPageCount; ++i) { - addPageStartsV4

(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(_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); - // 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); -} - -const std::string CacheBuilder::cdHashSecond() -{ - return cdHash(_cdHashSecond); -} - -const std::string CacheBuilder::uuid() const -{ - 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& aliases) -{ - typedef dyld3::closure::ClosureBuilder::CachedDylibInfo CachedDylibInfo; - - // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray() - __block std::vector dylibInfos; - __block std::unordered_map 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 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& targets, - const dyld3::Array& 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 dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size()); - const dyld3::Array 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 CacheBuilder::addImageArray() -{ - // build trie of dylib paths - __block std::vector 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 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 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 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& 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& 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 patchImages; - __block std::vector patchExports; - __block std::vector patchLocations; - __block std::vector 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& 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& 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 CacheBuilder::addOtherImageArray(const std::vector& otherDylibsAndBundles, std::vector& 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 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 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& osExecutables) -{ - const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer; - - __block std::vector osExecutablesDiags; - __block std::vector 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 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 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 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 CacheBuilder::writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset)) -{ - const dyld_cache_header* cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer; - const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + cacheHeader->mappingOffset); - assert(_readExecuteRegion.sizeInUse == mappings[0].size); - assert(_readWriteRegion.sizeInUse == mappings[1].size); - assert(_readOnlyRegion.sizeInUse == mappings[2].size); - assert(_readExecuteRegion.cacheFileOffset == mappings[0].fileOffset); - assert(_readWriteRegion.cacheFileOffset == mappings[1].fileOffset); - assert(_readOnlyRegion.cacheFileOffset == mappings[2].fileOffset); - assert(_codeSignatureRegion.sizeInUse == cacheHeader->codeSignatureSize); - assert(cacheHeader->codeSignatureOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse); - cacheSizeCallback(_readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse+_codeSignatureRegion.sizeInUse); - bool fullyWritten = copyCallback(_readExecuteRegion.buffer, _readExecuteRegion.sizeInUse, mappings[0].fileOffset); - fullyWritten &= copyCallback(_readWriteRegion.buffer, _readWriteRegion.sizeInUse, mappings[1].fileOffset); - fullyWritten &= copyCallback(_readOnlyRegion.buffer, _readOnlyRegion.sizeInUse, mappings[2].fileOffset); - if ( _localSymbolsRegion.sizeInUse != 0 ) { - assert(cacheHeader->localSymbolsOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse); - fullyWritten &= copyCallback(_localSymbolsRegion.buffer, _localSymbolsRegion.sizeInUse, cacheHeader->localSymbolsOffset); - } - fullyWritten &= copyCallback(_codeSignatureRegion.buffer, _codeSignatureRegion.sizeInUse, cacheHeader->codeSignatureOffset); - return fullyWritten; -} - - -void CacheBuilder::writeFile(const std::string& path) -{ - std::string pathTemplate = path + "-XXXXXX"; - size_t templateLen = strlen(pathTemplate.c_str())+2; - 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) ) { - // 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; - }; - // 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--" - // 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 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); -} - - -CacheBuilder::ASLR_Tracker::~ASLR_Tracker() -{ - if ( _bitmap != nullptr ) - ::free(_bitmap); -} - -void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart, size_t rwRegionSize) -{ - _pageCount = (unsigned)(rwRegionSize+_pageSize-1)/_pageSize; - _regionStart = (uint8_t*)rwRegionStart; - _endStart = (uint8_t*)rwRegionStart + rwRegionSize; - _bitmap = (bool*)calloc(_pageCount*(_pageSize/4)*sizeof(bool), 1); -} - -void CacheBuilder::ASLR_Tracker::add(void* loc) -{ - if (!_enabled) - return; - uint8_t* p = (uint8_t*)loc; - assert(p >= _regionStart); - assert(p < _endStart); - _bitmap[(p-_regionStart)/4] = true; -} - -void CacheBuilder::ASLR_Tracker::remove(void* loc) -{ - if (!_enabled) - return; - uint8_t* p = (uint8_t*)loc; - assert(p >= _regionStart); - assert(p < _endStart); - _bitmap[(p-_regionStart)/4] = false; -} - -bool CacheBuilder::ASLR_Tracker::has(void* loc) -{ - if (!_enabled) - return true; - uint8_t* p = (uint8_t*)loc; - assert(p >= _regionStart); - assert(p < _endStart); - return _bitmap[(p-_regionStart)/4]; -} - - -//////////////////////////// DylibTextCoalescer //////////////////////////////////// - -bool CacheBuilder::DylibTextCoalescer::sectionWasCoalesced(std::string_view sectionName) const { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - if (it == supportedSections.end()) - return false; - return !it->second->empty(); -} - -CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName) { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - assert(it != supportedSections.end()); - return *it->second; -} - -const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName) const { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - assert(it != supportedSections.end()); - return *it->second; -} - -//////////////////////////// CacheCoalescedText //////////////////////////////////// -const char* CacheBuilder::CacheCoalescedText::SupportedSections[] = { - "__objc_classname", - "__objc_methname", - "__objc_methtype", -}; - -void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAnalyzer *ma, - DylibTextCoalescer& textCoalescer) { - static const bool log = false; - - // We can only remove sections if we know we have split seg v2 to point to it - // Otherwise, a PC relative load in the __TEXT segment wouldn't know how to point to the new strings - // which are no longer in the same segment - uint32_t splitSegSize = 0; - const void* splitSegStart = ma->getSplitSeg(splitSegSize); - if (!splitSegStart) - return; - - if ((*(const uint8_t*)splitSegStart) != DYLD_CACHE_ADJ_V2_FORMAT) - return; - - // We can only remove sections from the end of a segment, so cache them all and walk backwards. - __block std::vector> textSectionInfos; - ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stop) { - if (strcmp(sectInfo.segInfo.segName, "__TEXT") != 0) - return; - assert(!malformedSectionRange); - textSectionInfos.push_back({ sectInfo.sectName, sectInfo }); - }); - - const std::set supportedSections(std::begin(SupportedSections), std::end(SupportedSections)); - int64_t slide = ma->getSlide(); - - for (auto sectionInfoIt = textSectionInfos.rbegin(); sectionInfoIt != textSectionInfos.rend(); ++sectionInfoIt) { - const std::string& sectionName = sectionInfoIt->first; - const dyld3::MachOAnalyzer::SectionInfo& sectInfo = sectionInfoIt->second; - - // If we find a section we can't handle then stop here. Hopefully we coalesced some from the end. - if (supportedSections.find(sectionName) == supportedSections.end()) - break; - - StringSection& cacheStringSection = getSectionData(sectionName); - - DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionStringData = textCoalescer.getSectionCoalescer(sectionName); - - // Walk the strings in this section - const uint8_t* content = (uint8_t*)(sectInfo.sectAddr + slide); - const char* s = (char*)content; - const char* end = s + sectInfo.sectSize; - while ( s < end ) { - std::string_view str = s; - auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize }); - if (itAndInserted.second) { - // If we inserted the string then we need to include it in the total - cacheStringSection.bufferSize += str.size() + 1; - if (log) - printf("Selector: %s -> %s\n", ma->installName(), s); - } else { - // Debugging only. If we didn't include the string then we saved that many bytes - cacheStringSection.savedSpace += str.size() + 1; - } - - // Now keep track of this offset in our source dylib as pointing to this offset - uint32_t sourceSectionOffset = (uint32_t)((uint64_t)s - (uint64_t)content); - uint32_t cacheSectionOffset = itAndInserted.first->second; - sectionStringData[sourceSectionOffset] = cacheSectionOffset; - s += str.size() + 1; - } - } -} - -void CacheBuilder::CacheCoalescedText::clear() { - *this = CacheBuilder::CacheCoalescedText(); -} - - -CacheBuilder::CacheCoalescedText::StringSection& CacheBuilder::CacheCoalescedText::getSectionData(std::string_view sectionName) { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - assert(it != supportedSections.end()); - return *it->second; -} - - -const CacheBuilder::CacheCoalescedText::StringSection& CacheBuilder::CacheCoalescedText::getSectionData(std::string_view sectionName) const { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - assert(it != supportedSections.end()); - return *it->second; -} +/* -*- 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 + +#include "MachOFileAbstraction.hpp" +#include "DyldSharedCache.h" +#include "CacheBuilder.h" +#include "Diagnostics.h" + + +CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem) + : _options(options) + , _fileSystem(fileSystem) + , _fullAllocatedBuffer(0) + , _diagnostics(options.loggingPrefix, options.verbose) + , _allocatedBufferSize(0) +{ +} + + +std::string CacheBuilder::errorMessage() +{ + return _diagnostics.errorMessage(); +} + +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 diags; + diags.resize(_sortedDylibs.size()); + + // Note this cannot to be done in parallel because the LOH Tracker and aslr tracker are not thread safe + for (size_t index = 0; index != _sortedDylibs.size(); ++index) { + const DylibInfo& dylib = _sortedDylibs[index]; + adjustDylibSegments(dylib, diags[index]); + } + for (const Diagnostics& diag : diags) { + if ( diag.hasError() ) { + _diagnostics.error("%s", diag.errorMessage().c_str()); + break; + } + } +} + + +CacheBuilder::ASLR_Tracker::~ASLR_Tracker() +{ + if ( _bitmap != nullptr ) + ::free(_bitmap); +} + +void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart, size_t rwRegionSize) +{ + _pageCount = (unsigned)(rwRegionSize+_pageSize-1)/_pageSize; + _regionStart = (uint8_t*)rwRegionStart; + _regionEnd = (uint8_t*)rwRegionStart + rwRegionSize; + _bitmap = (bool*)calloc(_pageCount*(_pageSize/4)*sizeof(bool), 1); +} + +void CacheBuilder::ASLR_Tracker::add(void* loc) +{ + if (!_enabled) + return; + uint8_t* p = (uint8_t*)loc; + assert(p >= _regionStart); + assert(p < _regionEnd); + _bitmap[(p-_regionStart)/4] = true; +} + +void CacheBuilder::ASLR_Tracker::remove(void* loc) +{ + if (!_enabled) + return; + uint8_t* p = (uint8_t*)loc; + assert(p >= _regionStart); + assert(p < _regionEnd); + _bitmap[(p-_regionStart)/4] = false; +} + +bool CacheBuilder::ASLR_Tracker::has(void* loc) +{ + if (!_enabled) + return true; + uint8_t* p = (uint8_t*)loc; + assert(p >= _regionStart); + 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 //////////////////////////////////// + +bool CacheBuilder::DylibTextCoalescer::sectionWasCoalesced(std::string_view sectionName) const { + if (sectionName.size() > 16) + sectionName = sectionName.substr(0, 16); + std::map supportedSections = { + { "__objc_classname", &objcClassNames }, + { "__objc_methname", &objcMethNames }, + { "__objc_methtype", &objcMethTypes } + }; + auto it = supportedSections.find(sectionName); + if (it == supportedSections.end()) + return false; + return !it->second->empty(); +} + +CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName) { + if (sectionName.size() > 16) + sectionName = sectionName.substr(0, 16); + std::map supportedSections = { + { "__objc_classname", &objcClassNames }, + { "__objc_methname", &objcMethNames }, + { "__objc_methtype", &objcMethTypes } + }; + auto it = supportedSections.find(sectionName); + assert(it != supportedSections.end()); + return *it->second; +} + +const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName) const { + if (sectionName.size() > 16) + sectionName = sectionName.substr(0, 16); + std::map supportedSections = { + { "__objc_classname", &objcClassNames }, + { "__objc_methname", &objcMethNames }, + { "__objc_methtype", &objcMethTypes } + }; + auto it = supportedSections.find(sectionName); + assert(it != supportedSections.end()); + return *it->second; +} + +//////////////////////////// CacheCoalescedText //////////////////////////////////// +const char* CacheBuilder::CacheCoalescedText::SupportedSections[] = { + "__objc_classname", + "__objc_methname", + "__objc_methtype", +}; + +void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAnalyzer *ma, + DylibTextCoalescer& textCoalescer) { + static const bool log = false; + + // We can only remove sections if we know we have split seg v2 to point to it + // Otherwise, a PC relative load in the __TEXT segment wouldn't know how to point to the new strings + // which are no longer in the same segment + uint32_t splitSegSize = 0; + const void* splitSegStart = ma->getSplitSeg(splitSegSize); + if (!splitSegStart) + return; + + if ((*(const uint8_t*)splitSegStart) != DYLD_CACHE_ADJ_V2_FORMAT) + return; + + // We can only remove sections from the end of a segment, so cache them all and walk backwards. + __block std::vector> textSectionInfos; + ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stop) { + if (strcmp(sectInfo.segInfo.segName, "__TEXT") != 0) + return; + assert(!malformedSectionRange); + textSectionInfos.push_back({ sectInfo.sectName, sectInfo }); + }); + + const std::set supportedSections(std::begin(SupportedSections), std::end(SupportedSections)); + int64_t slide = ma->getSlide(); + + for (auto sectionInfoIt = textSectionInfos.rbegin(); sectionInfoIt != textSectionInfos.rend(); ++sectionInfoIt) { + const std::string& sectionName = sectionInfoIt->first; + const dyld3::MachOAnalyzer::SectionInfo& sectInfo = sectionInfoIt->second; + + // If we find a section we can't handle then stop here. Hopefully we coalesced some from the end. + if (supportedSections.find(sectionName) == supportedSections.end()) + break; + + StringSection& cacheStringSection = getSectionData(sectionName); + + DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionStringData = textCoalescer.getSectionCoalescer(sectionName); + + // Walk the strings in this section + const uint8_t* content = (uint8_t*)(sectInfo.sectAddr + slide); + const char* s = (char*)content; + const char* end = s + sectInfo.sectSize; + while ( s < end ) { + std::string_view str = s; + auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize }); + if (itAndInserted.second) { + // If we inserted the string then we need to include it in the total + cacheStringSection.bufferSize += str.size() + 1; + if (log) + printf("Selector: %s -> %s\n", ma->installName(), s); + } else { + // Debugging only. If we didn't include the string then we saved that many bytes + cacheStringSection.savedSpace += str.size() + 1; + } + + // Now keep track of this offset in our source dylib as pointing to this offset + uint32_t sourceSectionOffset = (uint32_t)((uint64_t)s - (uint64_t)content); + uint32_t cacheSectionOffset = itAndInserted.first->second; + sectionStringData[sourceSectionOffset] = cacheSectionOffset; + s += str.size() + 1; + } + } +} + +void CacheBuilder::CacheCoalescedText::clear() { + *this = CacheBuilder::CacheCoalescedText(); +} + + +CacheBuilder::CacheCoalescedText::StringSection& CacheBuilder::CacheCoalescedText::getSectionData(std::string_view sectionName) { + if (sectionName.size() > 16) + sectionName = sectionName.substr(0, 16); + std::map supportedSections = { + { "__objc_classname", &objcClassNames }, + { "__objc_methname", &objcMethNames }, + { "__objc_methtype", &objcMethTypes } + }; + auto it = supportedSections.find(sectionName); + assert(it != supportedSections.end()); + return *it->second; +} + + +const CacheBuilder::CacheCoalescedText::StringSection& CacheBuilder::CacheCoalescedText::getSectionData(std::string_view sectionName) const { + if (sectionName.size() > 16) + sectionName = sectionName.substr(0, 16); + std::map supportedSections = { + { "__objc_classname", &objcClassNames }, + { "__objc_methname", &objcMethNames }, + { "__objc_methtype", &objcMethTypes } + }; + auto it = supportedSections.find(sectionName); + assert(it != supportedSections.end()); + return *it->second; +} diff --git a/dyld3/shared-cache/CacheBuilder.h b/dyld3/shared-cache/CacheBuilder.h index 6d6ef41..8b2bdc3 100644 --- a/dyld3/shared-cache/CacheBuilder.h +++ b/dyld3/shared-cache/CacheBuilder.h @@ -69,30 +69,7 @@ public: InputFile* inputFile; }; - void build(std::vector& inputFiles, - std::vector& aliases); - void build(const std::vector& dylibs, - const std::vector& otherOsDylibsInput, - const std::vector& osExecutables, - std::vector& aliases); - void build(const std::vector& dylibsToCache, - const std::vector& otherOsDylibs, - const std::vector& osExecutables, - std::vector& 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 warnings(); - const std::set 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 _high8Map; + std::unordered_map _authDataMap; + std::unordered_map _rebaseTarget32; + std::unordered_map _rebaseTarget64; }; typedef std::map> 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 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& dylibs, const std::unordered_map sortOrder); - void processSelectorStrings(const std::vector& executables); - void parseCoalescableSegments(); - void assignSegmentAddresses(); - - uint64_t cacheOverflowAmount(); - size_t evictLeafDylibs(uint64_t reductionTarget, std::vector& 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& aliases); - void addOtherImageArray(const std::vector&, std::vector& overflowDylibs); - void addClosures(const std::vector&); - void markPaddingInaccessible(); - - bool writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset)); - - template void writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount); - template bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info); - template void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info, - std::vector& pageStarts, std::vector& pageExtras); - - template void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount); - template bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info); - template void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info, - std::vector& pageStarts, std::vector& 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 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 _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 _sortedDylibs; - InstallNameToMA _installNameToCacheDylib; - std::unordered_map _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 _missingWeakImports; mutable LOH_Tracker _lohTracker; - const dyld3::closure::ImageArray* _imageArray; - uint32_t _sharedStringsPoolVmOffset; - uint8_t _cdHashFirst[20]; - uint8_t _cdHashSecond[20]; - std::unordered_map> _dylibToItsExports; - std::set> _dylibWeakExports; - std::unordered_map> _exportsToUses; - std::unordered_map _exportsToName; }; diff --git a/dyld3/shared-cache/DyldSharedCache.cpp b/dyld3/shared-cache/DyldSharedCache.cpp index 2d39af1..f0b179d 100644 --- a/dyld3/shared-cache/DyldSharedCache.cpp +++ b/dyld3/shared-cache/DyldSharedCache.cpp @@ -41,7 +41,7 @@ #include #include #include -#include "CacheBuilder.h" +#include "SharedCacheBuilder.h" #include "FileUtils.h" #endif @@ -67,8 +67,8 @@ DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions& const std::vector& otherOsDylibs, const std::vector& osExecutables) { - CreateResults results; - CacheBuilder cache(options, fileSystem); + CreateResults results; + SharedCacheBuilder cache(options, fileSystem); if (!cache.errorMessage().empty()) { results.errorMessage = cache.errorMessage(); return results; diff --git a/dyld3/shared-cache/MachOFileAbstraction.hpp b/dyld3/shared-cache/MachOFileAbstraction.hpp index ffa275c..0657d6f 100644 --- a/dyld3/shared-cache/MachOFileAbstraction.hpp +++ b/dyld3/shared-cache/MachOFileAbstraction.hpp @@ -775,8 +775,12 @@ public: } } - if (strcmp(segname, "__DATA") == 0) - return getSection("__DATA_CONST", sectname); + if (strcmp(segname, "__DATA") == 0) { + if (const macho_section

* dataConst = getSection("__DATA_CONST", sectname)) + return dataConst; + if (const macho_section

* 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 index c89d01b..0000000 --- a/dyld3/shared-cache/Manifest.h +++ /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 -#include -#include -#include -#include - -#include -#include - -#include -#include - -#import - -#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 dylibsForCache; - std::vector otherDylibsAndBundles; - std::vector mainExecutables; - std::string outputPath; - std::set 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 sources; - }; - - - struct SegmentInfo { - std::string name; - uint64_t startAddr; - uint64_t endAddr; - }; - - struct CacheInfo { - std::vector regions; - std::string cdHash; - }; - - struct CacheImageInfo { - bool included; - std::string exclusionInfo; - UUID uuid; - std::string installname; - std::vector segments; - CacheImageInfo(void) - : included(true) - { - } - }; - - struct Results { - std::string failure; - std::map dylibs; - std::map bundles; - std::map executables; - - std::set 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 metabomTags; - std::set metabomExcludeTags; - std::set metabomRestrictTags; - std::set restrictedInstallnames; - std::map 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 lambda) const; - }; - - const std::map& projects(); - const Configuration& configuration(const std::string& configuration) const; - void forEachConfiguration(std::function 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& overlays); - - BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set& 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 lambda); - bool filterForConfig(const std::string& configName); - std::set 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 lambda); - - void forEachSymlink(std::string configuration, std::function lambda); - -private: - NSDictionary* _manifestDict; - Diagnostics& _diags; - std::map _uuidMap; - std::map, UUID> _installNameMap; - std::vector> _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 _projects; - std::map _configurations; - std::map> _metabomTagMap; - std::map> _metabomSymlinkTagMap; - std::map> _metabomExcludeTagMap; - std::map> _metabomRestrictedTagMap; - - std::vector dylibsForCache(const std::string& configuration, const std::string& architecture); - std::vector otherDylibsAndBundles(const std::string& configuration, const std::string& architecture); - std::vector 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& 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& architectures); - bool loadParsers(const std::string& pathToMachO, const std::string& runtimePath, const std::set& architectures); - void dedupeDispositions(); - void calculateClosure(const std::string& configuration, const std::string& architecture); - void canonicalizeDylib(const std::string& installname); - template - void canonicalizeDylib(const std::string& installname, const uint8_t* p); - void addImplicitAliases(void); -}; -} - -namespace std { -template <> -struct hash { - 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 index 6d09811..0000000 --- a/dyld3/shared-cache/Manifest.mm +++ /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 -#include -#include -#include -#include -}; - -#include -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "FileAbstraction.hpp" -#include "Trie.hpp" -#include "FileUtils.h" -#include "StringUtils.h" -#include "MachOFile.h" -#include "MachOAnalyzer.h" - -#include -#include - -#include -#include - -#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 -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 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 lambda) const -{ - for (const auto& architecutre : architectures) { - lambda(architecutre.first); - } -} - -bool Manifest::Architecture::operator!=(const Architecture& other) const { return !(*this == other); } - -const std::map& 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 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& 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& architectures) -{ - assert(!_diags.hasError()); - - const MachOFile* mf = reinterpret_cast(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(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& 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()); -} - -// Perform the initialization that populateIt=false omitted. -void Manifest::populate(const std::set& overlays) -{ - auto metabom = MBMetabomOpen(metabomFile().c_str(), false); - auto metabomEnumerator = MBIteratorNewWithPath(metabom, ".", ""); - MBEntry entry; - - // Collect every architecture from the configurations. - std::set 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 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 tagStrs; - std::map 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 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 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& 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 Manifest::dylibsForCache(const std::string& configuration, const std::string& architecture) -{ - std::vector 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 Manifest::otherDylibsAndBundles(const std::string& configuration, const std::string& architecture) -{ - std::vector 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 Manifest::mainExecutables(const std::string& configuration, const std::string& architecture) -{ - std::vector 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 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 Manifest::resultsForConfiguration(const std::string& configName) { - std::set 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::set> 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 newUuids; - std::set processedUuids; - std::set 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 removedUUIDs; - - // Pull in all dependencies - while (!newUuids.empty()) { - std::set 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 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 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 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 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 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 diff --git a/dyld3/shared-cache/ObjC2Abstraction.hpp b/dyld3/shared-cache/ObjC2Abstraction.hpp index 085797c..ef2cd13 100644 --- a/dyld3/shared-cache/ObjC2Abstraction.hpp +++ b/dyld3/shared-cache/ObjC2Abstraction.hpp @@ -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); } }; diff --git a/dyld3/shared-cache/OptimizerBranches.cpp b/dyld3/shared-cache/OptimizerBranches.cpp index 67c31c2..b47a80a 100644 --- a/dyld3/shared-cache/OptimizerBranches.cpp +++ b/dyld3/shared-cache/OptimizerBranches.cpp @@ -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

* _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

::StubOptimizer(const DyldSharedCache* cache, macho_header

* 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

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); const uint32_t cmd_count = mh->ncmds(); macho_segment_command

* segCmd; @@ -415,20 +409,6 @@ void StubOptimizer

::buildStubMap(const std::unordered_set& 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 targetAddrToOptStubAddr; diff --git a/dyld3/shared-cache/OptimizerLinkedit.cpp b/dyld3/shared-cache/OptimizerLinkedit.cpp index 048522b..967f8cb 100644 --- a/dyld3/shared-cache/OptimizerLinkedit.cpp +++ b/dyld3/shared-cache/OptimizerLinkedit.cpp @@ -187,341 +187,6 @@ private: template -class AcceleratorTables { -public: - AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector*>& optimizers); - - uint32_t totalSize() const; - void copyTo(uint8_t* buffer); - -private: - typedef typename P::E E; - - struct NodeChain; - - struct DepNode { - std::vector _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& visitedNodes, const std::vector& from); - }; - - struct NodeChain { - NodeChain* prev; - DepNode* node; - }; - - std::unordered_map*, DepNode> _depDAG; - std::vector _extraInfo; - std::vector _trieBytes; - std::vector _reExportArray; - std::vector _dependencyArray; - std::vector _bottomUpArray; - std::vector _initializers; - std::vector _dofSections; - std::vector _rangeTable; - std::unordered_map*, uint32_t> _machHeaderToImageIndex; - std::unordered_map*> _dylibPathToMachHeader; - std::unordered_map*, LinkeditOptimizer

*> _machHeaderToOptimizer; - dyld_cache_accelerator_info _acceleratorInfoHeader; -}; - - -template -void AcceleratorTables

::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables

::DepNode* target, struct AcceleratorTables

::NodeChain& chain, Diagnostics& diag, - std::unordered_set& visitedNodes, const std::vector::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 -AcceleratorTables

::AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector*>& optimizers) -{ - // build table mapping tables to map between mach_header, index, and optimizer - for ( LinkeditOptimizer

* 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

* mhMapped = (macho_header

*)((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

* op : optimizers) { - _depDAG[op->machHeader()]._installName = op->installName(); - } - for (LinkeditOptimizer

* op : optimizers) { - DepNode& node = _depDAG[op->machHeader()]; - for (const char* depPath : op->getDownwardDependents()) { - macho_header

* 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 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*> sortedMachHeaders; - sortedMachHeaders.reserve(optimizers.size()); - for (LinkeditOptimizer

* 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

* lmh, macho_header

* 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

* 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

* mh : sortedMachHeaders) { - LinkeditOptimizer

* 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

* 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

* mh : sortedMachHeaders) { - LinkeditOptimizer

* 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

* 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

* mh : sortedMachHeaders) { - LinkeditOptimizer

* 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

* mh : sortedMachHeaders) { - LinkeditOptimizer

* 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

* mh : sortedMachHeaders) { - LinkeditOptimizer

* 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

* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"]; - LinkeditOptimizer

* libdyldOp = _machHeaderToOptimizer[libdyldMH]; - uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress(); - - // build range table for fast address->image lookups - for (macho_header

* mh : sortedMachHeaders) { - LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; - unsigned imageIndex = _machHeaderToImageIndex[mh]; - for (const macho_segment_command

* 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 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 -void AcceleratorTables

::DepNode::computeDepth() -{ - if ( _depth != 0 ) - return; - _depth = 1; - for (DepNode* node : _dependents) { - node->computeDepth(); - if ( node->_depth >= _depth ) - _depth = node->_depth + 1; - } -} - -template -uint32_t AcceleratorTables

::totalSize() const -{ - return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset + _acceleratorInfoHeader.rangeTableCount*sizeof(dyld_cache_range_entry), 14); -} - -template -void AcceleratorTables

::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 LinkeditOptimizer

::LinkeditOptimizer(void* cacheBuffer, macho_header

* mh, Diagnostics& diag) : _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diag) { @@ -621,6 +286,7 @@ LinkeditOptimizer

::LinkeditOptimizer(void* cacheBuffer, macho_header

* mh, } } break; + case LC_DYLD_CHAINED_FIXUPS: case LC_SEGMENT_SPLIT_INFO: remove = true; break; @@ -1089,25 +755,6 @@ void LinkeditOptimizer

::mergeLinkedits(CacheBuilder& builder, std::vector 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

::mergeLinkedits(CacheBuilder& builder, std::vectorheader.localSymbolsSize = localsBufferSize; // return buffer of local symbols, caller to free() it builder._localSymbolsRegion.buffer = (uint8_t*)localsBuffer; @@ -1189,7 +837,7 @@ void LinkeditOptimizer

::optimizeLinkedit(CacheBuilder& builder) void CacheBuilder::optimizeLinkedit() { - if ( _archLayout->is64 ) { + if ( _is64 ) { return LinkeditOptimizer>::optimizeLinkedit(*this); } else { diff --git a/dyld3/shared-cache/OptimizerObjC.cpp b/dyld3/shared-cache/OptimizerObjC.cpp index faf2330..1e81c27 100644 --- a/dyld3/shared-cache/OptimizerObjC.cpp +++ b/dyld3/shared-cache/OptimizerObjC.cpp @@ -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/CacheBuilder.cpp b/dyld3/shared-cache/SharedCacheBuilder.cpp similarity index 89% copy from dyld3/shared-cache/CacheBuilder.cpp copy to dyld3/shared-cache/SharedCacheBuilder.cpp index 0666b0c..b1523d1 100644 --- a/dyld3/shared-cache/CacheBuilder.cpp +++ b/dyld3/shared-cache/SharedCacheBuilder.cpp @@ -22,7 +22,6 @@ * @APPLE_LICENSE_HEADER_END@ */ - #include #include #include @@ -31,35 +30,25 @@ #include #include #include -#include #include -#include -#include +#include #include -#include +#include + #include #include #include -#include -#include -#include -#include -#include -#include - -#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 "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" @@ -83,7 +72,7 @@ #define ARMV7K_MAX 0x20000000 #endif -const CacheBuilder::ArchLayout CacheBuilder::_s_archLayout[] = { +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 }, @@ -101,13 +90,13 @@ const CacheBuilder::ArchLayout CacheBuilder::_s_archLayout[] = { // These are dylibs that may be interposed, so stubs calling into them should never be bypassed -const char* const CacheBuilder::_s_neverStubEliminateDylibs[] = { +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 CacheBuilder::_s_neverStubEliminateSymbols[] = { +const char* const SharedCacheBuilder::_s_neverStubEliminateSymbols[] = { "___bzero", "___cxa_atexit", "___cxa_throw", @@ -369,254 +358,12 @@ const char* const CacheBuilder::_s_neverStubEliminateSymbols[] = { }; -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 CacheBuilder::warnings() -{ - return _diagnostics.warnings(); -} - -const std::set 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& dylibs, const std::unordered_map 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& overflowDylibs) -{ - // build a reverse map of all dylib dependencies - __block std::map> references; - std::map>* 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(); - }; - 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 dylibsToSort; - std::vector 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. +// Handles building a list of input files to the SharedCacheBuilder itself. class CacheInputBuilder { public: CacheInputBuilder(const dyld3::closure::FileSystem& fileSystem, @@ -837,6 +584,27 @@ private: 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& dylibsToCache, std::vector& otherDylibs, std::vector& couldNotLoadFiles) @@ -965,8 +733,8 @@ static void verifySelfContained(std::vector& dylibsTo // 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& inputFiles, - std::vector& aliases) { +void SharedCacheBuilder::build(std::vector& inputFiles, + std::vector& aliases) { // First filter down to files which are actually MachO's CacheInputBuilder cacheInputBuilder(_fileSystem, *_options.archs, _options.platform); @@ -1056,10 +824,10 @@ void CacheBuilder::build(std::vector& inputFiles, _fileSystem.unloadFile(loadedMachO.loadedFileInfo); } -void CacheBuilder::build(const std::vector& dylibs, - const std::vector& otherOsDylibsInput, - const std::vector& osExecutables, - std::vector& aliases) { +void SharedCacheBuilder::build(const std::vector& dylibs, + const std::vector& otherOsDylibsInput, + const std::vector& osExecutables, + std::vector& aliases) { std::vector dylibsToCache; std::vector otherDylibs; @@ -1104,10 +872,10 @@ void CacheBuilder::build(const std::vector& dylibs build(dylibsToCache, otherDylibs, executables, aliases); } -void CacheBuilder::build(const std::vector& dylibs, - const std::vector& otherOsDylibsInput, - const std::vector& osExecutables, - std::vector& aliases) +void SharedCacheBuilder::build(const std::vector& dylibs, + const std::vector& otherOsDylibsInput, + const std::vector& osExecutables, + std::vector& aliases) { // error out instead of crash if cache has no dylibs // FIXME: plist should specify required vs optional dylibs @@ -1233,8 +1001,11 @@ void CacheBuilder::build(const std::vector& dylibs, 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 ) { @@ -1284,8 +1055,214 @@ void CacheBuilder::build(const std::vector& dylibs, return; } - -void CacheBuilder::writeCacheHeader() +const std::set SharedCacheBuilder::warnings() +{ + return _diagnostics.warnings(); +} + +const std::set 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& dylibs, const std::unordered_map 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& overflowDylibs) +{ + // build a reverse map of all dylib dependencies + __block std::map> references; + std::map>* 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(); + }; + 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 dylibsToSort; + std::vector 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"; @@ -1328,7 +1305,7 @@ void CacheBuilder::writeCacheHeader() dyldCacheHeader->dylibsExpectedOnDisk = !_options.dylibsRemovedDuringMastering; dyldCacheHeader->simulator = _options.forSimulator; dyldCacheHeader->locallyBuiltCache = _options.isLocallyBuiltCache; - dyldCacheHeader->builtFromChainedFixups= (strcmp(_options.archs->name(), "arm64e") == 0); // FIXME + dyldCacheHeader->builtFromChainedFixups= false; dyldCacheHeader->formatVersion = dyld3::closure::kFormatVersion; dyldCacheHeader->sharedRegionStart = _archLayout->sharedMemoryStart; dyldCacheHeader->sharedRegionSize = _archLayout->sharedMemorySize; @@ -1412,57 +1389,7 @@ void CacheBuilder::writeCacheHeader() 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 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& executables) { +void SharedCacheBuilder::processSelectorStrings(const std::vector& executables) { const bool log = false; _selectorStringsFromExecutables = 0; @@ -1499,7 +1426,7 @@ void CacheBuilder::processSelectorStrings(const std::vector& execut _diagnostics.verbose("Pulled in %lld selector strings from executables\n", _selectorStringsFromExecutables); } -void CacheBuilder::parseCoalescableSegments() { +void SharedCacheBuilder::parseCoalescableSegments() { const bool log = false; for (DylibInfo& dylib : _sortedDylibs) @@ -1514,7 +1441,7 @@ void CacheBuilder::parseCoalescableSegments() { } } -void CacheBuilder::assignSegmentAddresses() +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); @@ -1526,6 +1453,15 @@ void CacheBuilder::assignSegmentAddresses() //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; @@ -1732,7 +1668,7 @@ void CacheBuilder::assignSegmentAddresses() } // layout all __DATA_DIRTY segments, sorted (FIXME) - const size_t dylibCount = _sortedDylibs.size(); + const size_t dylibCount = _sortedDylibs.size(); uint32_t dirtyDataSortIndexes[dylibCount]; for (size_t i=0; i < dylibCount; ++i) dirtyDataSortIndexes[i] = (uint32_t)i; @@ -1813,7 +1749,7 @@ void CacheBuilder::assignSegmentAddresses() slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info2)); slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info3)); slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info4)); - _slideInfoBufferSizeAllocated = align(slideInfoSize + (_readWriteRegion.sizeInUse/4096) * _archLayout->slideInfoBytesPerPage, _archLayout->sharedRegionAlignP2); + _slideInfoBufferSizeAllocated = align(slideInfoSize + (_readWriteRegion.sizeInUse/4096) * _archLayout->slideInfoBytesPerPage + 0x4000, _archLayout->sharedRegionAlignP2); _slideInfoFileOffset = _readOnlyRegion.cacheFileOffset; addr += _slideInfoBufferSizeAllocated; } @@ -1896,1762 +1832,1704 @@ void CacheBuilder::assignSegmentAddresses() } } -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; +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; } - -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; - } - }); - }); +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; } - -template -bool CacheBuilder::makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info2* info) +void SharedCacheBuilder::buildImageArray(std::vector& aliases) { - typedef typename P::uint_t pint_t; + typedef dyld3::closure::ClosureBuilder::CachedDylibInfo CachedDylibInfo; - 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); + // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray() + __block std::vector dylibInfos; + __block std::unordered_map 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; + }); - 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); + // Convert symlinks from STL to simple char pointers. + std::vector dylibAliases; + dylibAliases.reserve(aliases.size()); + for (const auto& alias : aliases) + dylibAliases.push_back({ alias.realPath.c_str(), alias.aliasPath.c_str() }); - // 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; - } + dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers handlers; - // 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); + handlers.chainedBind = ^(dyld3::closure::ImageNum, const dyld3::MachOLoaded* imageLoadAddress, + const dyld_chained_starts_in_image* starts, + const dyld3::Array& targets, + const dyld3::Array& 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"); + } - return true; -} + }); + }; + 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); + }; -template -void CacheBuilder::addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info, - std::vector& pageStarts, std::vector& pageExtras) -{ - typedef typename P::uint_t pint_t; + 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; - 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); + // binder is called a second time for weak_bind info, which we ignore when building cache + const bool weakDefUseAlreadySet = targetInfo.weakBindCoalese && _aslrTracker.has(fixupLoc); - 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

(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; + // 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); } - pageExtras.push_back(i); - } - lastLocationOffset = offset; + 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"); } - } - 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 -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 pageStarts; - std::vector 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

(pageContent, bitmapForPage, info, pageStarts, pageExtras); - if ( _diagnostics.hasError() ) { - return; - } - pageContent += pageSize; - bitmapForPage += (sizeof(bool)*(pageSize/4)); + // 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 dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size()); + const dyld3::Array 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; } - // 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); +static bool operator==(const dyld_cache_patchable_location& a, const dyld_cache_patchable_location& b) { + return a.cacheOffset == b.cacheOffset; } -template -bool CacheBuilder::makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info4* info) +void SharedCacheBuilder::addImageArray() { - typedef typename P::uint_t pint_t; + // build trie of dylib paths + __block std::vector 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 trieBytes; + dylibsTrie.emit(trieBytes); + while ( (trieBytes.size() % 4) != 0 ) + trieBytes.push_back(0); - 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); + // build set of functions to never stub-eliminate because tools may need to override them + std::unordered_set alwaysGeneratePatch; + for (const char* const* p=_s_neverStubEliminateSymbols; *p != nullptr; ++p) + alwaysGeneratePatch.insert(*p); - 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); + // 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; - // 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; - } + 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(); + }; - // 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); + std::set alwaysPatchDylibs; + for (const char* const* d= _s_neverStubEliminateDylibs; *d != nullptr; ++d) + alwaysPatchDylibs.insert(*d); - return true; -} + // 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& dylibExports = _dylibToItsExports[ml]; + // On a customer cache, only store patch locations for interposable dylibs and weak binding + bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName); -template -void CacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info, - std::vector& pageStarts, std::vector& 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); + uint64_t numDylibExports = 0; + for (CacheOffset exportCacheOffset : dylibExports) { + if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset)) + continue; + std::vector& uses = _exportsToUses[exportCacheOffset]; + uses.erase(std::unique(uses.begin(), uses.end()), uses.end()); + numPatchLocations += uses.size(); - 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

(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; + std::string exportName = _exportsToName[exportCacheOffset]; + numPatchExportNameBytes += exportName.size() + 1; } - } - 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 -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; + numPatchExports += numDylibExports; + }); - // 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; + // Now reserve the space + __block std::vector patchImages; + __block std::vector patchExports; + __block std::vector patchLocations; + __block std::vector patchExportNames; - // set page starts and extras for each page - std::vector pageStarts; - std::vector pageExtras; - pageStarts.reserve(dataPageCount); - uint8_t* pageContent = _readWriteRegion.buffer; - const bool* bitmapForPage = bitmap; - for (unsigned i=0; i < dataPageCount; ++i) { - addPageStartsV4

(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 + patchImages.reserve(numPatchImages); + patchExports.reserve(numPatchExports); + patchLocations.reserve(numPatchLocations); + patchExportNames.reserve(numPatchExportNameBytes); -/* -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); - } + // 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& dylibExports = _dylibToItsExports[ml]; - // 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); + // On a customer cache, only store patch locations for interposable dylibs and weak binding + bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName); - _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2); -} + // 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& 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()); -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; + // And add the export name + const std::string& exportName = _exportsToName[exportCacheOffset]; + patchExportNames.insert(patchExportNames.end(), &exportName[0], &exportName[0] + exportName.size() + 1); } - } - if ( lastLoc != nullptr ) { - // mark last one as end of chain - lastLoc->arm64e.rebase.next = 0; - } - return result; -} - + patchImages.push_back(patchImage); + }); -void CacheBuilder::writeSlideInfoV3(const bool bitmap[], unsigned dataPageCount) -{ - const uint32_t pageSize = 4096; + while ( (patchExportNames.size() % 4) != 0 ) + patchExportNames.push_back('\0'); - // 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)); - } + 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(); - // 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"); + // 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; } -} - -void CacheBuilder::fipsSign() -{ - // find libcorecrypto.dylib in cache being built + // copy into cache and update header 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; - } + 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()); - // 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; - } + // 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()); - // 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 + _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size()+patchInfoSize,14); + + // Free the underlying image array buffer + _imageArray->deallocate(); } -void CacheBuilder::codeSign() +void SharedCacheBuilder::addOtherImageArray(const std::vector& otherDylibsAndBundles, std::vector& overflowDylibs) { - uint8_t dscHashType; - uint8_t dscHashSize; - uint32_t dscDigestFormat; - bool agile = false; + 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); - // 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; + // 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); } - std::string cacheIdentifier = "com.apple.dyld.cache."; - cacheIdentifier += _options.archs->name(); - if ( _options.dylibsRemovedDuringMastering ) { - if ( _options.optimizeStubs ) - cacheIdentifier += ".release"; - else - cacheIdentifier += ".development"; + for (const LoadedMachO* dylib : overflowDylibs) { + if (dylib->mappedFile.mh->canHavePrecomputedDlopenClosure(dylib->mappedFile.runtimePath.c_str(), ^(const char*) {}) ) + others.push_back(dylib->loadedFileInfo); } - // get pointers into shared cache buffer - size_t inBbufferSize = _readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse; - const uint16_t pageSize = _archLayout->csPageSize; + // 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; + }); - // 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 + const dyld3::closure::ImageArray* otherImageArray = cb.makeOtherDylibsImageArray(others, (uint32_t)_sortedDylibs.size()); - // 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"); + // build trie of paths + __block std::vector 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 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; } - _codeSignatureRegion.buffer = (uint8_t*)codeSigAlloc; - _codeSignatureRegion.bufferSize = sigSize; - _codeSignatureRegion.sizeInUse = sigSize; - // create overall code signature which is a superblob - CS_SuperBlob* sb = reinterpret_cast(_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); - } + // 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); - // 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; + // Free the underlying buffer + otherImageArray->deallocate(); +} - // 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 +void SharedCacheBuilder::addClosures(const std::vector& osExecutables) +{ + const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer; - // initialize dynamic fields of Code Directory - strcpy((char*)cd + idOffset, cacheIdentifier.c_str()); + __block std::vector osExecutablesDiags; + __block std::vector osExecutablesClosures; + osExecutablesDiags.resize(osExecutables.size()); + osExecutablesClosures.resize(osExecutables.size()); - // 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); + 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; + } + }); - 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)); + std::map 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]; } - }; - - // 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); - // 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; + osExecutablesDiags.clear(); + osExecutablesClosures.clear(); - // Now codesign page 0 again, because we modified it by setting uuid in header - codeSignPage(0); + // preflight space needed + size_t closuresSpace = 0; + for (const auto& entry : closures) { + closuresSpace += entry.second->size(); } - - // hash of entire code directory (cdHash) uses same hash as each page - uint8_t fullCdHash[dscHashSize]; - CCDigest(dscDigestFormat, (const uint8_t*)cd, cdSize, fullCdHash); - // Note: cdHash is defined as first 20 bytes of hash - memcpy(_cdHashFirst, fullCdHash, 20); - if ( agile ) { - uint8_t fullCdHash256[CS_HASH_SIZE_SHA256]; - CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, cd256Size, fullCdHash256); - // Note: cdHash is defined as first 20 bytes of hash, even for sha256 - memcpy(_cdHashSecond, fullCdHash256, 20); + 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; } - else { - memset(_cdHashSecond, 0, 20); + DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; + cache->header.progClosuresAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse; + uint8_t* closuresBase = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse; + std::vector 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 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); } -const bool CacheBuilder::agileSignature() -{ - return _options.codeSigningDigestMode == DyldSharedCache::Agile; -} - -static const std::string cdHash(uint8_t hash[20]) -{ - char buff[48]; - for (int i = 0; i < 20; ++i) - sprintf(&buff[2*i], "%2.2x", hash[i]); - return buff; -} - -const std::string CacheBuilder::cdHashFirst() -{ - return cdHash(_cdHashFirst); -} - -const std::string CacheBuilder::cdHashSecond() -{ - return cdHash(_cdHashSecond); -} -const std::string CacheBuilder::uuid() const +bool SharedCacheBuilder::writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset)) { - 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; + const dyld_cache_header* cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer; + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + cacheHeader->mappingOffset); + assert(_readExecuteRegion.sizeInUse == mappings[0].size); + assert(_readWriteRegion.sizeInUse == mappings[1].size); + assert(_readOnlyRegion.sizeInUse == mappings[2].size); + assert(_readExecuteRegion.cacheFileOffset == mappings[0].fileOffset); + assert(_readWriteRegion.cacheFileOffset == mappings[1].fileOffset); + assert(_readOnlyRegion.cacheFileOffset == mappings[2].fileOffset); + assert(_codeSignatureRegion.sizeInUse == cacheHeader->codeSignatureSize); + assert(cacheHeader->codeSignatureOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse); + cacheSizeCallback(_readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse+_codeSignatureRegion.sizeInUse); + bool fullyWritten = copyCallback(_readExecuteRegion.buffer, _readExecuteRegion.sizeInUse, mappings[0].fileOffset); + fullyWritten &= copyCallback(_readWriteRegion.buffer, _readWriteRegion.sizeInUse, mappings[1].fileOffset); + fullyWritten &= copyCallback(_readOnlyRegion.buffer, _readOnlyRegion.sizeInUse, mappings[2].fileOffset); + if ( _localSymbolsRegion.sizeInUse != 0 ) { + assert(cacheHeader->localSymbolsOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse); + fullyWritten &= copyCallback(_localSymbolsRegion.buffer, _localSymbolsRegion.sizeInUse, cacheHeader->localSymbolsOffset); + } + fullyWritten &= copyCallback(_codeSignatureRegion.buffer, _codeSignatureRegion.sizeInUse, cacheHeader->codeSignatureOffset); + return fullyWritten; } -void CacheBuilder::buildImageArray(std::vector& aliases) +void SharedCacheBuilder::writeFile(const std::string& path) { - typedef dyld3::closure::ClosureBuilder::CachedDylibInfo CachedDylibInfo; - - // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray() - __block std::vector dylibInfos; - __block std::unordered_map 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 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& targets, - const dyld3::Array& 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"); + 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) ) { + // 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; + }; + // 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--" + // 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; }; - - 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); + 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); +} - 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; +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); +} - // binder is called a second time for weak_bind info, which we ignore when building cache - const bool weakDefUseAlreadySet = targetInfo.weakBindCoalese && _aslrTracker.has(fixupLoc); +std::string SharedCacheBuilder::getMapFileBuffer(const std::string& cacheDisposition) const +{ + const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; + return cache->generateJSONMap(cacheDisposition.c_str()); +} - // 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"); - } - }; +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); - // 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 dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size()); - const dyld3::Array 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; - } + // 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); } -static bool operator==(const dyld_cache_patchable_location& a, const dyld_cache_patchable_location& b) { - return a.cacheOffset == b.cacheOffset; + +void SharedCacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) { + for (const DylibInfo& dylibInfo : _sortedDylibs) + callback(dylibInfo.runtimePath); } -void CacheBuilder::addImageArray() + +uint64_t SharedCacheBuilder::pathHash(const char* path) { - // build trie of dylib paths - __block std::vector 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))); + 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; + } }); }); - DylibIndexTrie dylibsTrie(dylibEntrys); - std::vector 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 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; +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; + } - 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(); - }; + // 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; + } - std::set alwaysPatchDylibs; - for (const char* const* d= _s_neverStubEliminateDylibs; *d != nullptr; ++d) - alwaysPatchDylibs.insert(*d); + // 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 +} - // 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& dylibExports = _dylibToItsExports[ml]; +void SharedCacheBuilder::codeSign() +{ + uint8_t dscHashType; + uint8_t dscHashSize; + uint32_t dscDigestFormat; + bool agile = false; - // On a customer cache, only store patch locations for interposable dylibs and weak binding - bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName); + // 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; + } - uint64_t numDylibExports = 0; - for (CacheOffset exportCacheOffset : dylibExports) { - if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset)) - continue; - std::vector& uses = _exportsToUses[exportCacheOffset]; - uses.erase(std::unique(uses.begin(), uses.end()), uses.end()); - numPatchLocations += uses.size(); + 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; - std::string exportName = _exportsToName[exportCacheOffset]; - numPatchExportNameBytes += exportName.size() + 1; - } - numPatchExports += numDylibExports; - }); + const uint16_t pageSize = _archLayout->csPageSize; - // Now reserve the space - __block std::vector patchImages; - __block std::vector patchExports; - __block std::vector patchLocations; - __block std::vector patchExportNames; + // 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 - patchImages.reserve(numPatchImages); - patchExports.reserve(numPatchExports); - patchLocations.reserve(numPatchLocations); - patchExportNames.reserve(numPatchExportNameBytes); + // 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; - // 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& dylibExports = _dylibToItsExports[ml]; + // create overall code signature which is a superblob + CS_SuperBlob* sb = reinterpret_cast(_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); + } - // On a customer cache, only store patch locations for interposable dylibs and weak binding - bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName); + // 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; - // Add the patch image which points in to the exports - dyld_cache_image_patches patchImage; - patchImage.patchExportsStartIndex = (uint32_t)patchExports.size(); - patchImage.patchExportsCount = 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 - // 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& uses = _exportsToUses[exportCacheOffset]; + // 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 - 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); + // initialize dynamic fields of Code Directory + strcpy((char*)cd + idOffset, cacheIdentifier.c_str()); - // Now add the list of locations. - patchLocations.insert(patchLocations.end(), uses.begin(), uses.end()); + // 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); - // 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); - }); + 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. - while ( (patchExportNames.size() % 4) != 0 ) - patchExportNames.push_back('\0'); + 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 - 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(); + // executable segment info + cd256->execSegBase = cd->execSegBase; + cd256->execSegLimit = cd->execSegLimit; + cd256->execSegFlags = cd->execSegFlags; - // 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; + // 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; } - // 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()); + // 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)); - // 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); + // 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; - // Free the underlying image array buffer - _imageArray->deallocate(); -} + 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); -void CacheBuilder::addOtherImageArray(const std::vector& otherDylibsAndBundles, std::vector& 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); + 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); + }); - // 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); - } + // 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); + // 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; - for (const LoadedMachO* dylib : overflowDylibs) { - if (dylib->mappedFile.mh->canHavePrecomputedDlopenClosure(dylib->mappedFile.runtimePath.c_str(), ^(const char*) {}) ) - others.push_back(dylib->loadedFileInfo); + // Now codesign page 0 again, because we modified it by setting uuid in header + codeSignPage(0); } - // 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; - }); + // 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 dyld3::closure::ImageArray* otherImageArray = cb.makeOtherDylibsImageArray(others, (uint32_t)_sortedDylibs.size()); +const bool SharedCacheBuilder::agileSignature() +{ + return _options.codeSigningDigestMode == DyldSharedCache::Agile; +} - // build trie of paths - __block std::vector 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 trieBytes; - dylibsTrie.emit(trieBytes); - while ( (trieBytes.size() % 4) != 0 ) - trieBytes.push_back(0); +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; +} - // 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; - } +const std::string SharedCacheBuilder::cdHashFirst() +{ + return cdHash(_cdHashFirst); +} - // 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); +const std::string SharedCacheBuilder::cdHashSecond() +{ + return cdHash(_cdHashSecond); +} - // Free the underlying buffer - otherImageArray->deallocate(); +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; } -void CacheBuilder::addClosures(const std::vector& osExecutables) + +template +bool SharedCacheBuilder::makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info2* info) { - const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer; + typedef typename P::uint_t pint_t; - __block std::vector osExecutablesDiags; - __block std::vector osExecutablesClosures; - osExecutablesDiags.resize(osExecutables.size()); - osExecutablesClosures.resize(osExecutables.size()); + 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); - 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; + 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); - std::map 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()); + // 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; } - } 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]; } + 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; } - 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 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 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); + // 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; } -bool CacheBuilder::writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset)) +template +void SharedCacheBuilder::addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info, + std::vector& pageStarts, std::vector& pageExtras) { - 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; -} + 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); -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) ) { - // 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); + 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; } - // 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; - }; - // 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 if ( !makeRebaseChainV2

(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; } - else { - _diagnostics.error("unable to fcntl(fd, F_GETPATH) on output file"); - ::close(fd); + } + 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 +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 pageStarts; + std::vector 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

(pageContent, bitmapForPage, info, pageStarts, pageExtras); + if ( _diagnostics.hasError() ) { 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--" - // 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 + 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 +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; } } - else { - _diagnostics.error("could not write file %s", pathTemplateSpace); + 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; } - ::close(fd); - ::unlink(pathTemplateSpace); + i = nonRebaseLocationOffsets[nrIndex]; + ++nrIndex; } - else { - _diagnostics.error("could not open file %s", pathTemplateSpace); + + // 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); -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); + return true; } -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 +template +void SharedCacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info, + std::vector& pageStarts, std::vector& pageExtras) { - const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; - return cache->generateJSONMap(cacheDisposition.c_str()); -} + 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); -void CacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) { - for (const DylibInfo& dylibInfo : _sortedDylibs) - callback(dylibInfo.runtimePath); + 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

(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); } -CacheBuilder::ASLR_Tracker::~ASLR_Tracker() -{ - if ( _bitmap != nullptr ) - ::free(_bitmap); -} -void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart, size_t rwRegionSize) +template +void SharedCacheBuilder::writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount) { - _pageCount = (unsigned)(rwRegionSize+_pageSize-1)/_pageSize; - _regionStart = (uint8_t*)rwRegionStart; - _endStart = (uint8_t*)rwRegionStart + rwRegionSize; - _bitmap = (bool*)calloc(_pageCount*(_pageSize/4)*sizeof(bool), 1); -} + typedef typename P::uint_t pint_t; + typedef typename P::E E; + const uint32_t pageSize = 4096; -void CacheBuilder::ASLR_Tracker::add(void* loc) -{ - if (!_enabled) - return; - uint8_t* p = (uint8_t*)loc; - assert(p >= _regionStart); - assert(p < _endStart); - _bitmap[(p-_regionStart)/4] = true; -} + // 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; -void CacheBuilder::ASLR_Tracker::remove(void* loc) -{ - if (!_enabled) - return; - uint8_t* p = (uint8_t*)loc; - assert(p >= _regionStart); - assert(p < _endStart); - _bitmap[(p-_regionStart)/4] = false; + // set page starts and extras for each page + std::vector pageStarts; + std::vector pageExtras; + pageStarts.reserve(dataPageCount); + uint8_t* pageContent = _readWriteRegion.buffer; + const bool* bitmapForPage = bitmap; + for (unsigned i=0; i < dataPageCount; ++i) { + addPageStartsV4

(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 -bool CacheBuilder::ASLR_Tracker::has(void* loc) +/* +void CacheBuilder::writeSlideInfoV1() { - if (!_enabled) - return true; - uint8_t* p = (uint8_t*)loc; - assert(p >= _regionStart); - assert(p < _endStart); - return _bitmap[(p-_regionStart)/4]; -} - - -//////////////////////////// DylibTextCoalescer //////////////////////////////////// - -bool CacheBuilder::DylibTextCoalescer::sectionWasCoalesced(std::string_view sectionName) const { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - if (it == supportedSections.end()) - return false; - return !it->second->empty(); -} + // 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); + } -CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName) { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - assert(it != supportedSections.end()); - return *it->second; -} + // 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); -const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName) const { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - assert(it != supportedSections.end()); - return *it->second; + _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2); } -//////////////////////////// CacheCoalescedText //////////////////////////////////// -const char* CacheBuilder::CacheCoalescedText::SupportedSections[] = { - "__objc_classname", - "__objc_methname", - "__objc_methtype", -}; - -void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAnalyzer *ma, - DylibTextCoalescer& textCoalescer) { - static const bool log = false; - - // We can only remove sections if we know we have split seg v2 to point to it - // Otherwise, a PC relative load in the __TEXT segment wouldn't know how to point to the new strings - // which are no longer in the same segment - uint32_t splitSegSize = 0; - const void* splitSegStart = ma->getSplitSeg(splitSegSize); - if (!splitSegStart) - return; - - if ((*(const uint8_t*)splitSegStart) != DYLD_CACHE_ADJ_V2_FORMAT) - return; - - // We can only remove sections from the end of a segment, so cache them all and walk backwards. - __block std::vector> textSectionInfos; - ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stop) { - if (strcmp(sectInfo.segInfo.segName, "__TEXT") != 0) - return; - assert(!malformedSectionRange); - textSectionInfos.push_back({ sectInfo.sectName, sectInfo }); - }); - - const std::set supportedSections(std::begin(SupportedSections), std::end(SupportedSections)); - int64_t slide = ma->getSlide(); - - for (auto sectionInfoIt = textSectionInfos.rbegin(); sectionInfoIt != textSectionInfos.rend(); ++sectionInfoIt) { - const std::string& sectionName = sectionInfoIt->first; - const dyld3::MachOAnalyzer::SectionInfo& sectInfo = sectionInfoIt->second; - - // If we find a section we can't handle then stop here. Hopefully we coalesced some from the end. - if (supportedSections.find(sectionName) == supportedSections.end()) - break; - - StringSection& cacheStringSection = getSectionData(sectionName); +*/ - DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionStringData = textCoalescer.getSectionCoalescer(sectionName); - // Walk the strings in this section - const uint8_t* content = (uint8_t*)(sectInfo.sectAddr + slide); - const char* s = (char*)content; - const char* end = s + sectInfo.sectSize; - while ( s < end ) { - std::string_view str = s; - auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize }); - if (itAndInserted.second) { - // If we inserted the string then we need to include it in the total - cacheStringSection.bufferSize += str.size() + 1; - if (log) - printf("Selector: %s -> %s\n", ma->installName(), s); - } else { - // Debugging only. If we didn't include the string then we saved that many bytes - cacheStringSection.savedSpace += str.size() + 1; +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; } - - // Now keep track of this offset in our source dylib as pointing to this offset - uint32_t sourceSectionOffset = (uint32_t)((uint64_t)s - (uint64_t)content); - uint32_t cacheSectionOffset = itAndInserted.first->second; - sectionStringData[sourceSectionOffset] = cacheSectionOffset; - s += str.size() + 1; + 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 CacheBuilder::CacheCoalescedText::clear() { - *this = CacheBuilder::CacheCoalescedText(); -} +void SharedCacheBuilder::writeSlideInfoV3(const bool bitmap[], unsigned dataPageCount) +{ + const uint32_t pageSize = 4096; -CacheBuilder::CacheCoalescedText::StringSection& CacheBuilder::CacheCoalescedText::getSectionData(std::string_view sectionName) { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - assert(it != supportedSections.end()); - return *it->second; -} + // 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)); + } -const CacheBuilder::CacheCoalescedText::StringSection& CacheBuilder::CacheCoalescedText::getSectionData(std::string_view sectionName) const { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - assert(it != supportedSections.end()); - return *it->second; + // 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/CacheBuilder.h b/dyld3/shared-cache/SharedCacheBuilder.h similarity index 53% copy from dyld3/shared-cache/CacheBuilder.h copy to dyld3/shared-cache/SharedCacheBuilder.h index 6d6ef41..97520b1 100644 --- a/dyld3/shared-cache/CacheBuilder.h +++ b/dyld3/shared-cache/SharedCacheBuilder.h @@ -22,52 +22,16 @@ */ -#ifndef CacheBuilder_h -#define CacheBuilder_h +#ifndef SharedCacheBuilder_h +#define SharedCacheBuilder_h -#include -#include -#include -#include -#include - -#include "ClosureFileSystem.h" +#include "CacheBuilder.h" #include "DyldSharedCache.h" -#include "Diagnostics.h" -#include "MachOAnalyzer.h" - - - -template class LinkeditOptimizer; - +#include "ClosureFileSystem.h" -class CacheBuilder { +class SharedCacheBuilder : public CacheBuilder { public: - CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem); - - struct InputFile { - enum State { - Unset, - MustBeIncluded, - MustBeIncludedForDependent, - MustBeExcludedIfUnused - }; - InputFile(const char* path, State state) : path(path), state(state) { } - const char* path; - State state = Unset; - Diagnostics diag; - - bool mustBeIncluded() const { - return (state == MustBeIncluded) || (state == MustBeIncludedForDependent); - } - }; - - // Contains a MachO which has been loaded from the file system and may potentially need to be unloaded later. - struct LoadedMachO { - DyldSharedCache::MappedMachO mappedFile; - dyld3::closure::LoadedFileInfo loadedFileInfo; - InputFile* inputFile; - }; + SharedCacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem); void build(std::vector& inputFiles, std::vector& aliases); @@ -79,12 +43,12 @@ public: const std::vector& otherOsDylibs, const std::vector& osExecutables, std::vector& 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 warnings(); const std::set evictions(); const bool agileSignature(); @@ -92,96 +56,26 @@ public: const std::string cdHashSecond(); const std::string uuid() const; - void forEachCacheDylib(void (^callback)(const std::string& path)); - - struct SegmentMappingInfo { - const void* srcSegment; - const char* segName; - void* dstSegment; - uint64_t dstCacheUnslidAddress; - uint32_t dstCacheFileOffset; - uint32_t dstCacheSegmentSize; - uint32_t dstCacheFileSize; - uint32_t copySegmentSize; - uint32_t srcSegmentIndex; - }; - - struct DylibTextCoalescer { - - typedef std::map DylibSectionOffsetToCacheSectionOffset; - - DylibSectionOffsetToCacheSectionOffset objcClassNames; - DylibSectionOffsetToCacheSectionOffset objcMethNames; - DylibSectionOffsetToCacheSectionOffset objcMethTypes; + void forEachCacheDylib(void (^callback)(const std::string& path)); - bool sectionWasCoalesced(std::string_view sectionName) const; - DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view sectionName); - const DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view sectionName) const; - }; - - struct CacheCoalescedText { - static const char* SupportedSections[3]; - struct StringSection { - // Map from class name strings to offsets in to the class names buffer - std::map stringsToOffsets; - uint8_t* bufferAddr = nullptr; - uint32_t bufferSize = 0; - uint64_t bufferVMAddr = 0; - - // Note this is for debugging only - uint64_t savedSpace = 0; - }; - - StringSection objcClassNames; - StringSection objcMethNames; - StringSection objcMethTypes; - - void parseCoalescableText(const dyld3::MachOAnalyzer* ma, - DylibTextCoalescer& textCoalescer); - void clear(); - - StringSection& getSectionData(std::string_view sectionName); - const StringSection& getSectionData(std::string_view sectionName) const; - }; - - class ASLR_Tracker - { - public: - ~ASLR_Tracker(); - - void setDataRegion(const void* rwRegionStart, size_t rwRegionSize); - void add(void* p); - void remove(void* p); - bool has(void* p); - const bool* bitmap() { return _bitmap; } - unsigned dataPageCount() { return _pageCount; } - void disable() { _enabled = false; }; +private: - private: + void writeSlideInfoV1(); - uint8_t* _regionStart = nullptr; - uint8_t* _endStart = nullptr; - bool* _bitmap = nullptr; - unsigned _pageCount = 0; - unsigned _pageSize = 4096; - bool _enabled = true; - }; + template void writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount); + template bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info); + template void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info, + std::vector& pageStarts, std::vector& pageExtras); - typedef std::map> LOH_Tracker; + 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); - struct Region - { - uint8_t* buffer = nullptr; - uint64_t bufferSize = 0; - uint64_t sizeInUse = 0; - uint64_t unslidLoadAddress = 0; - uint64_t cacheFileOffset = 0; - }; + template void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount); + template bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info); + template void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info, + std::vector& pageStarts, std::vector& pageExtras); -private: - template - friend class LinkeditOptimizer; - struct ArchLayout { uint64_t sharedMemoryStart; @@ -202,21 +96,6 @@ private: static const char* const _s_neverStubEliminateDylibs[]; static const char* const _s_neverStubEliminateSymbols[]; - struct UnmappedRegion - { - uint8_t* buffer = nullptr; - uint64_t bufferSize = 0; - uint64_t sizeInUse = 0; - }; - - struct DylibInfo - { - const LoadedMachO* input; - std::string runtimePath; - std::vector cacheLocation; - DylibTextCoalescer textCoalescer; - }; - void makeSortedDylibs(const std::vector& dylibs, const std::unordered_map sortOrder); void processSelectorStrings(const std::vector& executables); void parseCoalescableSegments(); @@ -229,11 +108,6 @@ private: 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& aliases); @@ -243,22 +117,6 @@ private: bool writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset)); - template void writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount); - template bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info); - template void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info, - std::vector& pageStarts, std::vector& pageExtras); - - template void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount); - template bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info); - template void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info, - std::vector& pageStarts, std::vector& pageExtras); - - // implemented in AdjustDylibSegemnts.cpp - void adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const; - - // implemented in OptimizerLinkedit.cpp - void optimizeLinkedit(); - // implemented in OptimizerObjC.cpp void optimizeObjC(); uint32_t computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount); @@ -271,40 +129,25 @@ private: 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 _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 _sortedDylibs; + 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 _dataDirtySegsOrder; - // Note this is mutable as the only parallel writes to it are done atomically to the bitmap - mutable ASLR_Tracker _aslrTracker; std::map _missingWeakImports; - mutable LOH_Tracker _lohTracker; - const dyld3::closure::ImageArray* _imageArray; - uint32_t _sharedStringsPoolVmOffset; + const dyld3::closure::ImageArray* _imageArray = nullptr; uint8_t _cdHashFirst[20]; uint8_t _cdHashSecond[20]; + bool _someDylibsUsedChainedFixups = false; std::unordered_map> _dylibToItsExports; std::set> _dylibWeakExports; std::unordered_map> _exportsToUses; @@ -313,13 +156,4 @@ private: - -inline uint64_t align(uint64_t addr, uint8_t p2) -{ - uint64_t mask = (1 << p2); - return (addr + mask - 1) & (-mask); -} - - - -#endif /* CacheBuilder_h */ +#endif /* SharedCacheBuilder_h */ diff --git a/dyld3/shared-cache/dyld_shared_cache_builder.mm b/dyld3/shared-cache/dyld_shared_cache_builder.mm index 4841dc7..0bbcc1e 100644 --- a/dyld3/shared-cache/dyld_shared_cache_builder.mm +++ b/dyld3/shared-cache/dyld_shared_cache_builder.mm @@ -59,11 +59,13 @@ #include #include +#include +#include +#include +#include -#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& 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& 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 *cachePaths = (std::set*)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 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>& inputFiles, std::vector>& mappedFiles, const std::set& baselineCacheFiles) { @@ -430,11 +343,11 @@ static void unloadMRMFiles(std::vector>& 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 likelyConfigs{}; - std::vector 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 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 requiredBinaries = { - "/usr/lib/libSystem.B.dylib" - }; - - // Get the file data for every MachO in the BOM. - __block dyld3::json::Node filesNode; - __block std::vector> 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 baselineDylibs = manifest.resultsForConfiguration(options.configuration); - - std::set newDylibs; - std::map 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 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 pathsNotInRoots; - for (std::pair 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 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* 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* 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* dylibsReasonsMissingFromNewCache = [NSMutableArray array]; - for (std::pair 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 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]; diff --git a/dyld3/shared-cache/dyldinfo.cpp b/dyld3/shared-cache/dyldinfo.cpp index 0e1817e..70ab985 100644 --- a/dyld3/shared-cache/dyldinfo.cpp +++ b/dyld3/shared-cache/dyldinfo.cpp @@ -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@ * @@ -43,10 +43,6 @@ #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 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 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 index 20f58c3..0000000 --- a/dyld3/shared-cache/make_ios_dyld_cache.cpp +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "MachOFile.h" -#include "FileUtils.h" -#include "StringUtils.h" -#include "DyldSharedCache.h" - - - -struct MappedMachOsByCategory -{ - std::string archName; - std::vector dylibsForCache; - std::vector otherDylibsAndBundles; - std::vector 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& 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& 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& paths, dyld3::Platform platform, std::vector& 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 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 option to specify directory in which to write cache file(s)\n"); - return 1; - } - - if ( rootPath.empty() ) { - fprintf(stderr, "missing -runtime_dir 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 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 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); -} - diff --git a/dyld3/shared-cache/mrm_shared_cache_builder.cpp b/dyld3/shared-cache/mrm_shared_cache_builder.cpp index 4453375..c013393 100644 --- a/dyld3/shared-cache/mrm_shared_cache_builder.cpp +++ b/dyld3/shared-cache/mrm_shared_cache_builder.cpp @@ -23,7 +23,7 @@ */ #include "mrm_shared_cache_builder.h" -#include "CacheBuilder.h" +#include "SharedCacheBuilder.h" #include "ClosureFileSystem.h" #include "FileUtils.h" #include @@ -194,7 +194,7 @@ private: struct BuildInstance { std::unique_ptr options; - std::unique_ptr builder; + std::unique_ptr builder; std::vector inputFiles; std::vector errors; std::vector 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(*options.get(), builder->fileSystem); + auto cacheBuilder = std::make_unique(*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) { diff --git a/dyld3/shared-cache/mrm_shared_cache_builder.h b/dyld3/shared-cache/mrm_shared_cache_builder.h index 33c4a69..983b1ca 100644 --- a/dyld3/shared-cache/mrm_shared_cache_builder.h +++ b/dyld3/shared-cache/mrm_shared_cache_builder.h @@ -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 index 49e58a2..0000000 --- a/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm +++ /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 -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#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 uuids; - std::map 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; -} diff --git a/include/mach-o/fixup-chains.h b/include/mach-o/fixup-chains.h index 907a0e7..2aba7bf 100644 --- a/include/mach-o/fixup-chains.h +++ b/include/mach-o/fixup-chains.h @@ -23,7 +23,7 @@ */ #ifndef __MACH_O_FIXUP_CHAINS__ -#define __MACH_O_FIXUP_CHAINS__ +#define __MACH_O_FIXUP_CHAINS__ 4 #include @@ -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 diff --git a/launch-cache/MachOFileAbstraction.hpp b/launch-cache/MachOFileAbstraction.hpp index 6c95c13..7168951 100644 --- a/launch-cache/MachOFileAbstraction.hpp +++ b/launch-cache/MachOFileAbstraction.hpp @@ -780,8 +780,12 @@ public: } } - if (strcmp(segname, "__DATA") == 0) - return getSection("__DATA_CONST", sectname); + if (strcmp(segname, "__DATA") == 0) { + if (const macho_section

* dataConst = getSection("__DATA_CONST", sectname)) + return dataConst; + if (const macho_section

* dataDirty = getSection("__DATA_DIRTY", sectname)) + return dataDirty; + } return NULL; } diff --git a/launch-cache/dyld_shared_cache_util.cpp b/launch-cache/dyld_shared_cache_util.cpp index 5f817ad..a4a81b2 100644 --- a/launch-cache/dyld_shared_cache_util.cpp +++ b/launch-cache/dyld_shared_cache_util.cpp @@ -44,8 +44,12 @@ #include #include #include +#include +#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 classVMAddrToName; + __block std::map 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 onDiskChainedFixupBindTargets; + __block std::map onDiskClassVMAddrToName; + __block std::map 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() : 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 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() : 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 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() : 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 classes = getClasses(ma); + std::optional 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 classes = getClasses(ma); + std::optional 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 classes = getClasses(ma); + std::optional 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 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 index 0000000..14c7533 --- /dev/null +++ b/local_test_runner/ContainerizedTestRunner.mm @@ -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 +#include +#include + +#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 *)testInvocations { + static NSArray *invocations = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSMutableArray *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 index 0000000..64d65ca --- /dev/null +++ b/local_test_runner/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/src/ImageLoader.cpp b/src/ImageLoader.cpp index 839ab99..dbadce3 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -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 diff --git a/src/ImageLoader.h b/src/ImageLoader.h index df2f44b..70107dc 100644 --- a/src/ImageLoader.h +++ b/src/ImageLoader.h @@ -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 diff --git a/src/ImageLoaderMachO.cpp b/src/ImageLoaderMachO.cpp index 7067ed8..1768df8 100644 --- a/src/ImageLoaderMachO.cpp +++ b/src/ImageLoaderMachO.cpp @@ -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); diff --git a/src/ImageLoaderMachOCompressed.cpp b/src/ImageLoaderMachOCompressed.cpp index 2b85fb8..1e01ed1 100644 --- a/src/ImageLoaderMachOCompressed.cpp +++ b/src/ImageLoaderMachOCompressed.cpp @@ -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: diff --git a/src/dyld2.cpp b/src/dyld2.cpp index 40fbd5b..18aec93 100644 --- a/src/dyld2.cpp +++ b/src/dyld2.cpp @@ -3374,19 +3374,36 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const 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::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); diff --git a/src/dyldSyscallInterface.h b/src/dyldSyscallInterface.h dissimilarity index 67% index c8ea4df..ad689a2 100644 --- a/src/dyldSyscallInterface.h +++ b/src/dyldSyscallInterface.h @@ -1,120 +1,151 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2004-2013 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -#ifndef __DYLD_SYSCALL_HELPERS__ -#define __DYLD_SYSCALL_HELPERS__ - -#include -#include - -#if __cplusplus -namespace dyld { -#endif - - // - // This file contains the table of function pointers the host dyld supplies - // to the iOS simulator 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); - 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); - bool (*OSAtomicCompareAndSwapPtrBarrier)(void* old, void* nw, void * volatile *value); - void (*OSMemoryBarrier)(void); - void* (*getProcessInfo)(void); // returns dyld_all_image_infos*; - int* (*errnoAddress)(void); - uint64_t (*mach_absolute_time)(void); - // Added in version 2 - kern_return_t (*thread_switch)(mach_port_name_t, int, mach_msg_timeout_t); - // Added in version 3 - DIR* (*opendir)(const char* path); - int (*readdir_r)(DIR* dirp, struct dirent* entry, struct dirent **result); - int (*closedir)(DIR* dirp); - // 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); - // 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); - // 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); - }; - extern const struct SyscallHelpers* gSyscallHelpers; - - -#if __cplusplus -} -#endif - -#endif +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __DYLD_SYSCALL_HELPERS__ +#define __DYLD_SYSCALL_HELPERS__ + +#include + +#include +#if __has_include() +#include +#else +__BEGIN_DECLS +extern int amfi_check_dyld_policy_self(uint64_t input_flags, uint64_t* output_flags); +__END_DECLS +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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. + // + struct SyscallHelpers + { + uintptr_t version; + 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); + 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); + DYLD_SYSCALL_VTABLE_ENTRY(mach_absolute_time); + // Added in version 2 + DYLD_SYSCALL_VTABLE_ENTRY(thread_switch); + // Added in version 3 + 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 + 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 + DYLD_SYSCALL_VTABLE_ENTRY(abort_with_payload); + // Add in version 7 + 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; + + +#if __cplusplus +} +#endif + +#endif diff --git a/src/glue.c b/src/glue.c index 92875de..8b02b74 100644 --- a/src/glue.c +++ b/src/glue.c @@ -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); diff --git a/testing/README.txt b/testing/README.txt index bb821c9..89ee46f 100755 --- a/testing/README.txt +++ b/testing/README.txt @@ -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 index 0000000..ea958c9 --- /dev/null +++ b/testing/build_ninja.py @@ -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 index f33149f..0000000 --- a/testing/build_tests.py +++ /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 index fe2227b..0000000 --- a/testing/include/dyld_test.h +++ /dev/null @@ -1,121 +0,0 @@ -#include - -#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); \ -}) diff --git a/testing/include/test_support.h b/testing/include/test_support.h dissimilarity index 98% index 5f3fc83..d054154 100644 --- a/testing/include/test_support.h +++ b/testing/include/test_support.h @@ -1,74 +1,79 @@ -#include -#include -#include -#include -#include - -#if __LP64__ -extern struct mach_header_64 __dso_handle; -#else -extern struct mach_header __dso_handle; -#endif - -static bool sIsATTY = false; -static const char * sTestName = NULL; -static uint64_t sTestCount = 0; - -__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, 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"); -} - -__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"); -} +#ifndef __DYLD_TEST_SUPPORT_H__ +#define __DYLD_TEST_SUPPORT_H__ 1 + +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include +#include +#include + +#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 __cplusplus +extern "C" { +#endif /* __cplusplus */ +__attribute__((format(printf, 3, 4))) +__attribute__ ((noreturn)) +extern void _PASS(const char* file, unsigned line, const char* format, ...); + +__attribute__((format(printf, 3, 4))) +__attribute__ ((noreturn)) +extern void _FAIL(const char* file, unsigned line, const char* format, ...); + +__attribute__((format(printf, 3, 4))) +extern void _LOG(const char* file, unsigned line, const char* format, ...); + +extern void _TIMEOUT(const char* file, unsigned line, uint64_t seconds); +#if __cplusplus +}; +#endif /* __cplusplus */ + +#endif /* __DYLD_TEST_SUPPORT_H__ */ diff --git a/testing/nocr/execserver.defs b/testing/lib/execserver.defs similarity index 96% rename from testing/nocr/execserver.defs rename to testing/lib/execserver.defs index e528df4..69468ba 100644 --- a/testing/nocr/execserver.defs +++ b/testing/lib/execserver.defs @@ -1 +1,2 @@ #include + diff --git a/testing/lib/test_support.cpp b/testing/lib/test_support.cpp new file mode 100644 index 0000000..a98dc22 --- /dev/null +++ b/testing/lib/test_support.cpp @@ -0,0 +1,721 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +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 + void withLock(F f) { + os_unfair_lock_lock(&_lock); + f(); + os_unfair_lock_unlock(&_lock); + } +private: + os_unfair_lock _lock; +}; + +template +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 +inline void GrowableArray::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 +inline void GrowableArray::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>& 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 logs; + const char *testName; + bool logImmediate; + bool logOnSuccess; + bool checkForLeaks; + OutputStyle output; + GrowableArray> 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 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 +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& 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*)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>& 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(""); + printf(""); + printf(""); + printf(""); + printf("PASS"); + printf(""); + printf(""); + } + }); + 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(""); + printf(""); + printf(""); + printf(""); + printf("PASS"); + printf("FILE%s", file); + printf("LINE%u", line); + vasprintf(&buffer, format, args); + printf("INFO%s", buffer); + free(buffer); + printf(""); + printf(""); + } + }); + 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 index 0000000..83a6dbf --- /dev/null +++ b/testing/lib/test_support.exp @@ -0,0 +1,6 @@ +__PASS +__FAIL +__LOG +__TIMEOUT +__ZN8_process* +__process* diff --git a/testing/nocr/nocr.c b/testing/nocr/nocr.c deleted file mode 100644 index 74c1e88..0000000 --- a/testing/nocr/nocr.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "execserverServer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -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 index 0000000..dd08d40 --- /dev/null +++ b/testing/nocr/nocr.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + +#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(); +} diff --git a/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c b/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c index 4c58f79..4d9ff34 100644 --- a/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c +++ b/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c @@ -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 @@ -14,21 +13,18 @@ #include #include +#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"); } diff --git a/testing/test-cases/NSAddImage-basic.dtest/main.c b/testing/test-cases/NSAddImage-basic.dtest/main.c index 5c870c4..f259721 100644 --- a/testing/test-cases/NSAddImage-basic.dtest/main.c +++ b/testing/test-cases/NSAddImage-basic.dtest/main.c @@ -11,18 +11,15 @@ #include #include +#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 index 74bf245..0000000 --- a/testing/test-cases/NSAddImage-fail.dtest/main.c +++ /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 -#include -#include -#include - - -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 index 0000000..0dada7d --- /dev/null +++ b/testing/test-cases/NSAddImage-fail.dtest/main.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} + diff --git a/testing/test-cases/NSAddImage-loaded.dtest/main.c b/testing/test-cases/NSAddImage-loaded.dtest/main.c index db7ca4b..0a24cb8 100644 --- a/testing/test-cases/NSAddImage-loaded.dtest/main.c +++ b/testing/test-cases/NSAddImage-loaded.dtest/main.c @@ -11,23 +11,19 @@ #include #include +#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"); } diff --git a/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c b/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c index ddd9c74..70fa3f0 100644 --- a/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c +++ b/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c @@ -11,30 +11,25 @@ #include #include -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"); } diff --git a/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c b/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c dissimilarity index 68% index 49341b3..b6eb2e0 100644 --- a/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c +++ b/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c @@ -1,70 +1,59 @@ -// BUILD_ONLY: MacOSX - -// BUILD: $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromFile-basic.exe -Wno-deprecated-declarations -// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle - -// RUN: ./NSCreateObjectFileImageFromFile-basic.exe $RUN_DIR/foo.bundle - - -#include -#include -#include -#include - - -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n"); - - const char* path = argv[1]; - - NSObjectFileImage ofi; - if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) { - printf("[FAIL] NSCreateObjectFileImageFromFile failed\n"); - return 0; - } - - NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); - if ( mod == NULL ) { - printf("[FAIL] NSLinkModule failed\n"); - return 0; - } - - NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle"); - if ( sym == NULL ) { - printf("[FAIL] NSLookupSymbolInModule failed\n"); - return 0; - } - - void* func = NSAddressOfSymbol(sym); - if ( func == NULL ) { - printf("[FAIL] NSAddressOfSymbol failed\n"); - return 0; - } - - Dl_info info; - if ( dladdr(func, &info) == 0 ) { - printf("[FAIL] dladdr(&p, xx) failed"); - return 0; - } - //printf("_fooInBundle found in %s\n", info.dli_fname); - - if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { - printf("[FAIL] NSUnLinkModule failed\n"); - return 0; - } - - if ( dladdr(func, &info) != 0 ) { - printf("[FAIL] dladdr(&p, xx) found but should not have\n"); - return 0; - } - - if ( !NSDestroyObjectFileImage(ofi) ) { - printf("[FAIL] NSDestroyObjectFileImage failed\n"); - return 0; - } - - printf("[PASS] NSCreateObjectFileImageFromFile-basic\n"); - return 0; -} - +// BUILD_ONLY: MacOSX + +// BUILD: $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromFile-basic.exe -Wno-deprecated-declarations +// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle + +// RUN: ./NSCreateObjectFileImageFromFile-basic.exe $RUN_DIR/foo.bundle + + +#include +#include +#include +#include + +#include "test_support.h" + +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 ) { + 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 ) { + 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"); + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + } + + PASS("Success"); +} + diff --git a/testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp b/testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp dissimilarity index 75% index 54c676d..d7e6f98 100644 --- a/testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp +++ b/testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp @@ -1,80 +1,70 @@ -// BUILD_ONLY: MacOSX - -// BUILD: $CXX main.cpp -o $BUILD_DIR/NSCreateObjectFileImageFromFile-stress.exe -Wno-deprecated-declarations -// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle - -// RUN: ./NSCreateObjectFileImageFromFile-stress.exe $RUN_DIR/foo.bundle - - -#include -#include -#include -#include -#include - -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n"); - - const char* path = argv[1]; - - std::vector 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; -} - +// BUILD_ONLY: MacOSX + +// BUILD: $CXX main.cpp -o $BUILD_DIR/NSCreateObjectFileImageFromFile-stress.exe -Wno-deprecated-declarations +// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle + +// RUN: ./NSCreateObjectFileImageFromFile-stress.exe $RUN_DIR/foo.bundle + + +#include +#include +#include +#include +#include + +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + const char* path = argv[1]; + + std::vector ofis; + for (unsigned i = 0; i != 32; ++i) { + 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"); +} + diff --git a/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c b/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c index ca41cb7..b9944ff 100644 --- a/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c +++ b/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c @@ -18,91 +18,77 @@ #include #include +#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"); } diff --git a/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c b/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c index 1adab58..7cc1d46 100644 --- a/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c +++ b/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c @@ -10,30 +10,26 @@ #include #include -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"); } diff --git a/testing/test-cases/_dyld_for_each_objc_class-duplicates.dtest/main.m b/testing/test-cases/_dyld_for_each_objc_class-duplicates.dtest/main.m index 33f114b..d9ebb73 100644 --- a/testing/test-cases/_dyld_for_each_objc_class-duplicates.dtest/main.m +++ b/testing/test-cases/_dyld_for_each_objc_class-duplicates.dtest/main.m @@ -11,6 +11,8 @@ #import +#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"); } diff --git a/testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm b/testing/test-cases/_dyld_for_each_objc_class-missing-weak-chained.dtest/main.mm similarity index 61% copy from testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm copy to testing/test-cases/_dyld_for_each_objc_class-missing-weak-chained.dtest/main.mm index ce4b38e..cd029cb 100644 --- a/testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm +++ b/testing/test-cases/_dyld_for_each_objc_class-missing-weak-chained.dtest/main.mm @@ -1,10 +1,13 @@ +// BUILD_ONLY: MacOSX -// BUILD: $CC missing.m -dynamiclib -o $TEMP_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 missing.m -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc -Wl,-fixup_chains +// BUILD: $CC lib1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc -Wl,-fixup_chains +// BUILD: $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains +// BUILD: $CXX main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak-chained.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains -lc++ -// RUN: ./_dyld_for_each_objc_class-missing-weak.exe +// BUILD: $SKIP_INSTALL $BUILD_DIR/libmissing.dylib + +// RUN: ./_dyld_for_each_objc_class-missing-weak-chained.exe // liblinked2 weakly links libmissing and so has a missing weak superclass. // This means we should not see classes from liblinked be returned from _dyld_for_each_objc_class @@ -17,6 +20,8 @@ #import +#include "test_support.h" + // All the libraries have a copy of DyldClass @interface DyldClass : NSObject @end @@ -42,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 @@ -63,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\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 +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: 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 +121,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 +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: 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 +166,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 +180,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"); } diff --git a/testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm b/testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm index ce4b38e..4752f00 100644 --- a/testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm +++ b/testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm @@ -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 +#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"); } diff --git a/testing/test-cases/_dyld_for_each_objc_class.dtest/main.m b/testing/test-cases/_dyld_for_each_objc_class.dtest/main.m index 517dfde..083e94d 100644 --- a/testing/test-cases/_dyld_for_each_objc_class.dtest/main.m +++ b/testing/test-cases/_dyld_for_each_objc_class.dtest/main.m @@ -14,6 +14,8 @@ #import +#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"); } diff --git a/testing/test-cases/_dyld_for_each_objc_protocol.dtest/main.m b/testing/test-cases/_dyld_for_each_objc_protocol.dtest/main.m index eea4adf..d80aea2 100644 --- a/testing/test-cases/_dyld_for_each_objc_protocol.dtest/main.m +++ b/testing/test-cases/_dyld_for_each_objc_protocol.dtest/main.m @@ -17,6 +17,8 @@ #include #include +#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"); } diff --git a/testing/test-cases/_dyld_get_image_slide.dtest/main.c b/testing/test-cases/_dyld_get_image_slide.dtest/main.c index e4f1b9d..ec3e198 100644 --- a/testing/test-cases/_dyld_get_image_slide.dtest/main.c +++ b/testing/test-cases/_dyld_get_image_slide.dtest/main.c @@ -10,11 +10,9 @@ #include #include +#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*)¬Magic); 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"); } diff --git a/testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m b/testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m dissimilarity index 61% index 2d19a99..5a7a735 100644 --- a/testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m +++ b/testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m @@ -1,72 +1,65 @@ -// BUILD_ONLY: MacOSX - -// BUILD: $CC main.m -o $BUILD_DIR/_dyld_get_objc_selector-chained.exe -lobjc -Wl,-fixup_chains - -// RUN: ./_dyld_get_objc_selector-chained.exe - -#include - -#import - -@interface DyldClass : NSObject -@end - -@implementation DyldClass --(void) dyldClassFoo { - -} -+(void) dyldClassFoo { - -} -@end - -@interface DyldMainClass : NSObject -@end - -@implementation DyldMainClass --(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-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; -} +// BUILD_ONLY: MacOSX + +// BUILD: $CC main.m -o $BUILD_DIR/_dyld_get_objc_selector-chained.exe -lobjc -Wl,-fixup_chains + +// RUN: ./_dyld_get_objc_selector-chained.exe + +#include + +#import + +#include "test_support.h" + +@interface DyldClass : NSObject +@end + +@implementation DyldClass +-(void) dyldClassFoo { + +} ++(void) dyldClassFoo { + +} +@end + +@interface DyldMainClass : NSObject +@end + +@implementation DyldMainClass +-(void) dyldMainClassFoo { + +} +-(void) dyldMainClassFoo2 { + +} +@end + +extern id objc_getClass(const char *name); + +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"); +} diff --git a/testing/test-cases/_dyld_get_objc_selector-shared-cache.dtest/main.c b/testing/test-cases/_dyld_get_objc_selector-shared-cache.dtest/main.c index d11e442..e698fbf 100644 --- a/testing/test-cases/_dyld_get_objc_selector-shared-cache.dtest/main.c +++ b/testing/test-cases/_dyld_get_objc_selector-shared-cache.dtest/main.c @@ -9,11 +9,9 @@ #include #include +#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"); } diff --git a/testing/test-cases/_dyld_get_objc_selector.dtest/main.m b/testing/test-cases/_dyld_get_objc_selector.dtest/main.m dissimilarity index 71% index 9845721..8375d3d 100644 --- a/testing/test-cases/_dyld_get_objc_selector.dtest/main.m +++ b/testing/test-cases/_dyld_get_objc_selector.dtest/main.m @@ -1,71 +1,58 @@ - -// BUILD: $CC main.m -o $BUILD_DIR/_dyld_get_objc_selector.exe -lobjc - -// RUN: ./_dyld_get_objc_selector.exe - -#include - -#import - -@interface DyldClass : NSObject -@end - -@implementation DyldClass --(void) dyldClassFoo { - -} -+(void) dyldClassFoo { - -} -@end - -@interface DyldMainClass : NSObject -@end - -@implementation DyldMainClass --(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 + +// BUILD: $CC main.m -o $BUILD_DIR/_dyld_get_objc_selector.exe -lobjc + +// RUN: ./_dyld_get_objc_selector.exe + +#include + +#import + +#include "test_support.h" + +@interface DyldClass : NSObject +@end + +@implementation DyldClass +-(void) dyldClassFoo {} ++(void) dyldClassFoo {} +@end + +@interface DyldMainClass : NSObject +@end + +@implementation DyldMainClass +-(void) dyldMainClassFoo {} +-(void) dyldMainClassFoo2 {} +@end + +extern id objc_getClass(const char *name); + +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; +} diff --git a/testing/test-cases/_dyld_images_for_addresses.dtest/main.c b/testing/test-cases/_dyld_images_for_addresses.dtest/main.c index c44d1f2..5ffcf9a 100644 --- a/testing/test-cases/_dyld_images_for_addresses.dtest/main.c +++ b/testing/test-cases/_dyld_images_for_addresses.dtest/main.c @@ -10,6 +10,8 @@ #include #include +#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"); } diff --git a/testing/test-cases/_dyld_is_memory_immutable-lock.dtest/main.c b/testing/test-cases/_dyld_is_memory_immutable-lock.dtest/main.c index e35435f..85c3be6 100644 --- a/testing/test-cases/_dyld_is_memory_immutable-lock.dtest/main.c +++ b/testing/test-cases/_dyld_is_memory_immutable-lock.dtest/main.c @@ -11,6 +11,7 @@ #include #include +#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(¬ify); 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"); } diff --git a/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c b/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c index a162cfa..94e4a0a 100644 --- a/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c +++ b/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c @@ -16,6 +16,8 @@ #include #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"); } diff --git a/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/bar.c b/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/bar.c index e3b00af..29b6521 100644 --- a/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/bar.c +++ b/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/bar.c @@ -1,13 +1,12 @@ -#include -#include #include +#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()); } } diff --git a/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/main.cxx b/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/main.cxx index c56126c..0cc7e5c 100644 --- a/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/main.cxx +++ b/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/main.cxx @@ -16,6 +16,8 @@ #include +#include "test_support.h" + extern "C" void foo(); extern mach_header __dso_handle; @@ -24,13 +26,13 @@ static std::unordered_set 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(¬ify); // 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"); } diff --git a/testing/test-cases/_dyld_register_for_image_loads.dtest/bar.c b/testing/test-cases/_dyld_register_for_image_loads.dtest/bar.c index e3b00af..5613fa7 100644 --- a/testing/test-cases/_dyld_register_for_image_loads.dtest/bar.c +++ b/testing/test-cases/_dyld_register_for_image_loads.dtest/bar.c @@ -3,11 +3,13 @@ #include #include +#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); } } diff --git a/testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx b/testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx index c4ec3b1..ed5a9c6 100644 --- a/testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx +++ b/testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx @@ -17,6 +17,8 @@ #include +#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(¬ify); // 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"); } diff --git a/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx b/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx index 905a153..eb1a05e 100644 --- a/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx +++ b/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx @@ -12,66 +12,54 @@ #include +#include "test_support.h" + extern mach_header __dso_handle; static std::unordered_set 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(¬ify); // 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"); } diff --git a/testing/test-cases/_dyld_shared_cache_is_locally_built.dtest/main.c b/testing/test-cases/_dyld_shared_cache_is_locally_built.dtest/main.c index 8672bb8..838dda8 100644 --- a/testing/test-cases/_dyld_shared_cache_is_locally_built.dtest/main.c +++ b/testing/test-cases/_dyld_shared_cache_is_locally_built.dtest/main.c @@ -9,16 +9,12 @@ #include #include +#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"); } diff --git a/testing/test-cases/amfi-hardened-dlopen-leaf.dtest/main.c b/testing/test-cases/amfi-hardened-dlopen-leaf.dtest/main.c index 5b999d2..35d77cf 100644 --- a/testing/test-cases/amfi-hardened-dlopen-leaf.dtest/main.c +++ b/testing/test-cases/amfi-hardened-dlopen-leaf.dtest/main.c @@ -18,42 +18,37 @@ #include #include +#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"); } diff --git a/testing/test-cases/bind-addend.dtest/main.c b/testing/test-cases/bind-addend.dtest/main.c index a059637..f89fe34 100644 --- a/testing/test-cases/bind-addend.dtest/main.c +++ b/testing/test-cases/bind-addend.dtest/main.c @@ -7,6 +7,8 @@ #include +#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"); } diff --git a/testing/test-cases/bind-rebase.dtest/main.c b/testing/test-cases/bind-rebase.dtest/main.c index 015bee1..78c83ce 100644 --- a/testing/test-cases/bind-rebase.dtest/main.c +++ b/testing/test-cases/bind-rebase.dtest/main.c @@ -9,6 +9,8 @@ #include #include +#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"); } diff --git a/testing/test-cases/chained-fixups-many-binds.dtest/main.c b/testing/test-cases/chained-fixups-many-binds.dtest/main.c index 698dd0a..35b0e77 100644 --- a/testing/test-cases/chained-fixups-many-binds.dtest/main.c +++ b/testing/test-cases/chained-fixups-many-binds.dtest/main.c @@ -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 index 0000000..2d36cf9 --- /dev/null +++ b/testing/test-cases/crt-old-mac10.5-vars-libSystem.dtest/main.c @@ -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 +#include +#include +#include +#include + +#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 index 0000000..ee3d8b3 --- /dev/null +++ b/testing/test-cases/crt-old-mac10.6-vars-libSystem.dtest/main.c @@ -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 +#include +#include +#include +#include + +#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-vars-libSystem.dtest/main.c b/testing/test-cases/crt-vars-libSystem.dtest/main.c index 6eb9381..9cf4775 100644 --- a/testing/test-cases/crt-vars-libSystem.dtest/main.c +++ b/testing/test-cases/crt-vars-libSystem.dtest/main.c @@ -9,6 +9,8 @@ #include #include +#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"); } diff --git a/testing/test-cases/cwd-relative-load.dtest/main.c b/testing/test-cases/cwd-relative-load.dtest/main.c index 8f2042f..b111390 100644 --- a/testing/test-cases/cwd-relative-load.dtest/main.c +++ b/testing/test-cases/cwd-relative-load.dtest/main.c @@ -11,18 +11,16 @@ #include +#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"); } diff --git a/testing/test-cases/dladdr-basic.dtest/main-no-syms.c b/testing/test-cases/dladdr-basic.dtest/main-no-syms.c index 672595d..baef3f6 100644 --- a/testing/test-cases/dladdr-basic.dtest/main-no-syms.c +++ b/testing/test-cases/dladdr-basic.dtest/main-no-syms.c @@ -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 @@ -11,28 +11,22 @@ #include #include - +#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"); } diff --git a/testing/test-cases/dladdr-basic.dtest/main.c b/testing/test-cases/dladdr-basic.dtest/main.c index 5f2e67c..4ee8ada 100644 --- a/testing/test-cases/dladdr-basic.dtest/main.c +++ b/testing/test-cases/dladdr-basic.dtest/main.c @@ -9,6 +9,8 @@ #include #include +#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");} diff --git a/testing/test-cases/dladdr-dylib.dtest/foo.c b/testing/test-cases/dladdr-dylib.dtest/foo.c index c62f8c6..1135218 100644 --- a/testing/test-cases/dladdr-dylib.dtest/foo.c +++ b/testing/test-cases/dladdr-dylib.dtest/foo.c @@ -9,6 +9,8 @@ #include #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"); } } diff --git a/testing/test-cases/dladdr-dylib.dtest/main.c b/testing/test-cases/dladdr-dylib.dtest/main.c index a4401ab..6a56135 100644 --- a/testing/test-cases/dladdr-dylib.dtest/main.c +++ b/testing/test-cases/dladdr-dylib.dtest/main.c @@ -13,6 +13,8 @@ #include #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"); } diff --git a/testing/test-cases/dlclose-static-terminator.dtest/main.c b/testing/test-cases/dlclose-static-terminator.dtest/main.c index dd19bbf..da4a01a 100644 --- a/testing/test-cases/dlclose-static-terminator.dtest/main.c +++ b/testing/test-cases/dlclose-static-terminator.dtest/main.c @@ -9,6 +9,7 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c b/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c index 431c63c..53d222f 100644 --- a/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c +++ b/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c @@ -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 @@ -15,70 +14,52 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c b/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c index 8c91cb5..a4d11b5 100644 --- a/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c +++ b/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c @@ -11,6 +11,7 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c b/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c index 79c113a..b358dc9 100644 --- a/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c +++ b/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c @@ -10,65 +10,52 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c b/testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c index 3fff117..abb8a3a 100644 --- a/testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c +++ b/testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c @@ -10,35 +10,29 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c index 81bc32e..c5752e5 100644 --- a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c +++ b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c @@ -4,19 +4,21 @@ #include #include +#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; diff --git a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c index 41201ea..036d102 100644 --- a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c +++ b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c @@ -11,23 +11,19 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c index 8fda486..7eef719 100644 --- a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c +++ b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c @@ -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 @@ -10,23 +10,19 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-RTLD_NOW.dtest/main.c b/testing/test-cases/dlopen-RTLD_NOW.dtest/main.c index 29f9b31..385ea03 100644 --- a/testing/test-cases/dlopen-RTLD_NOW.dtest/main.c +++ b/testing/test-cases/dlopen-RTLD_NOW.dtest/main.c @@ -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 @@ -13,23 +15,21 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-atpath-restricted.dtest/main.c b/testing/test-cases/dlopen-atpath-restricted.dtest/main.c index bd55fb4..a49cf34 100644 --- a/testing/test-cases/dlopen-atpath-restricted.dtest/main.c +++ b/testing/test-cases/dlopen-atpath-restricted.dtest/main.c @@ -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 @@ -29,52 +22,40 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-bad-file.dtest/main.c b/testing/test-cases/dlopen-bad-file.dtest/main.c dissimilarity index 64% index 63108fd..2a11c5f 100644 --- a/testing/test-cases/dlopen-bad-file.dtest/main.c +++ b/testing/test-cases/dlopen-bad-file.dtest/main.c @@ -1,47 +1,36 @@ - -// BUILD: cp bad.txt $BUILD_DIR/libnota.dylib -// BUILD: $CC main.c -o $BUILD_DIR/dlopen-bad-file.exe -DRUN_DIR="$RUN_DIR" - -// RUN: ./dlopen-bad-file.exe - -#include -#include -#include - - - -int main() -{ - printf("[BEGIN] dlopen-bad-file\n"); - - // try to dlopen() a text file - void* handle = dlopen(RUN_DIR "/libnota.dylib", RTLD_FIRST); - if ( handle != NULL ) { - printf("[FAIL] dlopen-bad-file should have failed on non-mach-o file %s\n", RUN_DIR "/libnota.dylib"); - return 0; - } - const char* message = dlerror(); - if ( (strstr(message, "mach-o") == NULL) && (strstr(message, "too short") == NULL) ) { - printf("dlerror: %s\n", message); - printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'mach-o'\n"); - return 0; - } - - // try to dlopen() a directory - handle = dlopen(RUN_DIR, RTLD_FIRST); - if ( handle != NULL ) { - printf("[FAIL] dlopen-bad-file should have failed on dir %s\n", RUN_DIR); - return 0; - } - message = dlerror(); - if ( strstr(message, "not a file") == NULL ) { - printf("dlerror: %s\n", message); - printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'not a file'\n"); - return 0; - } - - printf("[PASS] dlopen-bad-file\n"); - - return 0; -} - + +// BUILD: $CP bad.txt $BUILD_DIR/libnota.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-bad-file.exe -DRUN_DIR="$RUN_DIR" + +// RUN: ./dlopen-bad-file.exe + +#include +#include +#include + +#include "test_support.h" + +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 ) { + 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) ) { + FAIL("dlerror() message '%s' did not contain 'mach-o'", message); + } + + // try to dlopen() a directory + 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 ) { + FAIL("dlerror() message '%s' did not contain 'not a file", message); + } + + PASS("Success"); +} + diff --git a/testing/test-cases/dlopen-basic.dtest/main.c b/testing/test-cases/dlopen-basic.dtest/main.c dissimilarity index 68% index 26e3c5d..60c6711 100644 --- a/testing/test-cases/dlopen-basic.dtest/main.c +++ b/testing/test-cases/dlopen-basic.dtest/main.c @@ -1,48 +1,38 @@ - -// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/test.dylib -// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test.bundle -// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-basic.exe - -// RUN: ./dlopen-basic.exe - -#include -#include - - -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); -} - - - -int main() -{ - tryImage(RUN_DIR "/test.bundle"); - tryImage(RUN_DIR "/test.dylib"); - - return 0; -} - + +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/test.dylib +// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test.bundle +// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-basic.exe + +// RUN: ./dlopen-basic.exe + +#include +#include + +#include "test_support.h" + +static void tryImage(const char* 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(int argc, const char* argv[], const char* envp[], const char* apple[]) { + tryImage(RUN_DIR "/test.bundle"); + tryImage(RUN_DIR "/test.dylib"); + PASS("Success"); +} + diff --git a/testing/test-cases/dlopen-empty-data.dtest/main.c b/testing/test-cases/dlopen-empty-data.dtest/main.c index 9bf8051..430a2f7 100644 --- a/testing/test-cases/dlopen-empty-data.dtest/main.c +++ b/testing/test-cases/dlopen-empty-data.dtest/main.c @@ -9,19 +9,16 @@ #include #include -// 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"); } diff --git a/testing/test-cases/dlopen-fail-cleanly.dtest/main.c b/testing/test-cases/dlopen-fail-cleanly.dtest/main.c index ceee099..61eb1a3 100644 --- a/testing/test-cases/dlopen-fail-cleanly.dtest/main.c +++ b/testing/test-cases/dlopen-fail-cleanly.dtest/main.c @@ -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 @@ -12,16 +15,13 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-flat.dtest/main.c b/testing/test-cases/dlopen-flat.dtest/main.c dissimilarity index 75% index 84f5835..67a00f5 100644 --- a/testing/test-cases/dlopen-flat.dtest/main.c +++ b/testing/test-cases/dlopen-flat.dtest/main.c @@ -1,92 +1,76 @@ - -// BUILD: $CC foo.c -dynamiclib -Wl,-U,_gInitialisersCalled -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -// BUILD: $CC bar.c -dynamiclib -Wl,-U,_gInitialisersCalled $BUILD_DIR/libfoo.dylib -flat_namespace -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib -Wl,-w -// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-flat.exe - -// RUN: ./dlopen-flat.exe - -#include -#include - -int gInitialisersCalled = 0; - -int main() { - printf("[BEGIN] dlopen-flat\n"); - 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; - } - - // 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; - } - } - - // 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; - } - - // 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; - } - } - - printf("[PASS] dlopen-flat\n"); - return 0; -} - + +// BUILD: $CC foo.c -dynamiclib -Wl,-U,_gInitialisersCalled -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC bar.c -dynamiclib -Wl,-U,_gInitialisersCalled $BUILD_DIR/libfoo.dylib -flat_namespace -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib -Wl,-w +// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-flat.exe + +// RUN: ./dlopen-flat.exe + +#include +#include + +#include "test_support.h" + +int gInitialisersCalled = 0; + +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) { + 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) { + 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) { + 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) { + FAIL("dlopen(\"" RUN_DIR "/libfoo.dylib\" failed with error: %s", dlerror()); + } + if (gInitialisersCalled != 3) { + FAIL("gInitialisersCalled != 3"); + } + } + + PASS("Success"); +} + diff --git a/testing/test-cases/dlopen-framework-fallback.dtest/main.c b/testing/test-cases/dlopen-framework-fallback.dtest/main.c dissimilarity index 69% index 02c467e..0f47a99 100644 --- a/testing/test-cases/dlopen-framework-fallback.dtest/main.c +++ b/testing/test-cases/dlopen-framework-fallback.dtest/main.c @@ -1,35 +1,28 @@ - -// BUILD: $CC main.c -o $BUILD_DIR/dlopen-framework-fallback.exe - -// RUN: ./dlopen-framework-fallback.exe - -#include -#include - - - -int main() -{ - printf("[BEGIN] dlopen-framework-fallback\n"); - - // 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; -} - + +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-framework-fallback.exe + +// RUN: ./dlopen-framework-fallback.exe + +#include +#include + +#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()); + } + + // validate handle works to find symbols + void* sym = dlsym(handle, "CFRetain"); + if ( sym == NULL ) { + FAIL("dlerror(): %s", dlerror()); + } + + PASS("Success"); + + return 0; +} + diff --git a/testing/test-cases/dlopen-haswell.dtest/main.c b/testing/test-cases/dlopen-haswell.dtest/main.c index a76b9c5..abb9139 100644 --- a/testing/test-cases/dlopen-haswell.dtest/main.c +++ b/testing/test-cases/dlopen-haswell.dtest/main.c @@ -13,6 +13,8 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-in-init.dtest/foo.c b/testing/test-cases/dlopen-in-init.dtest/foo.c index 5f91679..6b89368 100644 --- a/testing/test-cases/dlopen-in-init.dtest/foo.c +++ b/testing/test-cases/dlopen-in-init.dtest/foo.c @@ -1,16 +1,7 @@ - - -#include #include -#include -#include -#include -#include -#include #include -#include -#include +#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; diff --git a/testing/test-cases/dlopen-in-init.dtest/main.c b/testing/test-cases/dlopen-in-init.dtest/main.c index d96489e..987925f 100644 --- a/testing/test-cases/dlopen-in-init.dtest/main.c +++ b/testing/test-cases/dlopen-in-init.dtest/main.c @@ -9,6 +9,7 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-in-init2.dtest/bar.c b/testing/test-cases/dlopen-in-init2.dtest/bar.c index 336e471..6f930dc 100644 --- a/testing/test-cases/dlopen-in-init2.dtest/bar.c +++ b/testing/test-cases/dlopen-in-init2.dtest/bar.c @@ -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"); + } +} diff --git a/testing/test-cases/dlopen-in-init2.dtest/foo.c b/testing/test-cases/dlopen-in-init2.dtest/foo.c index bdd1e73..199fb4f 100644 --- a/testing/test-cases/dlopen-in-init2.dtest/foo.c +++ b/testing/test-cases/dlopen-in-init2.dtest/foo.c @@ -3,7 +3,8 @@ #include #include -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 +} diff --git a/testing/test-cases/dlopen-in-init2.dtest/main.c b/testing/test-cases/dlopen-in-init2.dtest/main.c index 454802d..a9d1231 100644 --- a/testing/test-cases/dlopen-in-init2.dtest/main.c +++ b/testing/test-cases/dlopen-in-init2.dtest/main.c @@ -17,17 +17,14 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-in-init3.dtest/bar.c b/testing/test-cases/dlopen-in-init3.dtest/bar.c index 8629f2c..4f15068 100644 --- a/testing/test-cases/dlopen-in-init3.dtest/bar.c +++ b/testing/test-cases/dlopen-in-init3.dtest/bar.c @@ -3,42 +3,39 @@ #include #include +#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 +} diff --git a/testing/test-cases/dlopen-in-init3.dtest/foo.c b/testing/test-cases/dlopen-in-init3.dtest/foo.c index 86850a8..3fc5a5a 100644 --- a/testing/test-cases/dlopen-in-init3.dtest/foo.c +++ b/testing/test-cases/dlopen-in-init3.dtest/foo.c @@ -1,5 +1,4 @@ - -#include +#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 +} diff --git a/testing/test-cases/dlopen-in-init3.dtest/main.c b/testing/test-cases/dlopen-in-init3.dtest/main.c index 4f6b362..17ac740 100644 --- a/testing/test-cases/dlopen-in-init3.dtest/main.c +++ b/testing/test-cases/dlopen-in-init3.dtest/main.c @@ -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 @@ -16,21 +16,20 @@ #include #include -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"); } diff --git a/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c b/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c index 4547e90..2e90694 100644 --- a/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c +++ b/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c @@ -18,82 +18,71 @@ #include #include +#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"); +} diff --git a/testing/test-cases/dlopen-intertwined.dtest/base.c b/testing/test-cases/dlopen-intertwined.dtest/base.c index e3143fb..3b37d08 100644 --- a/testing/test-cases/dlopen-intertwined.dtest/base.c +++ b/testing/test-cases/dlopen-intertwined.dtest/base.c @@ -2,6 +2,8 @@ #include #include +#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; } diff --git a/testing/test-cases/dlopen-intertwined.dtest/main.c b/testing/test-cases/dlopen-intertwined.dtest/main.c index 11580d3..93668e1 100644 --- a/testing/test-cases/dlopen-intertwined.dtest/main.c +++ b/testing/test-cases/dlopen-intertwined.dtest/main.c @@ -16,6 +16,8 @@ #include #include +#include "test_support.h" + // main deps on A // main dlopens B which deps on C // main dlopens D which deps on C @@ -25,39 +27,31 @@ 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"); } diff --git a/testing/test-cases/dlopen-long-error-message.dtest/main.c b/testing/test-cases/dlopen-long-error-message.dtest/main.c index 8b0a16a..d517337 100644 --- a/testing/test-cases/dlopen-long-error-message.dtest/main.c +++ b/testing/test-cases/dlopen-long-error-message.dtest/main.c @@ -8,23 +8,18 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-prebuilt-dlopen-closure.dtest/main.c b/testing/test-cases/dlopen-prebuilt-dlopen-closure.dtest/main.c index 483725f..bca7d59 100644 --- a/testing/test-cases/dlopen-prebuilt-dlopen-closure.dtest/main.c +++ b/testing/test-cases/dlopen-prebuilt-dlopen-closure.dtest/main.c @@ -10,19 +10,14 @@ #include #include -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"); } diff --git a/testing/test-cases/dlopen-race.dtest/foo.c b/testing/test-cases/dlopen-race.dtest/foo.c index c8e9924..13457f2 100644 --- a/testing/test-cases/dlopen-race.dtest/foo.c +++ b/testing/test-cases/dlopen-race.dtest/foo.c @@ -1,5 +1,5 @@ int foo() { - return 10; + return 10; } diff --git a/testing/test-cases/dlopen-race.dtest/main.c b/testing/test-cases/dlopen-race.dtest/main.c index bad9ff7..1b1c3cb 100644 --- a/testing/test-cases/dlopen-race.dtest/main.c +++ b/testing/test-cases/dlopen-race.dtest/main.c @@ -9,25 +9,21 @@ #include #include +#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; } diff --git a/testing/test-cases/dlopen-realpath.dtest/main.c b/testing/test-cases/dlopen-realpath.dtest/main.c index c622e9f..30793d4 100644 --- a/testing/test-cases/dlopen-realpath.dtest/main.c +++ b/testing/test-cases/dlopen-realpath.dtest/main.c @@ -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 #include +#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"); } diff --git a/testing/test-cases/dlopen-recurse.dtest/bar.c b/testing/test-cases/dlopen-recurse.dtest/bar.c index c86856e..077c499 100644 --- a/testing/test-cases/dlopen-recurse.dtest/bar.c +++ b/testing/test-cases/dlopen-recurse.dtest/bar.c @@ -1,5 +1,5 @@ int bar() { - return 0; + return 0; } diff --git a/testing/test-cases/dlopen-recurse.dtest/main.c b/testing/test-cases/dlopen-recurse.dtest/main.c index 53c244e..a48fc72 100644 --- a/testing/test-cases/dlopen-recurse.dtest/main.c +++ b/testing/test-cases/dlopen-recurse.dtest/main.c @@ -10,17 +10,13 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c b/testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c index c86856e..077c499 100644 --- a/testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c +++ b/testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c @@ -1,5 +1,5 @@ int bar() { - return 0; + return 0; } diff --git a/testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c b/testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c index ab6936e..0b8be87 100644 --- a/testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c +++ b/testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c @@ -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 @@ -10,20 +9,14 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c b/testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c index dba4287..c655aa3 100644 --- a/testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c +++ b/testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c @@ -3,17 +3,14 @@ #include #include -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; } - diff --git a/testing/test-cases/dlopen-rpath-implicit.dtest/foo.c b/testing/test-cases/dlopen-rpath-implicit.dtest/foo.c index c8e9924..13457f2 100644 --- a/testing/test-cases/dlopen-rpath-implicit.dtest/foo.c +++ b/testing/test-cases/dlopen-rpath-implicit.dtest/foo.c @@ -1,5 +1,5 @@ int foo() { - return 10; + return 10; } diff --git a/testing/test-cases/dlopen-rpath-implicit.dtest/main.c b/testing/test-cases/dlopen-rpath-implicit.dtest/main.c index 6d43d7f..19bde69 100644 --- a/testing/test-cases/dlopen-rpath-implicit.dtest/main.c +++ b/testing/test-cases/dlopen-rpath-implicit.dtest/main.c @@ -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 #include +#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"); } diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c index 08b8b6a..d9219bf 100644 --- a/testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c +++ b/testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c @@ -1,10 +1,7 @@ -#include -#include - +#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"); } diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c index 852b89b..127cfb9 100644 --- a/testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c +++ b/testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c @@ -1,5 +1,5 @@ int sub2() { - return 2; + return 2; } diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/foo.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/foo.c index c8e9924..13457f2 100644 --- a/testing/test-cases/dlopen-rpath-prev-override.dtest/foo.c +++ b/testing/test-cases/dlopen-rpath-prev-override.dtest/foo.c @@ -1,5 +1,5 @@ int foo() { - return 10; + return 10; } diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/good.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/good.c index 98c93f6..d1955d6 100644 --- a/testing/test-cases/dlopen-rpath-prev-override.dtest/good.c +++ b/testing/test-cases/dlopen-rpath-prev-override.dtest/good.c @@ -1,5 +1,5 @@ int sub1() { - return 1; + return 1; } diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/main.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/main.c index 8aa206f..30b5dbb 100644 --- a/testing/test-cases/dlopen-rpath-prev-override.dtest/main.c +++ b/testing/test-cases/dlopen-rpath-prev-override.dtest/main.c @@ -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 @@ -16,18 +15,14 @@ #include #include -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"); } diff --git a/testing/test-cases/dlopen-rpath-prev.dtest/foo.c b/testing/test-cases/dlopen-rpath-prev.dtest/foo.c index c8e9924..13457f2 100644 --- a/testing/test-cases/dlopen-rpath-prev.dtest/foo.c +++ b/testing/test-cases/dlopen-rpath-prev.dtest/foo.c @@ -1,5 +1,5 @@ int foo() { - return 10; + return 10; } diff --git a/testing/test-cases/dlopen-rpath-prev.dtest/main.c b/testing/test-cases/dlopen-rpath-prev.dtest/main.c index 67741d0..d164692 100644 --- a/testing/test-cases/dlopen-rpath-prev.dtest/main.c +++ b/testing/test-cases/dlopen-rpath-prev.dtest/main.c @@ -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 @@ -13,18 +12,14 @@ #include #include -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"); } diff --git a/testing/test-cases/dlopen-rpath-prev.dtest/sub1.c b/testing/test-cases/dlopen-rpath-prev.dtest/sub1.c index 98c93f6..d1955d6 100644 --- a/testing/test-cases/dlopen-rpath-prev.dtest/sub1.c +++ b/testing/test-cases/dlopen-rpath-prev.dtest/sub1.c @@ -1,5 +1,5 @@ int sub1() { - return 1; + return 1; } diff --git a/testing/test-cases/dlopen-rpath-prev.dtest/sub2.c b/testing/test-cases/dlopen-rpath-prev.dtest/sub2.c index 852b89b..127cfb9 100644 --- a/testing/test-cases/dlopen-rpath-prev.dtest/sub2.c +++ b/testing/test-cases/dlopen-rpath-prev.dtest/sub2.c @@ -1,5 +1,5 @@ int sub2() { - return 2; + return 2; } diff --git a/testing/test-cases/dlopen-signing.dtest/main.c b/testing/test-cases/dlopen-signing.dtest/main.c index eaaa69b..6a41bcd 100644 --- a/testing/test-cases/dlopen-signing.dtest/main.c +++ b/testing/test-cases/dlopen-signing.dtest/main.c @@ -10,39 +10,30 @@ #include #include -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"); } diff --git a/testing/test-cases/dlopen-symlink.dtest/main.c b/testing/test-cases/dlopen-symlink.dtest/main.c index ce197a9..50ba18d 100644 --- a/testing/test-cases/dlopen-symlink.dtest/main.c +++ b/testing/test-cases/dlopen-symlink.dtest/main.c @@ -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 @@ -12,17 +12,13 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c b/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c index 445c15e..a6c750d 100644 --- a/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c +++ b/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c @@ -10,6 +10,7 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c b/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c index 2300ad1..d0fd9e9 100644 --- a/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c +++ b/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c @@ -10,6 +10,7 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c b/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c index 4a3130e..09dfda2 100644 --- a/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c +++ b/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c @@ -10,6 +10,7 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c b/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c index 5af0e2f..7f83ffe 100644 --- a/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c +++ b/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c @@ -10,6 +10,7 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlsym-handle.dtest/main.c b/testing/test-cases/dlsym-handle.dtest/main.c index 52e7750..1bd5499 100644 --- a/testing/test-cases/dlsym-handle.dtest/main.c +++ b/testing/test-cases/dlsym-handle.dtest/main.c @@ -11,89 +11,74 @@ #include #include +#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"); } diff --git a/testing/test-cases/dlsym-in-interposed-malloc.dtest/interposer.c b/testing/test-cases/dlsym-in-interposed-malloc.dtest/interposer.c index 802f30e..ef66f2d 100644 --- a/testing/test-cases/dlsym-in-interposed-malloc.dtest/interposer.c +++ b/testing/test-cases/dlsym-in-interposed-malloc.dtest/interposer.c @@ -5,6 +5,8 @@ #include #include +#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); diff --git a/testing/test-cases/dlsym-in-interposed-malloc.dtest/main.c b/testing/test-cases/dlsym-in-interposed-malloc.dtest/main.c index 70adcf0..a7faa59 100644 --- a/testing/test-cases/dlsym-in-interposed-malloc.dtest/main.c +++ b/testing/test-cases/dlsym-in-interposed-malloc.dtest/main.c @@ -9,16 +9,12 @@ #include #include -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"); } diff --git a/testing/test-cases/dlsym-re-export.dtest/main.c b/testing/test-cases/dlsym-re-export.dtest/main.c index b1752d6..4bc5ee3 100644 --- a/testing/test-cases/dlsym-re-export.dtest/main.c +++ b/testing/test-cases/dlsym-re-export.dtest/main.c @@ -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 @@ -12,32 +11,25 @@ #include #include -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"); } diff --git a/testing/test-cases/dtrace.dtest/main.c b/testing/test-cases/dtrace.dtest/main.c index 088e50c..b3b7995 100644 --- a/testing/test-cases/dtrace.dtest/main.c +++ b/testing/test-cases/dtrace.dtest/main.c @@ -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 #include #include #include +#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"); } diff --git a/testing/test-cases/dyld-insert-library-double.dtest/main.cpp b/testing/test-cases/dyld-insert-library-double.dtest/main.cpp index 1139647..cc29682 100644 --- a/testing/test-cases/dyld-insert-library-double.dtest/main.cpp +++ b/testing/test-cases/dyld-insert-library-double.dtest/main.cpp @@ -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 #include +#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"); } diff --git a/testing/test-cases/dyld-insert-library-rpath.dtest/main.cpp b/testing/test-cases/dyld-insert-library-rpath.dtest/main.cpp index 3bf1204..d714e93 100644 --- a/testing/test-cases/dyld-insert-library-rpath.dtest/main.cpp +++ b/testing/test-cases/dyld-insert-library-rpath.dtest/main.cpp @@ -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 #include +#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 index 3657491..0000000 --- a/testing/test-cases/dyld_abort_payload.dtest/main.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -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 index 0000000..9dfdc34 --- /dev/null +++ b/testing/test-cases/dyld_abort_payload.dtest/main.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); +} + diff --git a/testing/test-cases/dyld_fork-locks.dest/main.c b/testing/test-cases/dyld_fork-locks.dest/main.c index 371c5a8..db60130 100644 --- a/testing/test-cases/dyld_fork-locks.dest/main.c +++ b/testing/test-cases/dyld_fork-locks.dest/main.c @@ -7,6 +7,8 @@ #include #include +#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(¬ifyBeforeFork); if (isParent) { - printf("[PASS] dyld_fork_test\n"); + PASS("Success"); } return 0; -} \ No newline at end of file +} diff --git a/testing/test-cases/dyld_get_image_versions.dtest/main.c b/testing/test-cases/dyld_get_image_versions.dtest/main.c index 71f6bc2..a8c4a97 100644 --- a/testing/test-cases/dyld_get_image_versions.dtest/main.c +++ b/testing/test-cases/dyld_get_image_versions.dtest/main.c @@ -7,15 +7,14 @@ #include #include -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 index 43b3565..0000000 --- a/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt +++ /dev/null @@ -1 +0,0 @@ -bad file diff --git a/testing/test-cases/dyld_get_sdk_version.dtest/main.c b/testing/test-cases/dyld_get_sdk_version.dtest/main.c index c5032a0..b2df752 100644 --- a/testing/test-cases/dyld_get_sdk_version.dtest/main.c +++ b/testing/test-cases/dyld_get_sdk_version.dtest/main.c @@ -7,23 +7,20 @@ #include #include -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"); } diff --git a/testing/test-cases/dyld_has_inserted_or_interposing_libraries.dtest/main.c b/testing/test-cases/dyld_has_inserted_or_interposing_libraries.dtest/main.c index 765b47d..27df5a1 100644 --- a/testing/test-cases/dyld_has_inserted_or_interposing_libraries.dtest/main.c +++ b/testing/test-cases/dyld_has_inserted_or_interposing_libraries.dtest/main.c @@ -15,15 +15,15 @@ #include #include -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"); } diff --git a/testing/test-cases/dyld_image_path_containing_address.dtest/main.c b/testing/test-cases/dyld_image_path_containing_address.dtest/main.c index 9064caf..cf31cda 100644 --- a/testing/test-cases/dyld_image_path_containing_address.dtest/main.c +++ b/testing/test-cases/dyld_image_path_containing_address.dtest/main.c @@ -10,23 +10,19 @@ #include #include +#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"); } diff --git a/testing/test-cases/dyld_need_closure.dtest/main.c b/testing/test-cases/dyld_need_closure.dtest/main.c index 68aa687..20b5029 100644 --- a/testing/test-cases/dyld_need_closure.dtest/main.c +++ b/testing/test-cases/dyld_need_closure.dtest/main.c @@ -8,24 +8,18 @@ #include #include +#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"); } diff --git a/testing/test-cases/dyld_process_info.dtest/linksWithCF.c b/testing/test-cases/dyld_process_info.dtest/linksWithCF.c index 862da98..96c3a65 100644 --- a/testing/test-cases/dyld_process_info.dtest/linksWithCF.c +++ b/testing/test-cases/dyld_process_info.dtest/linksWithCF.c @@ -1,13 +1,21 @@ +#include #include -#include -#include +#include +#include __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 index edd700b..0000000 --- a/testing/test-cases/dyld_process_info.dtest/main.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 index 0000000..75d6789 --- /dev/null +++ b/testing/test-cases/dyld_process_info.dtest/main.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index eaab560..0000000 --- a/testing/test-cases/dyld_process_info_notify.dtest/main.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -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 index 0000000..35d8f15 --- /dev/null +++ b/testing/test-cases/dyld_process_info_notify.dtest/main.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); + +} diff --git a/testing/test-cases/dyld_process_info_notify.dtest/target.c b/testing/test-cases/dyld_process_info_notify.dtest/target.c index 55fd668..dc80b08 100644 --- a/testing/test-cases/dyld_process_info_notify.dtest/target.c +++ b/testing/test-cases/dyld_process_info_notify.dtest/target.c @@ -5,19 +5,40 @@ #include #include #include +#include - - -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 index 3872e7d..0000000 --- a/testing/test-cases/dyld_process_info_unload.dtest/main.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -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 index 0000000..961ed8a --- /dev/null +++ b/testing/test-cases/dyld_process_info_unload.dtest/main.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(); +} diff --git a/testing/test-cases/dyld_process_info_unload.dtest/target.c b/testing/test-cases/dyld_process_info_unload.dtest/target.c dissimilarity index 77% index be923e7..b70c268 100644 --- a/testing/test-cases/dyld_process_info_unload.dtest/target.c +++ b/testing/test-cases/dyld_process_info_unload.dtest/target.c @@ -1,24 +1,33 @@ -#include -#include -#include -#include -#include -#include - - - -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"); - - return 0; -} - +#include +#include +#include +#include +#include +#include + +#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); + + 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"); + }); + + dispatch_main(); +} + diff --git a/testing/test-cases/dyld_shared_cache_some_image_overridden.dtest/main.c b/testing/test-cases/dyld_shared_cache_some_image_overridden.dtest/main.c index 827e893..06abd8a 100644 --- a/testing/test-cases/dyld_shared_cache_some_image_overridden.dtest/main.c +++ b/testing/test-cases/dyld_shared_cache_some_image_overridden.dtest/main.c @@ -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 @@ -21,35 +19,31 @@ #include #include +#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; } diff --git a/testing/test-cases/dyld_version_spis.dtest/main.c b/testing/test-cases/dyld_version_spis.dtest/main.c index f5c77ce..3e738c9 100644 --- a/testing/test-cases/dyld_version_spis.dtest/main.c +++ b/testing/test-cases/dyld_version_spis.dtest/main.c @@ -7,11 +7,11 @@ #include #include +#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"); } diff --git a/testing/test-cases/dylib-re-export-old-format.dtest/main.c b/testing/test-cases/dylib-re-export-old-format.dtest/main.c index ea4d7f0..80ce72f 100644 --- a/testing/test-cases/dylib-re-export-old-format.dtest/main.c +++ b/testing/test-cases/dylib-re-export-old-format.dtest/main.c @@ -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 +#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"); } diff --git a/testing/test-cases/dylib-re-export.dtest/main.c b/testing/test-cases/dylib-re-export.dtest/main.c index 879b0b6..3484893 100644 --- a/testing/test-cases/dylib-re-export.dtest/main.c +++ b/testing/test-cases/dylib-re-export.dtest/main.c @@ -9,16 +9,16 @@ #include +#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 index 0000000..6dd84b6 --- /dev/null +++ b/testing/test-cases/dylib-static-link.dtest/main.c @@ -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 + +#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 index 6a4a62d..0000000 --- a/testing/test-cases/dylib-static-link.dtest/missing.c +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include - -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 index 2eb3393..0000000 --- a/testing/test-cases/dylib-static-link.dtest/present.c +++ /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 - -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; -} - - diff --git a/testing/test-cases/dylib-static-weak-link.dtest/missing.c b/testing/test-cases/dylib-static-weak-link.dtest/missing.c dissimilarity index 63% index c633047..af32304 100644 --- a/testing/test-cases/dylib-static-weak-link.dtest/missing.c +++ b/testing/test-cases/dylib-static-weak-link.dtest/missing.c @@ -1,19 +1,14 @@ -#include -#include - -extern int foo __attribute__((weak_import)); - - -int main() -{ - printf("[BEGIN] dylib-static-weak-link missing\n"); - // dylib won't be found at runtime, so &foo should be NULL - if ( &foo == NULL ) - printf("[PASS] dylib-static-weak-link missing\n"); - else - printf("[FAIL] dylib-static-weak-link missing, &foo != NULL\n"); - - return 0; -} - - +#include "test_support.h" + +extern int foo __attribute__((weak_import)); + + +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 ) + PASS("Success"); + else + FAIL("&foo != NULL"); +} + + diff --git a/testing/test-cases/dylib-static-weak-link.dtest/present.c b/testing/test-cases/dylib-static-weak-link.dtest/present.c index f51e384..6f1aa5b 100644 --- a/testing/test-cases/dylib-static-weak-link.dtest/present.c +++ b/testing/test-cases/dylib-static-weak-link.dtest/present.c @@ -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 @@ -10,24 +12,22 @@ #include #include +#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; } diff --git a/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c b/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c index 6b547bb..d3c94cf 100644 --- a/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c +++ b/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c @@ -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 #include -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"); } diff --git a/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c b/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c index 9972627..0422db7 100644 --- a/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c +++ b/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c @@ -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 -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"); } diff --git a/testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c b/testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c dissimilarity index 72% index 9fcf357..7411281 100644 --- a/testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c +++ b/testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c @@ -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 - -// RUN: ./DYLD_FORCE_PLATFORM.exe -// RUN: ./DYLD_FORCE_PLATFORM_FAIL.exe - -#include -#include "dyld_test.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"); - - // 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"); -#endif -} -#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"); - - // 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"); -} -#endif + +// BUILD_ONLY: MacOSX +// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FORCE_PLATFORM.exe -DENABLE_ALT_PLATFORMS=1 +// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FORCE_PLATFORM-fail.exe +// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/env-DYLD_FORCE_PLATFORM.exe +// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/env-DYLD_FORCE_PLATFORM-fail.exe + +// RUN: DYLD_FORCE_PLATFORM=6 ./env-DYLD_FORCE_PLATFORM.exe +// RUN: DYLD_FORCE_PLATFORM=6 ./env-DYLD_FORCE_PLATFORM-fail.exe + +#include +#include + +#include "test_support.h" + +#if ENABLE_ALT_PLATFORMS +__attribute__((section("__DATA,__allow_alt_plat"))) uint64_t dummy; + +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 +#if 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 +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 + if(dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib")) { FAIL("Should not be able to dlopen libswiftUIKit"); } + PASS("Success"); +} +#endif diff --git a/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c b/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c index 8a2b4f6..c1da4eb 100644 --- a/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c +++ b/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c @@ -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 @@ -11,19 +10,16 @@ #include #include +#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"); } diff --git a/testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c b/testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c index 49101de..5b4f4f8 100644 --- a/testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c +++ b/testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c @@ -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 @@ -26,17 +25,17 @@ #include #include +#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"); } diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c index bcdc22f..3062bea 100644 --- a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c @@ -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 @@ -18,21 +16,18 @@ #include #include +#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"); } diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c b/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c index 9402412..6683eb5 100644 --- a/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c @@ -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 @@ -11,19 +10,16 @@ #include #include +#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"); } diff --git a/testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/main.c b/testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/main.c index 2fbce3a..3010022 100644 --- a/testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/main.c +++ b/testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/main.c @@ -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 @@ -32,34 +29,36 @@ #include #include // for atoi() +#include + #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; } diff --git a/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH.dtest/main.c b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH.dtest/main.c index b0a7e64..2554358 100644 --- a/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH.dtest/main.c +++ b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH.dtest/main.c @@ -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 @@ -46,10 +46,10 @@ // 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 // fprintf(), NULL #include // 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; } + diff --git a/testing/test-cases/flat-namespace-absolute-symbol.dtest/main.c b/testing/test-cases/flat-namespace-absolute-symbol.dtest/main.c index 6fcd005..87670c4 100644 --- a/testing/test-cases/flat-namespace-absolute-symbol.dtest/main.c +++ b/testing/test-cases/flat-namespace-absolute-symbol.dtest/main.c @@ -11,18 +11,15 @@ #include #include +#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"); } diff --git a/testing/test-cases/flat-namespace.dtest/main.c b/testing/test-cases/flat-namespace.dtest/main.c index 37c99fc..306bd7e 100644 --- a/testing/test-cases/flat-namespace.dtest/main.c +++ b/testing/test-cases/flat-namespace.dtest/main.c @@ -11,18 +11,15 @@ #include #include -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 // 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"); } diff --git a/testing/test-cases/image_infos-uuids.dtest/main.c b/testing/test-cases/image_infos-uuids.dtest/main.c index b96c698..a592be5 100644 --- a/testing/test-cases/image_infos-uuids.dtest/main.c +++ b/testing/test-cases/image_infos-uuids.dtest/main.c @@ -11,6 +11,8 @@ #include #include +#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"); } diff --git a/testing/test-cases/init-term-segments.dtest/main.c b/testing/test-cases/init-term-segments.dtest/main.c index e91a2cd..f0ef7b8 100644 --- a/testing/test-cases/init-term-segments.dtest/main.c +++ b/testing/test-cases/init-term-segments.dtest/main.c @@ -9,42 +9,35 @@ #include #include -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"); } diff --git a/testing/test-cases/interpose-malloc.dtest/main.c b/testing/test-cases/interpose-malloc.dtest/main.c index 9ef1842..5baba7d 100644 --- a/testing/test-cases/interpose-malloc.dtest/main.c +++ b/testing/test-cases/interpose-malloc.dtest/main.c @@ -11,38 +11,32 @@ #include #include +#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"); } diff --git a/testing/test-cases/interpose-resolver.dtest/main.c b/testing/test-cases/interpose-resolver.dtest/main.c index 89651e8..bee0659 100644 --- a/testing/test-cases/interpose-resolver.dtest/main.c +++ b/testing/test-cases/interpose-resolver.dtest/main.c @@ -11,27 +11,25 @@ #include #include +#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; } diff --git a/testing/test-cases/interpose-then-dlopen.dtest/main.c b/testing/test-cases/interpose-then-dlopen.dtest/main.c index e712974..8e7e99e 100644 --- a/testing/test-cases/interpose-then-dlopen.dtest/main.c +++ b/testing/test-cases/interpose-then-dlopen.dtest/main.c @@ -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 #include +#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"); } diff --git a/testing/test-cases/interpose-weak.dtest/main.c b/testing/test-cases/interpose-weak.dtest/main.c index 5248427..07024b0 100644 --- a/testing/test-cases/interpose-weak.dtest/main.c +++ b/testing/test-cases/interpose-weak.dtest/main.c @@ -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 #include +#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"); } diff --git a/testing/test-cases/launch-image-cache.dtest/main.c b/testing/test-cases/launch-image-cache.dtest/main.c index a4f4a1b..48fe6a8 100644 --- a/testing/test-cases/launch-image-cache.dtest/main.c +++ b/testing/test-cases/launch-image-cache.dtest/main.c @@ -45,6 +45,8 @@ #include +#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"); } diff --git a/testing/test-cases/lazy-symbol-missing.dtest/main.c b/testing/test-cases/lazy-symbol-missing.dtest/main.c index a6c14e6..0c74e99 100644 --- a/testing/test-cases/lazy-symbol-missing.dtest/main.c +++ b/testing/test-cases/lazy-symbol-missing.dtest/main.c @@ -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 #include +#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 index 5aaeff3..0000000 --- a/testing/test-cases/lazy-symbol-missing.dtest/runner.c +++ /dev/null @@ -1,101 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 index 0000000..aadd53f --- /dev/null +++ b/testing/test-cases/lazy-symbol-missing.dtest/runner.cpp @@ -0,0 +1,55 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index d60aee3..0000000 --- a/testing/test-cases/macOS-cache-rebuild.dtest/main.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include - -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 index 0000000..9d3ea90 --- /dev/null +++ b/testing/test-cases/macOS-cache-rebuild.dtest/main.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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(); +} + diff --git a/testing/test-cases/missing-weak-def.dtest/main.c b/testing/test-cases/missing-weak-def.dtest/main.c index b4921cd..f677581 100644 --- a/testing/test-cases/missing-weak-def.dtest/main.c +++ b/testing/test-cases/missing-weak-def.dtest/main.c @@ -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 @@ -11,20 +13,17 @@ #include #include +#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"); } diff --git a/testing/test-cases/no-shared-cache.dtest/main.c b/testing/test-cases/no-shared-cache.dtest/main.c index 9407740..1ce992e 100644 --- a/testing/test-cases/no-shared-cache.dtest/main.c +++ b/testing/test-cases/no-shared-cache.dtest/main.c @@ -10,22 +10,19 @@ #include #include +#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"); } diff --git a/testing/test-cases/operator-new.dtest/main.cxx b/testing/test-cases/operator-new.dtest/main.cxx index 1773a03..a6d0c4a 100644 --- a/testing/test-cases/operator-new.dtest/main.cxx +++ b/testing/test-cases/operator-new.dtest/main.cxx @@ -3,10 +3,9 @@ // RUN: ./operator-new.exe -#include #include - +#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"); } diff --git a/testing/test-cases/read-only-data.dtest/main.c b/testing/test-cases/read-only-data.dtest/main.c index 63e43ef..a4797ee 100644 --- a/testing/test-cases/read-only-data.dtest/main.c +++ b/testing/test-cases/read-only-data.dtest/main.c @@ -17,6 +17,8 @@ #include #include +#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(¬ify); @@ -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"); } diff --git a/testing/test-cases/restrict-search.dtest/main.c b/testing/test-cases/restrict-search.dtest/main.c index e8355ca..a00a77b 100644 --- a/testing/test-cases/restrict-search.dtest/main.c +++ b/testing/test-cases/restrict-search.dtest/main.c @@ -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 #include +#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; diff --git a/testing/test-cases/rpath-absolute.dtest/main.c b/testing/test-cases/rpath-absolute.dtest/main.c index f37c78e..93b2305 100644 --- a/testing/test-cases/rpath-absolute.dtest/main.c +++ b/testing/test-cases/rpath-absolute.dtest/main.c @@ -12,14 +12,13 @@ #include +#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 index 0000000..723758f --- /dev/null +++ b/testing/test-cases/rpath-relative.dtest/foo.c @@ -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 index 0000000..2779b7b --- /dev/null +++ b/testing/test-cases/rpath-relative.dtest/main.c @@ -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 + +#include "test_support.h" + +extern char* __progname; + + +int main(int argc, const char* argv[]) +{ + PASS("%s", __progname); +} + + diff --git a/testing/test-cases/rpath-weak-missing.dtest/main.c b/testing/test-cases/rpath-weak-missing.dtest/main.c index 4ed1aa6..bfc258e 100644 --- a/testing/test-cases/rpath-weak-missing.dtest/main.c +++ b/testing/test-cases/rpath-weak-missing.dtest/main.c @@ -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 @@ -13,19 +15,16 @@ #include #include +#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"); } diff --git a/testing/test-cases/shared_cache_iterate.dtest/main.c b/testing/test-cases/shared_cache_iterate.dtest/main.c index 2327d3d..a6058f6 100644 --- a/testing/test-cases/shared_cache_iterate.dtest/main.c +++ b/testing/test-cases/shared_cache_iterate.dtest/main.c @@ -18,7 +18,7 @@ #include #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"); } diff --git a/testing/test-cases/shared_cache_optimized.dtest/main.c b/testing/test-cases/shared_cache_optimized.dtest/main.c index df44b5e..2d8f4e6 100644 --- a/testing/test-cases/shared_cache_optimized.dtest/main.c +++ b/testing/test-cases/shared_cache_optimized.dtest/main.c @@ -9,17 +9,13 @@ #include #include +#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"); } diff --git a/testing/test-cases/shared_cache_range.dtest/main.c b/testing/test-cases/shared_cache_range.dtest/main.c index 1e08b62..89ddaf6 100644 --- a/testing/test-cases/shared_cache_range.dtest/main.c +++ b/testing/test-cases/shared_cache_range.dtest/main.c @@ -13,6 +13,8 @@ #include #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"); } diff --git a/testing/test-cases/static-terminators.dtest/base.c b/testing/test-cases/static-terminators.dtest/base.c index 847dfef..388cb84 100644 --- a/testing/test-cases/static-terminators.dtest/base.c +++ b/testing/test-cases/static-terminators.dtest/base.c @@ -1,7 +1,8 @@ #include -#include #include +#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"); } diff --git a/testing/test-cases/static-terminators.dtest/foo.c b/testing/test-cases/static-terminators.dtest/foo.c index 4afcee7..5da79d5 100644 --- a/testing/test-cases/static-terminators.dtest/foo.c +++ b/testing/test-cases/static-terminators.dtest/foo.c @@ -1,6 +1,7 @@ #include #include +#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(); } diff --git a/testing/test-cases/static-terminators.dtest/main.c b/testing/test-cases/static-terminators.dtest/main.c index 3e6d3ac..7df67c8 100644 --- a/testing/test-cases/static-terminators.dtest/main.c +++ b/testing/test-cases/static-terminators.dtest/main.c @@ -10,6 +10,7 @@ #include #include +#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; } diff --git a/testing/test-cases/symbol-resolver-basic.dtest/main.c b/testing/test-cases/symbol-resolver-basic.dtest/main.c index cebb529..17af5fa 100644 --- a/testing/test-cases/symbol-resolver-basic.dtest/main.c +++ b/testing/test-cases/symbol-resolver-basic.dtest/main.c @@ -11,26 +11,27 @@ #include #include +#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; diff --git a/testing/test-cases/thread-local-atexit-macOS.dtest/main.cpp b/testing/test-cases/thread-local-atexit-macOS.dtest/main.cpp index 5989115..dcc3c40 100644 --- a/testing/test-cases/thread-local-atexit-macOS.dtest/main.cpp +++ b/testing/test-cases/thread-local-atexit-macOS.dtest/main.cpp @@ -8,6 +8,8 @@ #include #include +#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; } diff --git a/testing/test-cases/thread-local-atexit.dtest/main.cpp b/testing/test-cases/thread-local-atexit.dtest/main.cpp index d86dcab..950fb11 100644 --- a/testing/test-cases/thread-local-atexit.dtest/main.cpp +++ b/testing/test-cases/thread-local-atexit.dtest/main.cpp @@ -7,6 +7,8 @@ #include #include +#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; diff --git a/testing/test-cases/thread-local-cleanup.dtest/main.c b/testing/test-cases/thread-local-cleanup.dtest/main.c index e301792..b7fcf17 100644 --- a/testing/test-cases/thread-local-cleanup.dtest/main.c +++ b/testing/test-cases/thread-local-cleanup.dtest/main.c @@ -7,30 +7,21 @@ #include #include +#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"); } diff --git a/testing/test-cases/thread-local-destructors.dtest/main.cpp b/testing/test-cases/thread-local-destructors.dtest/main.cpp index 1c43d27..0386e41 100644 --- a/testing/test-cases/thread-local-destructors.dtest/main.cpp +++ b/testing/test-cases/thread-local-destructors.dtest/main.cpp @@ -16,6 +16,8 @@ #include +#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; diff --git a/testing/test-cases/thread-local-variables.dtest/main.c b/testing/test-cases/thread-local-variables.dtest/main.c index d7c73b2..272f142 100644 --- a/testing/test-cases/thread-local-variables.dtest/main.c +++ b/testing/test-cases/thread-local-variables.dtest/main.c @@ -14,6 +14,7 @@ #include #include +#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"); } diff --git a/testing/test-cases/unix-conformance.dtest/main.c b/testing/test-cases/unix-conformance.dtest/main.c index a6540f8..48da96e 100644 --- a/testing/test-cases/unix-conformance.dtest/main.c +++ b/testing/test-cases/unix-conformance.dtest/main.c @@ -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 @@ -13,11 +15,9 @@ #include #include -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"); } diff --git a/testing/test-cases/weak-coalesce-inserted-dylibs.dtest/main.cpp b/testing/test-cases/weak-coalesce-inserted-dylibs.dtest/main.cpp index f4fdad1..3f7d1ef 100644 --- a/testing/test-cases/weak-coalesce-inserted-dylibs.dtest/main.cpp +++ b/testing/test-cases/weak-coalesce-inserted-dylibs.dtest/main.cpp @@ -10,6 +10,8 @@ #include #include +#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"); } diff --git a/testing/test-cases/weak-coalesce-unload.dtest/main.c b/testing/test-cases/weak-coalesce-unload.dtest/main.c index dc98c47..dc19b9d 100644 --- a/testing/test-cases/weak-coalesce-unload.dtest/main.c +++ b/testing/test-cases/weak-coalesce-unload.dtest/main.c @@ -11,117 +11,99 @@ #include #include +#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 index 543a5be..0000000 --- a/testing/test-cases/weak-coalesce.dtest/Makefile +++ /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 - diff --git a/testing/test-cases/weak-coalesce.dtest/base.c b/testing/test-cases/weak-coalesce.dtest/base.c dissimilarity index 62% index b8a04c9..d0618b8 100644 --- a/testing/test-cases/weak-coalesce.dtest/base.c +++ b/testing/test-cases/weak-coalesce.dtest/base.c @@ -1,63 +1,64 @@ -#include -#include - -#include "base.h" - -static bool wasProblem = false; -static const char* coal1Where = NULL; -static int* coal1Addr = NULL; -static int checkInCountCoal1 = 0; - -void baseVerifyCoal1(const char* where, int* addr) -{ - //fprintf(stderr, "baseVerifyCoal1(%s, %p)\n", where, addr); - ++checkInCountCoal1; - if ( coal1Where == NULL ) { - coal1Where = where; - coal1Addr = addr; - } - else { - if ( addr != coal1Addr ) { - fprintf(stderr, "coal1 resolved to different locations. %p in %s and %p in %s\n", - coal1Addr, coal1Where, addr, where); - wasProblem = true; - } - } -} - - -static const char* coal2Where = NULL; -static int* coal2Addr = NULL; -static int checkInCountCoal2 = 0; - -void baseVerifyCoal2(const char* where, int* addr) -{ - //fprintf(stderr, "baseVerifyCoal2(%s, %p)\n", where, addr); - ++checkInCountCoal2; - if ( coal2Where == NULL ) { - coal2Where = where; - coal2Addr = addr; - } - else { - if ( addr != coal2Addr ) { - fprintf(stderr, "coal2 resolved to different locations. %p in %s and %p in %s\n", - coal2Addr, coal2Where, addr, where); - wasProblem = true; - } - } -} - - - -void baseCheck() -{ - if ( wasProblem ) - printf("[FAIL] weak-coalesce: was problem\n"); - else if ( checkInCountCoal1 != 4 ) - printf("[FAIL] weak-coalesce: checkInCountCoal1 != 4\n"); - else if ( checkInCountCoal2 != 4 ) - printf("[FAIL] weak-coalesce: checkInCountCoal2 != 2\n"); - else - printf("[PASS] weak-coalesce\n"); -} - +#include +#include + +#include "test_support.h" +#include "base.h" + +static bool wasProblem = false; +static const char* coal1Where = NULL; +static int* coal1Addr = NULL; +static int checkInCountCoal1 = 0; + +void baseVerifyCoal1(const char* where, int* addr) +{ + 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; + } + } +} + + +static const char* coal2Where = NULL; +static int* coal2Addr = NULL; +static int checkInCountCoal2 = 0; + +void baseVerifyCoal2(const char* where, int* addr) +{ + 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 ) + FAIL("was problem"); + else if ( checkInCountCoal1 != 4 ) + FAIL("checkInCountCoal1 != 4"); + else if ( checkInCountCoal2 != 4 ) + FAIL("checkInCountCoal2 != 2"); + else + PASS("Success"); +} + diff --git a/testing/test-cases/weak-coalesce.dtest/foo1.c b/testing/test-cases/weak-coalesce.dtest/foo1.c index 0707e0d..10bb3dd 100644 --- a/testing/test-cases/weak-coalesce.dtest/foo1.c +++ b/testing/test-cases/weak-coalesce.dtest/foo1.c @@ -21,6 +21,9 @@ * @APPLE_LICENSE_HEADER_END@ */ #include + +#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); } diff --git a/testing/test-cases/weak-coalesce.dtest/foo2.c b/testing/test-cases/weak-coalesce.dtest/foo2.c index 74d884b..37adf6a 100644 --- a/testing/test-cases/weak-coalesce.dtest/foo2.c +++ b/testing/test-cases/weak-coalesce.dtest/foo2.c @@ -1,14 +1,16 @@ #include +#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); } diff --git a/testing/test-cases/weak-coalesce.dtest/foo3.c b/testing/test-cases/weak-coalesce.dtest/foo3.c index e086d5c..190e2fe 100644 --- a/testing/test-cases/weak-coalesce.dtest/foo3.c +++ b/testing/test-cases/weak-coalesce.dtest/foo3.c @@ -1,4 +1,4 @@ -#include +#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); } diff --git a/testing/test-cases/weak-coalesce.dtest/main.c b/testing/test-cases/weak-coalesce.dtest/main.c index 4d09091..3e19d5d 100644 --- a/testing/test-cases/weak-coalesce.dtest/main.c +++ b/testing/test-cases/weak-coalesce.dtest/main.c @@ -11,16 +11,13 @@ #include #include +#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 index 0000000..82f5a4f --- /dev/null +++ b/testing/test-cases/weak-def-bind-old-format.dtest/bar.c @@ -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 index 0000000..172a9c1 --- /dev/null +++ b/testing/test-cases/weak-def-bind-old-format.dtest/foo.c @@ -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 index 0000000..780d087 --- /dev/null +++ b/testing/test-cases/weak-def-bind-old-format.dtest/main.c @@ -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 + +#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"); +} + + diff --git a/testing/test-cases/weak-dylib-re-export.dtest/main.c b/testing/test-cases/weak-dylib-re-export.dtest/main.c dissimilarity index 62% index 7b9b7e6..a7aac22 100644 --- a/testing/test-cases/weak-dylib-re-export.dtest/main.c +++ b/testing/test-cases/weak-dylib-re-export.dtest/main.c @@ -1,26 +1,28 @@ - - -// 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 main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR - -// RUN: ./dylib-re-export.exe - - -#include - -__attribute__((weak_import)) -extern int bar(); - -int main() -{ - printf("[BEGIN] dylib-re-export\n"); - if ( &bar == 0 ) - printf("[PASS] dylib-re-export\n"); - else - printf("[FAIL] dylib-re-export, wrong value\n"); - - return 0; -} - - + + +// 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 + +#include "test_support.h" + +__attribute__((weak_import)) +extern int bar(); + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if ( &bar == 0 ) + PASS("SUCCESS"); + else + FAIL("wrong value"); + + return 0; +} + + -- 2.7.4