--- /dev/null
+/*
+* Copyright (c) 2019 Apple Inc. All rights reserved.
+*
+* @APPLE_LICENSE_HEADER_START@
+*
+* "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
+* Reserved. This file contains Original Code and/or Modifications of
+* Original Code as defined in and that are subject to the Apple Public
+* Source License Version 1.0 (the 'License'). You may not use this file
+* except in compliance with the License. Please obtain a copy of the
+* License at http://www.apple.com/publicsource and read it before using
+* this file.
+*
+* The Original Code and all software distributed under the License are
+* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+* License for the specific language governing rights and limitations
+* under the License."
+*
+* @APPLE_LICENSE_HEADER_END@
+*/
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <sys/attr.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <copyfile.h>
+
+#include <set>
+#include <string>
+#include <vector>
+#include <functional>
+#include <filesystem>
+
+#include "StringUtils.h"
+#include "MachOFile.h"
+
+std::set<std::string> scanForDependencies(const std::string& path) {
+ __block std::set<std::string> result;
+ struct stat stat_buf;
+ int fd = open(path.c_str(), O_RDONLY, 0);
+ if (fd == -1) {
+ return result;
+ }
+
+ if (fstat(fd, &stat_buf) == -1) {
+ close(fd);
+ return result;
+ }
+
+ const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (buffer == MAP_FAILED) {
+ close(fd);
+ return result;
+ }
+
+ auto scanner = ^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+ if (isWeak) { return; } // We explicily avoid LC_LOAD_WEAK_DYLIB since we are trying to build a minimal chroot
+ if (loadPath[0] != '/') { return; } // Only include absolute dependencies
+ result.insert(loadPath);
+ };
+ Diagnostics diag;
+ if ( dyld3::FatFile::isFatFile(buffer) ) {
+ const dyld3::FatFile* ff = (dyld3::FatFile*)buffer;
+ ff->forEachSlice(diag, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
+ const dyld3::MachOFile* mf = (dyld3::MachOFile*)sliceStart;
+ mf->forEachDependentDylib(scanner);
+ });
+ } else {
+ const dyld3::MachOFile* mf = (dyld3::MachOFile*)buffer;
+ if (mf->isMachO(diag, stat_buf.st_size)) {
+ mf->forEachDependentDylib(scanner);
+ }
+ }
+ close(fd);
+ return result;
+}
+
+std::string withoutPrefixPath(const std::string& path, const std::string& prefix ) {
+ std::string result = path;
+ size_t pos = result.find(prefix);
+ result.erase(pos, prefix.length());
+ return result;
+}
+
+void add_symlinks_to_dylib(const std::string path) {
+ static std::set<std::string> alreadyMatched;
+ size_t pos = path.rfind(".framework/Versions/");
+ auto prefixPath = path.substr(0, pos);
+ if (alreadyMatched.find(prefixPath) != alreadyMatched.end()) { return; }
+
+ if (pos == std::string::npos) { return; }
+// fprintf(stderr, "PATH: %s\n", path.c_str());
+ size_t versionStart = pos+20;
+ size_t versionEnd = versionStart;
+ while (path[versionEnd] != '/') {
+ ++versionEnd;
+ }
+ size_t frameworkNameBegin = pos;
+ while (path[frameworkNameBegin-1] != '/') {
+ --frameworkNameBegin;
+ }
+ auto frameworkName = path.substr(frameworkNameBegin, pos-frameworkNameBegin);
+ auto version = path.substr(versionStart, versionEnd-versionStart);
+ std::string mainLinkPath = prefixPath + ".framework/" + frameworkName;
+ std::string mainLinkTarget = "Versions/Current/" + frameworkName;
+ std::string versionLinkPath = prefixPath + ".framework/Versions/Current";
+ std::string versionLinkTarget = version;;
+ alreadyMatched.insert(prefixPath);
+ if (!std::filesystem::exists(versionLinkPath)) {
+ std::filesystem::create_symlink(version, versionLinkPath);
+ }
+ if (!std::filesystem::exists(mainLinkPath)) {
+ std::filesystem::create_symlink(mainLinkTarget, mainLinkPath);
+ }
+}
+
+void add_symlink(const std::string& target, const std::string& path) {
+ if (!std::filesystem::exists(path)) {
+ std::filesystem::create_symlink(target, path);
+ }
+}
+
+void buildChroot(const std::string& chroot, const std::string& fallback, const std::vector<std::string>& binaries) {
+ auto chrootPath = std::filesystem::path(chroot);
+ auto fallbackPath = std::filesystem::path(fallback);
+
+ for (const auto& binary : binaries) {
+ if (std::filesystem::exists(chroot + binary)) { continue; }
+ std::filesystem::create_directories(std::filesystem::path(chroot + binary).parent_path());
+ std::filesystem::copy(fallback + binary, chroot + binary);
+ }
+ bool foundNewEntries = true;
+ std::set<std::string> scannedFiles;
+ std::string devfsPath = chroot + "/dev";
+ while (foundNewEntries) {
+ foundNewEntries = false;
+ for(auto file = std::filesystem::recursive_directory_iterator(chroot);
+ file != std::filesystem::recursive_directory_iterator();
+ ++file ) {
+ auto filePath = file->path().string();
+ if (filePath == devfsPath) {
+ file.disable_recursion_pending();
+ continue;
+ }
+ if (scannedFiles.find(filePath) != scannedFiles.end()) { continue; }
+ scannedFiles.insert(filePath);
+ auto candidates = scanForDependencies(filePath);
+ for (const auto& candidate : candidates) {
+ if (std::filesystem::exists(chroot + candidate)) { continue; }
+ if (!std::filesystem::exists(fallback + candidate)) { continue; }
+ std::filesystem::create_directories(std::filesystem::path(chroot + candidate).parent_path());
+ std::filesystem::copy(fallback + candidate, chroot + candidate);
+ add_symlinks_to_dylib(chroot + candidate);
+ foundNewEntries = true;
+ }
+ }
+ }
+ add_symlink("libSystem.B.dylib", chroot + "/usr/lib/libSystem.dylib");
+ add_symlink("libSystem.dylib", chroot + "/usr/lib/libc.dylib");
+}
+
+int main(int argc, const char * argv[]) {
+ std::vector<std::string> binaries;
+ std::vector<std::string> overlays;
+ std::string fallback;
+ std::string chroot;
+ for (int i = 1; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (arg[0] == '-') {
+ if (strcmp(arg, "-chroot") == 0) {
+ chroot = argv[++i];
+ } else if (strcmp(arg, "-fallback") == 0) {
+ fallback = argv[++i];
+ } else if (strcmp(arg, "-add_file") == 0) {
+ binaries.push_back(argv[++i]);
+ } else {
+ fprintf(stderr, "unknown option: %s\n", arg);
+ exit(-1);
+ }
+ } else {
+ fprintf(stderr, "unknown option: %s\n", arg);
+ exit(-1);
+ }
+ }
+
+ if (chroot.length() == 0) {
+ fprintf(stderr, "No -chroot <dir>\n");
+ exit(-1);
+ }
+ if (fallback.length() == 0) {
+ fprintf(stderr, "No -fallback <dir>\n");
+ exit(-1);
+ }
+ buildChroot(chroot, fallback, binaries);
+ // insert code here...
+ return 0;
+}
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
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
--- /dev/null
+{
+ "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
+}
buildConfigurationList = F9F6F42A1C1FB0A700BD8FED /* Build configuration list for PBXAggregateTarget "dyld_tests" */;
buildPhases = (
F9F6F42B1C1FB0AE00BD8FED /* build */,
+ 37E2FC9D22F62FE1004AF213 /* install */,
);
dependencies = (
- F97FF3661C237F97000ACDD2 /* PBXTargetDependency */,
+ 37382F6A230CB46500E375CE /* PBXTargetDependency */,
);
name = dyld_tests;
productName = dyld_tests;
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
+ 37065AA82310889D00A20034 /* libtest_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3721A635230CABAF00594066 /* libtest_support.a */; };
+ 37065AAB2310A18300A20034 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F93F46511FA420630060D9F9 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; };
+ 3715A301232320BD0059433D /* ContainerizedTestRunner.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3715A300232320BD0059433D /* ContainerizedTestRunner.mm */; };
+ 3715A303232320BD0059433D /* libtest_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3721A635230CABAF00594066 /* libtest_support.a */; };
+ 37382F68230CADEE00E375CE /* test_support.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37382F67230CADEE00E375CE /* test_support.cpp */; };
373C58F1219CE478003442D5 /* BootArgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 373C58EF219CE478003442D5 /* BootArgs.cpp */; };
- 37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; };
- 37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
- 37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
- 37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */; };
- 37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
- 37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
- 37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
- 37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
- 37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
- 37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
- 37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
- 376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; };
- 376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; };
- 378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
- 378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
- 37908A2E1E3A8632009613FA /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; };
+ 375A4C74233DE07600CFBD6B /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+ 375A4C75233DE09E00CFBD6B /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */; };
37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
37908A321E3ED667009613FA /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
37918AC52058915E00F39A77 /* dyld.codes in install ktrace codes file */ = {isa = PBXBuildFile; fileRef = 37918AC42058913800F39A77 /* dyld.codes */; };
- 37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
- 37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+ 37CE9D1A2321A7EB001FBA91 /* chroot_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37CE9D192321A7EB001FBA91 /* chroot_util.cpp */; };
37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; };
37D7DB011E96F3EB00D52CEA /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; };
37F597D52061ED0B00F9B6F9 /* dyld_usage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */; };
37F597D72061ED3200F9B6F9 /* libktrace.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F597D62061ED3200F9B6F9 /* libktrace.tbd */; };
+ C11ECA90233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; };
+ C11ECA91233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; };
+ C11ECA92233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; };
+ C11ECA93233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; };
+ C11ECA94233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; };
+ C123176B22B9B4F00046E3E5 /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
C1436B2C203BE67D00028AF1 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
C172C9DD20252CB500159311 /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+ C176CB5C2321AB74009C1259 /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
C17984D61FE9E9160057D002 /* mrm_shared_cache_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */; };
C187B90D1FE067C70042D3B7 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
C187B90E1FE067CD0042D3B7 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
C187B9161FE0680A0042D3B7 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
C187B9171FE068180042D3B7 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
C187B9181FE068260042D3B7 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
- C187B9191FE0682C0042D3B7 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
C187B91B1FE0683F0042D3B7 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
C187B91E1FE0684C0042D3B7 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
C18A75F9209A1AF600DC01BB /* JSONReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = C18A75F8209A1AF600DC01BB /* JSONReader.mm */; };
C1D268391FE0BC94009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
C1D2683A1FE0BCF3009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; };
C1D2683F1FE98D4F009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
- C1D268401FE9B464009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
C1F003CC213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
- C1F003CD213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
C1F003CE213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
C1F003CF213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
C1F003D0213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
F93D73451F8475C3007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
F93D73461F8475C3007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
F93D73471F8C4E55007D9413 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
- F93D73481F8FF780007D9413 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
- F93D73491F8FF780007D9413 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
- F93D734A1F8FF780007D9413 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
- F93D734B1F8FF79E007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
- F93D734C1F8FF79E007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
- F93D734D1F8FF79E007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
F93D734E1F8FF7C2007D9413 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
F93D734F1F8FF7C2007D9413 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
F93D73501F8FF7C2007D9413 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
F93D73511F8FF7C2007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
F93D73521F8FF7C2007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
F93D73531F8FF7C2007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
- F93F46521FA420850060D9F9 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F93F46511FA420630060D9F9 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; };
F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; };
F977DDCB1E53BF5500609230 /* SharedCacheRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */; };
F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */; };
F97C61A21D9CAE3500A84CD7 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97C61A01D9CA6B800A84CD7 /* Logging.cpp */; };
- F97FF3611C23640C000ACDD2 /* nocr.c in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.c */; };
+ F97FF3611C23640C000ACDD2 /* nocr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.cpp */; };
F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */ = {isa = PBXBuildFile; fileRef = F97FF3631C237F5C000ACDD2 /* nocr.1 */; };
F98692171DC3EFD500CBEDE6 /* update_dyld_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */; };
F98692181DC3EFD700CBEDE6 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
/* End PBXBuildRule section */
/* Begin PBXContainerItemProxy section */
+ 3715A30A23272F890059433D /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 3721B6A72321A75B006F6AB7;
+ remoteInfo = chroot_util;
+ };
+ 37382F69230CB46500E375CE /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 3721A634230CABAF00594066;
+ remoteInfo = test_support;
+ };
37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
remoteGlobalIDString = F97C61A61DBAD1A900A84CD7;
remoteInfo = dyld_closure_util;
};
- F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = F97FF3551C23638F000ACDD2;
- remoteInfo = nocr;
- };
F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
);
runOnlyForDeploymentPostprocessing = 1;
};
- 377685FE1AC4B27D00026E6C /* CopyFiles */ = {
+ 3721B6A62321A75B006F6AB7 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
/* Begin PBXFileReference section */
3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; };
+ 37065AA72310856E00A20034 /* nocr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nocr; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3715A2FE232320BC0059433D /* ContainerizedTestRunner.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ContainerizedTestRunner.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3715A300232320BD0059433D /* ContainerizedTestRunner.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ContainerizedTestRunner.mm; sourceTree = "<group>"; };
+ 3715A302232320BD0059433D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 3721A635230CABAF00594066 /* libtest_support.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libtest_support.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3721B6A82321A75B006F6AB7 /* chroot_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chroot_util; sourceTree = BUILT_PRODUCTS_DIR; };
+ 37382F67230CADEE00E375CE /* test_support.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = test_support.cpp; path = testing/lib/test_support.cpp; sourceTree = "<group>"; };
373C58EF219CE478003442D5 /* BootArgs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = BootArgs.cpp; path = dyld3/BootArgs.cpp; sourceTree = "<group>"; };
373C58F0219CE478003442D5 /* BootArgs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BootArgs.h; path = dyld3/BootArgs.h; sourceTree = "<group>"; };
+ 376AA37C23305CE10070C28C /* ContainerizedTestRunner.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = ContainerizedTestRunner.xctestplan; path = dyld.xcodeproj/ContainerizedTestRunner.xctestplan; sourceTree = "<group>"; };
376ED1D71C46F2710051DD54 /* Metabom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metabom.framework; path = AppleInternal/Library/Frameworks/Metabom.framework; sourceTree = SDKROOT; };
- 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = multi_dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; };
37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/dyld_shared_cache_builder.mm"; sourceTree = "<group>"; };
- 37908A281E3A853E009613FA /* Manifest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Manifest.mm; path = "dyld3/shared-cache/Manifest.mm"; sourceTree = "<group>"; };
- 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = multi_dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/multi_dyld_shared_cache_builder.mm"; sourceTree = "<group>"; };
37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = "dyld3/shared-cache/FileAbstraction.hpp"; sourceTree = "<group>"; };
37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = "dyld3/shared-cache/MachOFileAbstraction.hpp"; sourceTree = "<group>"; };
- 37908A2C1E3A85A4009613FA /* Manifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Manifest.h; path = "dyld3/shared-cache/Manifest.h"; sourceTree = "<group>"; };
37908A2D1E3A85A4009613FA /* Trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Trie.hpp; path = "dyld3/shared-cache/Trie.hpp"; sourceTree = "<group>"; };
37918AC0205890D700F39A77 /* dyld.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = dyld.plist; sourceTree = "<group>"; };
37918AC42058913800F39A77 /* dyld.codes */ = {isa = PBXFileReference; lastKnownFileType = text; path = dyld.codes; sourceTree = "<group>"; };
- 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BuilderUtils.mm; path = "dyld3/shared-cache/BuilderUtils.mm"; sourceTree = "<group>"; };
- 37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuilderUtils.h; path = "dyld3/shared-cache/BuilderUtils.h"; sourceTree = "<group>"; };
+ 37CE9D192321A7EB001FBA91 /* chroot_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = chroot_util.cpp; sourceTree = "<group>"; };
37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tracing.cpp; path = dyld3/Tracing.cpp; sourceTree = "<group>"; };
37D7DAFF1E96F0ED00D52CEA /* Tracing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Tracing.h; path = dyld3/Tracing.h; sourceTree = "<group>"; };
37F597CD2061EB4200F9B6F9 /* dyld_usage */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_usage; sourceTree = BUILT_PRODUCTS_DIR; };
37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_usage.cpp; path = src/dyld_usage.cpp; sourceTree = "<group>"; };
37F597D62061ED3200F9B6F9 /* libktrace.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libktrace.tbd; path = usr/lib/libktrace.tbd; sourceTree = SDKROOT; };
37F7A5961BB363820039043A /* Bom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bom.framework; path = System/Library/PrivateFrameworks/Bom.framework; sourceTree = SDKROOT; };
+ C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SharedCacheBuilder.cpp; path = "dyld3/shared-cache/SharedCacheBuilder.cpp"; sourceTree = "<group>"; };
+ C11ECA8F233C307C0011726F /* SharedCacheBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SharedCacheBuilder.h; path = "dyld3/shared-cache/SharedCacheBuilder.h"; sourceTree = "<group>"; };
C187B90A1FE063A40042D3B7 /* slc_builder.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = slc_builder.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
C18A75F5209940A500DC01BB /* JSONWriter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JSONWriter.h; path = dyld3/JSONWriter.h; sourceTree = "<group>"; };
C18A75F6209A18AC00DC01BB /* JSONReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JSONReader.h; path = dyld3/JSONReader.h; sourceTree = "<group>"; };
F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; sourceTree = "<group>"; };
F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = "<group>"; };
F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = "<group>"; };
- F93F46511FA420630060D9F9 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; sourceTree = "<group>"; };
+ F93F46511FA420630060D9F9 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/lib/execserver.defs; sourceTree = "<group>"; };
F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_sim_shared_cache.xcconfig; sourceTree = "<group>"; };
F94942B21E6796D40019AE08 /* closured.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = closured.1; sourceTree = "<group>"; };
F94C22241E513CA90079E5DD /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
F97C61A01D9CA6B800A84CD7 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = dyld3/Logging.cpp; sourceTree = "<group>"; usesTabs = 0; };
F97C61A11D9CA6B800A84CD7 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = dyld3/Logging.h; sourceTree = "<group>"; usesTabs = 0; };
F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_closure_util; sourceTree = BUILT_PRODUCTS_DIR; };
- F97FF3561C23638F000ACDD2 /* nocr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nocr; sourceTree = BUILT_PRODUCTS_DIR; };
- F97FF35F1C236402000ACDD2 /* nocr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nocr.c; path = testing/nocr/nocr.c; sourceTree = "<group>"; };
+ F97FF35F1C236402000ACDD2 /* nocr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = nocr.cpp; path = testing/nocr/nocr.cpp; sourceTree = "<group>"; };
F97FF3631C237F5C000ACDD2 /* nocr.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = nocr.1; path = ../../../testing/nocr/nocr.1; sourceTree = "<group>"; };
F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.h; sourceTree = "<group>"; };
F98692001DC3EF4800CBEDE6 /* Diagnostics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Diagnostics.h; path = dyld3/Diagnostics.h; sourceTree = "<group>"; usesTabs = 0; };
F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; sourceTree = "<group>"; };
F9AC7E930B7BB67700FEB38B /* version.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = BUILT_PRODUCTS_DIR; };
F9B01E3D0739ABDE00CF981B /* dyld.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld.exp; path = src/dyld.exp; sourceTree = SOURCE_ROOT; };
- F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = make_ios_dyld_cache.cpp; path = "dyld3/shared-cache/make_ios_dyld_cache.cpp"; sourceTree = "<group>"; };
F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs_macOS.cpp; path = dyld3/APIs_macOS.cpp; sourceTree = "<group>"; };
F9C275581DA71A13007A5D8A /* Loading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Loading.cpp; path = dyld3/Loading.cpp; sourceTree = "<group>"; usesTabs = 0; };
F9C275591DA71A13007A5D8A /* Loading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Loading.h; path = dyld3/Loading.h; sourceTree = "<group>"; usesTabs = 0; };
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */,
- 378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */,
+ C123176B22B9B4F00046E3E5 /* Bom.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 3715A2FB232320BC0059433D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3715A303232320BD0059433D /* libtest_support.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
- 377685FD1AC4B27D00026E6C /* Frameworks */ = {
+ 3721B6A52321A75B006F6AB7 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */,
- 378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 37065AA82310889D00A20034 /* libtest_support.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 3715A2FF232320BC0059433D /* local_test_runner */ = {
+ isa = PBXGroup;
+ children = (
+ 3715A300232320BD0059433D /* ContainerizedTestRunner.mm */,
+ 3715A302232320BD0059433D /* Info.plist */,
+ );
+ path = local_test_runner;
+ sourceTree = "<group>";
+ };
37918ABF2058908000F39A77 /* tracing */ = {
isa = PBXGroup;
children = (
37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */,
F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */,
F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */,
- 37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */,
- 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */,
F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */,
F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */,
F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */,
F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */,
F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */,
F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */,
- 37908A2C1E3A85A4009613FA /* Manifest.h */,
- 37908A281E3A853E009613FA /* Manifest.mm */,
F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */,
F98692101DC3EF6C00CBEDE6 /* ObjC2Abstraction.hpp */,
F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */,
F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */,
F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */,
+ C11ECA8F233C307C0011726F /* SharedCacheBuilder.h */,
+ C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */,
F902031F1DEE83C000AC3F76 /* StringUtils.h */,
37908A2D1E3A85A4009613FA /* Trie.hpp */,
F9D862441DC9759C000A199A /* dyld_closure_util.cpp */,
37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */,
- 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */,
- F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */,
F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */,
F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */,
F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */,
F9ED4C870630A72200DF4E74 = {
isa = PBXGroup;
children = (
+ 376AA37C23305CE10070C28C /* ContainerizedTestRunner.xctestplan */,
F9F6F4261C1FAF8000BD8FED /* testing */,
F971DD121A4A0E0700BBDD52 /* configs */,
F9ED4CBB0630A7AA00DF4E74 /* src */,
F9ED4CC30630A7BE00DF4E74 /* doc */,
F9ED4CBE0630A7B100DF4E74 /* include */,
+ 3715A2FF232320BC0059433D /* local_test_runner */,
F9ED4C990630A76000DF4E74 /* Products */,
F96D19A41D9363B7007AF3CE /* dyld3 */,
F939373D0A94FC4700070A07 /* launch-cache */,
F9F2A5590F7AEE9800B7C9EB /* libdsc.a */,
F99B8E670FEC121100701838 /* dyld_shared_cache_util */,
F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */,
- 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */,
- F97FF3561C23638F000ACDD2 /* nocr */,
F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */,
F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */,
C187B90A1FE063A40042D3B7 /* slc_builder.dylib */,
F92C7E1421E59840000D12B5 /* libdyld.dylib */,
F9556D3920C1F896004DF62A /* dyldinfo */,
F98E37952332D048003706B4 /* update_dyld_shared_cache_root_mode */,
+ 3721A635230CABAF00594066 /* libtest_support.a */,
+ 37065AA72310856E00A20034 /* nocr */,
+ 3721B6A82321A75B006F6AB7 /* chroot_util */,
+ 3715A2FE232320BC0059433D /* ContainerizedTestRunner.xctest */,
);
name = Products;
sourceTree = "<group>";
F9ED4CBB0630A7AA00DF4E74 /* src */ = {
isa = PBXGroup;
children = (
+ 37382F67230CADEE00E375CE /* test_support.cpp */,
F93F46511FA420630060D9F9 /* execserver.defs */,
- F97FF35F1C236402000ACDD2 /* nocr.c */,
+ F97FF35F1C236402000ACDD2 /* nocr.cpp */,
37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */,
+ 37CE9D192321A7EB001FBA91 /* chroot_util.cpp */,
F9ED4CC60630A7F100DF4E74 /* dyld_debugger.cpp */,
F9ED4CC70630A7F100DF4E74 /* dyld2.cpp */,
F9ED4CC80630A7F100DF4E74 /* dyld2.h */,
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
+ 3721A631230CABAF00594066 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F92C7E0221E59840000D12B5 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
productReference = 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */;
productType = "com.apple.product-type.tool";
};
- 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = {
+ 3715A2FD232320BC0059433D /* ContainerizedTestRunner */ = {
isa = PBXNativeTarget;
- buildConfigurationList = 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */;
+ buildConfigurationList = 3715A306232320BD0059433D /* Build configuration list for PBXNativeTarget "ContainerizedTestRunner" */;
buildPhases = (
- 377685F31AC4B27D00026E6C /* make dyld_cache_config.h */,
- 377685F41AC4B27D00026E6C /* Sources */,
- 377685FD1AC4B27D00026E6C /* Frameworks */,
- 377685FE1AC4B27D00026E6C /* CopyFiles */,
+ 3715A3092327003C0059433D /* Build Everything */,
+ 3715A2FA232320BC0059433D /* Sources */,
+ 3715A2FB232320BC0059433D /* Frameworks */,
+ 3715A2FC232320BC0059433D /* Resources */,
);
buildRules = (
);
dependencies = (
+ 3715A30B23272F890059433D /* PBXTargetDependency */,
);
- name = multi_dyld_shared_cache_builder;
- productName = update_os_interlinked_dylib;
- productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
+ name = ContainerizedTestRunner;
+ productName = local_test_runner;
+ productReference = 3715A2FE232320BC0059433D /* ContainerizedTestRunner.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 3721A634230CABAF00594066 /* test_support */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 3721A638230CABAF00594066 /* Build configuration list for PBXNativeTarget "test_support" */;
+ buildPhases = (
+ 3721A631230CABAF00594066 /* Headers */,
+ 3721A632230CABAF00594066 /* Sources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = test_support;
+ productName = test_support;
+ productReference = 3721A635230CABAF00594066 /* libtest_support.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ 3721B6A72321A75B006F6AB7 /* chroot_util */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 3721B6AE2321A75B006F6AB7 /* Build configuration list for PBXNativeTarget "chroot_util" */;
+ buildPhases = (
+ 3721B6A42321A75B006F6AB7 /* Sources */,
+ 3721B6A52321A75B006F6AB7 /* Frameworks */,
+ 3721B6A62321A75B006F6AB7 /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = chroot_util;
+ productName = chroot_builder;
+ productReference = 3721B6A82321A75B006F6AB7 /* chroot_util */;
productType = "com.apple.product-type.tool";
};
37F597CC2061EB4200F9B6F9 /* dyld_usage */ = {
);
name = nocr;
productName = nocr;
- productReference = F97FF3561C23638F000ACDD2 /* nocr */;
+ productReference = 37065AA72310856E00A20034 /* nocr */;
productType = "com.apple.product-type.tool";
};
F98E37762332D048003706B4 /* update_dyld_shared_cache_root_mode_tool */ = {
isa = PBXProject;
attributes = {
DefaultBuildSystemTypeForWorkspace = Latest;
- LastUpgradeCheck = 1000;
+ LastUpgradeCheck = 1120;
TargetAttributes = {
+ 3715A2FD232320BC0059433D = {
+ CreatedOnToolsVersion = 11.0;
+ ProvisioningStyle = Automatic;
+ };
+ 3721A634230CABAF00594066 = {
+ CreatedOnToolsVersion = 11.0;
+ ProvisioningStyle = Automatic;
+ };
+ 3721B6A72321A75B006F6AB7 = {
+ CreatedOnToolsVersion = 11.0;
+ ProvisioningStyle = Automatic;
+ };
37A0AD0A1C15FFF500731E50 = {
CreatedOnToolsVersion = 8.0;
};
};
buildConfigurationList = F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */;
compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
+ developmentRegion = en;
hasScannedForEncodings = 1;
knownRegions = (
- English,
- Japanese,
- French,
- German,
+ en,
+ Base,
);
mainGroup = F9ED4C870630A72200DF4E74;
productRefGroup = F9ED4C990630A76000DF4E74 /* Products */;
F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */,
F92C7DE521E59840000D12B5 /* libdyld_driverkit */,
F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */,
- 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
F98E37762332D048003706B4 /* update_dyld_shared_cache_root_mode_tool */,
3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */,
F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */,
F97FF3551C23638F000ACDD2 /* nocr */,
37F597CC2061EB4200F9B6F9 /* dyld_usage */,
F9556D3820C1F896004DF62A /* dyldinfo */,
+ 3721A634230CABAF00594066 /* test_support */,
+ 3721B6A72321A75B006F6AB7 /* chroot_util */,
+ 3715A2FD232320BC0059433D /* ContainerizedTestRunner */,
);
};
/* End PBXProject section */
+/* Begin PBXResourcesBuildPhase section */
+ 3715A2FC232320BC0059433D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
/* Begin PBXShellScriptBuildPhase section */
3703A1121B38C1B300ADBA7F /* make dyld_cache_config.h */ = {
isa = PBXShellScriptBuildPhase;
shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n if [ \"$?\" -eq \"0\" ]; then\n echo -n \"#define ARM64_32_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_32_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n fi\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
showEnvVarsInLog = 0;
};
+ 3715A3092327003C0059433D /* Build Everything */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ name = "Build Everything";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "env -i PATH=\"${PATH}\" xcodebuild install -sdk ${SDKROOT} -configuration ${CONFIGURATION} -target dyld -target libdyld -target dyld_tests TOOLCHAINS=\"${TOOLCHAINS}\" DSTROOT=${DERIVED_FILES_DIR}/TestRoot OBJROOT=${DERIVED_FILES_DIR}/objroot XCTestGenPath=${DERIVED_FILES_DIR}/XCTestGenerated.h\n${BUILT_PRODUCTS_DIR}/chroot_util -chroot ${DERIVED_FILES_DIR}/TestRoot -fallback / -add_file /bin/echo -add_file /bin/sh -add_file /bin/bash -add_file /bin/ls -add_file /usr/sbin/dtrace -add_file /sbin/mount -add_file /sbin/mount_devfs -add_file /usr/lib/libobjc-trampolines.dylib -add_file /usr/bin/leaks\n/bin/mkdir -p ${DERIVED_FILES_DIR}/TestRoot/dev\n/bin/mkdir -m 777 -p ${DERIVED_FILES_DIR}/TestRoot/tmp\n";
+ showEnvVarsInLog = 0;
+ };
371C117D208ADFC700FD9036 /* Suppress simulator dyld_usage */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
shellScript = "# dyld_usage requires libktrace which is not available in the simulator\n# The target builds a dummy app that we delete\nif [ \"${PRODUCT_NAME}\" != \"dyld_sim\" ]\nthen\nOBJROOT_USAGE=\"${TARGET_TEMP_DIR}/Objects_Usage\"\nxcodebuild install -target dyld_usage SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_USAGE}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi\n";
showEnvVarsInLog = 0;
};
- 377685F31AC4B27D00026E6C /* make dyld_cache_config.h */ = {
+ 37E2FC9D22F62FE1004AF213 /* install */ = {
isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
+ buildActionMask = 8;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
);
- name = "make dyld_cache_config.h";
+ name = install;
+ outputFileListPaths = (
+ );
outputPaths = (
- "$(DERIVED_FILE_DIR)/dyld_cache_config.h",
);
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/bash;
- shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n if [ \"$?\" -eq \"0\" ]; then\n echo -n \"#define ARM64_32_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_32_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n fi\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "#LLBUILD=$(xcrun --sdk $SDKROOT --find llbuild 2> /dev/null)\nNINJA=${LLBUILD:-`xcrun --sdk $SDKROOT --find ninja 2> /dev/null`}\nINSTALL_TARGET=\"install\"\n\nif [ ! -z \"$LLBUILD\" ]; then\n NINJA=\"$LLBUILD ninja build\"\nfi\n\nif [ ! -z \"$ONLY_BUILD_TEST\" ]; then\n INSTALL_TARGET=\"install-$BUILD_ONLY\"\nfi\n\n${NINJA} -C ${DERIVED_FILES_DIR} ${INSTALL_TARGET}\n";
showEnvVarsInLog = 0;
};
C1225E3E21FA84BF0079CF9C /* create dyld_cache_config.h */ = {
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "\nif [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n OBJROOT_LOCAL=\"${TARGET_TEMP_DIR}/Objects_Local\"\n xcodebuild install -target dyld_shared_cache_builder SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_LOCAL}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n if [ \"${RC_BRIDGE}\" != \"YES\" ]\n then\n OBJROOT_SIM=\"${TARGET_TEMP_DIR}/Objects_Sim\"\n xcodebuild install -target update_dyld_sim_shared_cache SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_SIM}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n fi\nelse\n OBJROOT_MAC=\"${TARGET_TEMP_DIR}/Objects_Mac\"\n xcodebuild install -target update_dyld_shared_cache_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_MAC}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n OBJROOT_MAC=\"${TARGET_TEMP_DIR}/Objects2_Mac\"\n xcodebuild install -target update_dyld_shared_cache_root_mode_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_MAC}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi\n";
+ shellScript = "if [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n OBJROOT_LOCAL=\"${TARGET_TEMP_DIR}/Objects_Local\"\n xcodebuild install -target dyld_shared_cache_builder SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_LOCAL}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n if [ \"${RC_BRIDGE}\" != \"YES\" ]\n then\n OBJROOT_SIM=\"${TARGET_TEMP_DIR}/Objects_Sim\"\n xcodebuild install -target update_dyld_sim_shared_cache SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_SIM}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n fi\nelse\n OBJROOT_MAC=\"${TARGET_TEMP_DIR}/Objects_Mac\"\n xcodebuild install -target update_dyld_shared_cache_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_MAC}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n OBJROOT_MAC=\"${TARGET_TEMP_DIR}/Objects2_Mac\"\n xcodebuild install -target update_dyld_shared_cache_root_mode_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_MAC}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi\n";
showEnvVarsInLog = 0;
};
F951DA862228E5560057BA43 /* install headers */ = {
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n rm -rf ${DSTROOT}/usr/local/include\n rm -rf ${DSTROOT}/usr/local/lib\n rm -rf ${DSTROOT}/usr/lib\nfi\n";
+ shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n rm -rf ${DSTROOT}/usr/local/include/mach-o/dsc_iterator.h\n rm -rf ${DSTROOT}/usr/local/include/mach-o/dsc_extractor.h\n rm -rf ${DSTROOT}/usr/local/lib/slc_builder.dylib\n rm -rf ${DSTROOT}/usr/local/lib/libdsc.a\n rm -rf ${DSTROOT}/usr/lib/dsc_extractor.bundle\nfi\n";
showEnvVarsInLog = 0;
};
F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = {
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "${SRCROOT}/testing/build_tests.py && cp ${SRCROOT}/testing/run_all_dyld_tests.py ${DSTROOT}/AppleInternal/CoreOS/tests/dyld/\n";
+ shellScript = "SYMROOT=${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/dyld_tests\nOBJROOT=${PROJECT_TEMP_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}\nSDKROOT=${SDKROOT:-$(xcrun -sdk macosx.internal --show-sdk-path)}\nDEPLOYMENT_TARGET_CLANG_FLAG_NAME=${DEPLOYMENT_TARGET_CLANG_FLAG_NAME:-\"mmacosx-version-min\"}\nARCHS=${RC_ARCHS}\nDERIVED_FILES_DIR=${DERIVED_FILES_DIR}\nLDFLAGS=\"-L$BUILT_PRODUCTS_DIR\"\n#LLBUILD=$(xcrun --sdk $SDKROOT --find llbuild 2> /dev/null)\nNINJA=${LLBUILD:-`xcrun --sdk $SDKROOT --find ninja 2> /dev/null`}\nBUILD_TARGET=${ONLY_BUILD_TEST:-all}\n\nif [ ! -z \"$LLBUILD\" ]; then\n NINJA=\"$LLBUILD ninja build\"\nfi\n\nOSVERSION=\"10.14\"\nif [ ! -z \"$DEPLOYMENT_TARGET_CLANG_ENV_NAME\" ]; then\n OSVERSION=${!DEPLOYMENT_TARGET_CLANG_ENV_NAME}\nfi\n\nif [ -z \"$SRCROOT\" ]; then\n echo \"Error $$SRCROOT must be set\"\nfi\n\nif [ -z \"$ARCHS\" ]; then\n PLATFORM_NAME=${PLATFORM_NAME:macosx}\n case \"$PLATFORM_NAME\" in\n \"watchos\") ARCHS=\"armv7k arm64_32\"\n ;;\n \"appletvos\") ARCHS=\"arm64\"\n ;;\n \"macosx\") ARCHS=${ARCHS_STANDARD_64_BIT}\n ;;\n *) ARCHS=${ARCHS_STANDARD_32_64_BIT}\n ;;\n esac\nfi\n\nif [ -z \"$ARCHS\" ]; then\n ARCHS=\"x86_64\"\nfi\n\n/bin/mkdir -p ${DERIVED_FILES_DIR}\nTMPFILE=$(mktemp ${DERIVED_FILES_DIR}/config.ninja.XXXXXX)\n\necho \"OBJROOT = $OBJROOT\" >> $TMPFILE\necho \"OSFLAG = $DEPLOYMENT_TARGET_CLANG_FLAG_NAME\" >> $TMPFILE\necho \"OSVERSION = $OSVERSION\" >> $TMPFILE\necho \"SDKROOT = $SDKROOT\" >> $TMPFILE\necho \"SRCROOT = $SRCROOT\" >> $TMPFILE\necho \"SYMROOT = $SYMROOT\" >> $TMPFILE\necho \"BUILT_PRODUCTS_DIR = $BUILT_PRODUCTS_DIR\" >> $TMPFILE\necho \"INSTALL_GROUP = $INSTALL_GROUP\" >> $TMPFILE\necho \"INSTALL_MODE_FLAG = $INSTALL_MODE_FLAG\" >> $TMPFILE\necho \"INSTALL_OWNER = $INSTALL_OWNER\" >> $TMPFILE\necho \"INSTALL_DIR = $INSTALL_DIR\" >> $TMPFILE\necho \"USER_HEADER_SEARCH_PATHS = $USER_HEADER_SEARCH_PATHS\" >> $TMPFILE\necho \"SYSTEM_HEADER_SEARCH_PATHS = $SYSTEM_HEADER_SEARCH_PATHS\" >> $TMPFILE\necho \"ARCHS = $ARCHS\" >> $TMPFILE\necho \"DERIVED_FILES_DIR = $DERIVED_FILES_DIR\" >> $TMPFILE\necho \"LDFLAGS = $LDFLAGS\" >> $TMPFILE\n\n/usr/bin/rsync -vc $TMPFILE ${DERIVED_FILES_DIR}/config.ninja\n/bin/rm -f $TMPFILE\n\n${SRCROOT}/testing/build_ninja.py ${DERIVED_FILES_DIR}/config.ninja\n${NINJA} -C ${DERIVED_FILES_DIR} ${BUILD_TARGET}\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
F93D73531F8FF7C2007D9413 /* MachOAnalyzer.cpp in Sources */,
37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */,
C18A75F9209A1AF600DC01BB /* JSONReader.mm in Sources */,
+ C11ECA92233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */,
37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */,
37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */,
37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */,
37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */,
- 37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */,
37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */,
37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */,
C1F003CE213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */,
37908A321E3ED667009613FA /* FileUtils.cpp in Sources */,
- 37908A2E1E3A8632009613FA /* Manifest.mm in Sources */,
C1D268351FE0A77B009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
- 377685F41AC4B27D00026E6C /* Sources */ = {
+ 3715A2FA232320BC0059433D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3715A301232320BD0059433D /* ContainerizedTestRunner.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 3721A632230CABAF00594066 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- F93D734B1F8FF79E007D9413 /* MachOFile.cpp in Sources */,
- F93D734C1F8FF79E007D9413 /* MachOLoaded.cpp in Sources */,
- F93D734D1F8FF79E007D9413 /* MachOAnalyzer.cpp in Sources */,
- C1F003CD213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */,
- 37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */,
- 37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */,
- 37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */,
- 37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */,
- 37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */,
- 37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */,
- 37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */,
- 37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */,
- 37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */,
- 37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */,
- 37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */,
- 37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */,
- F93D73481F8FF780007D9413 /* Closure.cpp in Sources */,
- F93D73491F8FF780007D9413 /* ClosureWriter.cpp in Sources */,
- F93D734A1F8FF780007D9413 /* ClosureBuilder.cpp in Sources */,
- C1D268401FE9B464009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
+ 37065AAB2310A18300A20034 /* execserver.defs in Sources */,
+ 37382F68230CADEE00E375CE /* test_support.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 3721B6A42321A75B006F6AB7 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 375A4C75233DE09E00CFBD6B /* Diagnostics.cpp in Sources */,
+ 375A4C74233DE07600CFBD6B /* MachOFile.cpp in Sources */,
+ 37CE9D1A2321A7EB001FBA91 /* chroot_util.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C187B91B1FE0683F0042D3B7 /* OptimizerLinkedit.cpp in Sources */,
C187B90E1FE067CD0042D3B7 /* ClosureWriter.cpp in Sources */,
C187B91E1FE0684C0042D3B7 /* AdjustDylibSegments.cpp in Sources */,
- C187B9191FE0682C0042D3B7 /* BuilderUtils.mm in Sources */,
C187B90F1FE067D30042D3B7 /* ClosureBuilder.cpp in Sources */,
C187B9131FE067F10042D3B7 /* CacheBuilder.cpp in Sources */,
C187B9121FE067E60042D3B7 /* MachOAnalyzer.cpp in Sources */,
C187B9101FE067D90042D3B7 /* MachOFile.cpp in Sources */,
C187B9111FE067E10042D3B7 /* MachOLoaded.cpp in Sources */,
C187B90D1FE067C70042D3B7 /* Closure.cpp in Sources */,
+ C11ECA94233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
C1D268311FE0891C009F115B /* mrm_shared_cache_builder.cpp in Sources */,
C187B9141FE067FA0042D3B7 /* OptimizerBranches.cpp in Sources */,
);
F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */,
F98692191DC3EFDA00CBEDE6 /* FileUtils.cpp in Sources */,
F98692201DC3F99300CBEDE6 /* Diagnostics.cpp in Sources */,
+ C11ECA90233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */,
F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */,
F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */,
F96354361DCD74A400895049 /* FileUtils.cpp in Sources */,
F96354371DCD74A400895049 /* Diagnostics.cpp in Sources */,
F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */,
+ C11ECA93233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
F96354391DCD74A400895049 /* OptimizerObjC.cpp in Sources */,
37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */,
F963543C1DCD74A400895049 /* OptimizerLinkedit.cpp in Sources */,
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- F93F46521FA420850060D9F9 /* execserver.defs in Sources */,
- F97FF3611C23640C000ACDD2 /* nocr.c in Sources */,
+ F97FF3611C23640C000ACDD2 /* nocr.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F98E37852332D048003706B4 /* AdjustDylibSegments.cpp in Sources */,
F98E37862332D048003706B4 /* FileUtils.cpp in Sources */,
F98E37872332D048003706B4 /* Diagnostics.cpp in Sources */,
+ C11ECA91233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
F98E37882332D048003706B4 /* OptimizerObjC.cpp in Sources */,
F98E37892332D048003706B4 /* OptimizerBranches.cpp in Sources */,
F98E378A2332D048003706B4 /* OptimizerLinkedit.cpp in Sources */,
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ C176CB5C2321AB74009C1259 /* ClosureFileSystemPhysical.cpp in Sources */,
C1960ED02090D9F0007E3E6B /* Diagnostics.cpp in Sources */,
C1960ED42090DA09007E3E6B /* Closure.cpp in Sources */,
C1960ECF2090D9E5007E3E6B /* DyldSharedCache.cpp in Sources */,
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 3715A30B23272F890059433D /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 3721B6A72321A75B006F6AB7 /* chroot_util */;
+ targetProxy = 3715A30A23272F890059433D /* PBXContainerItemProxy */;
+ };
+ 37382F6A230CB46500E375CE /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 3721A634230CABAF00594066 /* test_support */;
+ targetProxy = 37382F69230CB46500E375CE /* PBXContainerItemProxy */;
+ };
37A0AD0F1C16000F00731E50 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */;
target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */;
targetProxy = F96543A01E343601003C5540 /* PBXContainerItemProxy */;
};
- F97FF3661C237F97000ACDD2 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = F97FF3551C23638F000ACDD2 /* nocr */;
- targetProxy = F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */;
- };
F99B8EB20FEC220C00701838 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
};
name = Release;
};
- 377686001AC4B27D00026E6C /* Debug */ = {
+ 3715A307232320BD0059433D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = NO;
+ CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
- DEAD_CODE_STRIPPING = YES;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- FRAMEWORK_SEARCH_PATHS = (
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
"$(inherited)",
- "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
- "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
- "$(SDKROOT)/AppleInternal/Library/Frameworks",
);
- GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ INFOPLIST_FILE = local_test_runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ OTHER_CFLAGS = "-DCHROOT_PATH=\\\"$(DERIVED_FILES_DIR)/TestRoot\\\"";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.apple.dyld.local-test-runner";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ STRIP_INSTALLED_PRODUCT = YES;
+ USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache $SRCROOT/testing/include";
+ };
+ name = Debug;
+ };
+ 3715A308232320BD0059433D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ INFOPLIST_FILE = local_test_runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ OTHER_CFLAGS = "-DCHROOT_PATH=\\\"$(DERIVED_FILES_DIR)/TestRoot\\\"";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.apple.dyld.local-test-runner";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ STRIP_INSTALLED_PRODUCT = YES;
+ USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache $SRCROOT/testing/include";
+ };
+ name = Release;
+ };
+ 3721A636230CABAF00594066 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_TESTABILITY = YES;
+ EXECUTABLE_PREFIX = lib;
+ EXPORTED_SYMBOLS_FILE = "";
+ GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_CPP_EXCEPTIONS = YES;
+ GCC_ENABLE_OBJC_EXCEPTIONS = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
- "BUILDING_CACHE_BUILDER=1",
);
- GCC_SYMBOLS_PRIVATE_EXTERN = NO;
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
- MACOSX_DEPLOYMENT_TARGET = 10.11;
- MTL_ENABLE_DEBUG_INFO = YES;
- ONLY_ACTIVE_ARCH = YES;
- OTHER_CFLAGS = (
- "-DBOM_SUPPORT=1",
- "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
- );
- PRODUCT_NAME = multi_dyld_shared_cache_builder;
+ GENERATE_MASTER_OBJECT_FILE = YES;
+ LLVM_LTO = NO;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ PRELINK_FLAGS = "-exported_symbols_list $(SRCROOT)/testing/lib/test_support.exp";
+ PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
+ SKIP_INSTALL = YES;
STRIP_INSTALLED_PRODUCT = NO;
- USER_HEADER_SEARCH_PATHS = "../launch-cache/";
- VALID_ARCHS = "x86_64 x86_64h";
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
+ USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include";
};
name = Debug;
};
- 377686011AC4B27D00026E6C /* Release */ = {
+ 3721A637230CABAF00594066 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = NO;
+ CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_UNREACHABLE_CODE = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
- DEAD_CODE_STRIPPING = YES;
- DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_NS_ASSERTIONS = NO;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- FRAMEWORK_SEARCH_PATHS = (
+ EXECUTABLE_PREFIX = lib;
+ EXPORTED_SYMBOLS_FILE = "";
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_ENABLE_OBJC_EXCEPTIONS = NO;
+ GCC_OPTIMIZATION_LEVEL = s;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GENERATE_MASTER_OBJECT_FILE = YES;
+ LLVM_LTO = NO;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ PRELINK_FLAGS = "-exported_symbols_list $(SRCROOT)/testing/lib/test_support.exp";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ SKIP_INSTALL = YES;
+ STRIP_INSTALLED_PRODUCT = NO;
+ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
+ USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include";
+ };
+ name = Release;
+ };
+ 3721B6AC2321A75B006F6AB7 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_CPP_EXCEPTIONS = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
"$(inherited)",
- "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
- "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
- "$(SDKROOT)/AppleInternal/Library/Frameworks",
);
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_ENABLE_CPP_EXCEPTIONS = YES;
- GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
- MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = macosx.internal;
+ };
+ name = Debug;
+ };
+ 3721B6AD2321A75B006F6AB7 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_ENABLE_CPP_EXCEPTIONS = NO;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
- OTHER_CFLAGS = (
- "-DBOM_SUPPORT=1",
- "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
- );
- PRODUCT_NAME = multi_dyld_shared_cache_builder;
+ MTL_FAST_MATH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
- STRIP_INSTALLED_PRODUCT = NO;
- USER_HEADER_SEARCH_PATHS = "../launch-cache/";
- VALID_ARCHS = "x86_64 x86_64h";
- VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
+ "BUILDING_DYLDINFO=1",
"$(inherited)",
);
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_DYLDINFO=1";
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
+ USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include";
};
name = Debug;
};
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx.internal;
+ USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ OTHER_CFLAGS = "-DBUILDING_SHARED_CACHE_UTIL=1";
PRODUCT_NAME = dyld_shared_cache_util;
SDKROOT = macosx.internal;
SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
GCC_DYNAMIC_NO_PIC = NO;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ OTHER_CFLAGS = "-DBUILDING_SHARED_CACHE_UTIL=1";
PRODUCT_NAME = dyld_shared_cache_util;
SDKROOT = macosx.internal;
SKIP_INSTALL = NO;
);
OTHER_LDFLAGS = (
"-Wl,-no_inits",
+ "$(USE_CHAINED_FIXUPS)",
"-nostdlib",
"-lCrashReporterClient",
"$(LIBSYSTEM_LIBS)",
);
OTHER_LDFLAGS = (
"-Wl,-no_inits",
+ "$(USE_CHAINED_FIXUPS)",
"-nostdlib",
"-lCrashReporterClient",
"$(LIBSYSTEM_LIBS)",
HEADER_SEARCH_PATHS = ./include;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx.internal;
+ STRIP_INSTALLED_PRODUCT = NO;
USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache";
WARNING_CFLAGS = "-Wimplicit-fallthrough";
};
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator";
+ SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders";
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/dyld3 $(SRCROOT)/shared-cache $(SRCROOT)/include $(SRCROOT)/testing/include";
};
name = Debug;
};
buildSettings = {
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator";
+ SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders";
+ USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/dyld3 $(SRCROOT)/shared-cache $(SRCROOT)/include $(SRCROOT)/testing/include";
};
name = Release;
};
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */ = {
+ 3715A306232320BD0059433D /* Build configuration list for PBXNativeTarget "ContainerizedTestRunner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3715A307232320BD0059433D /* Debug */,
+ 3715A308232320BD0059433D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 3721A638230CABAF00594066 /* Build configuration list for PBXNativeTarget "test_support" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 3721A636230CABAF00594066 /* Debug */,
+ 3721A637230CABAF00594066 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 3721B6AE2321A75B006F6AB7 /* Build configuration list for PBXNativeTarget "chroot_util" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- 377686001AC4B27D00026E6C /* Debug */,
- 377686011AC4B27D00026E6C /* Release */,
+ 3721B6AC2321A75B006F6AB7 /* Debug */,
+ 3721B6AD2321A75B006F6AB7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
#include <ptrauth.h>
#endif
+extern mach_header __dso_handle;
namespace dyld {
extern dyld_all_image_infos dyld_all_image_infos;
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);
});
// 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
// dyld3 binds all such missing symbols to this one handler.
// We need the crash log to contain the backtrace so someone can
// figure out the symbol.
- abort_report_np("missing lazy symbol called");
+
+ auto allImageInfos = gAllImages.oldAllImageInfo();
+ allImageInfos->errorKind = DYLD_EXIT_REASON_SYMBOL_MISSING;
+ allImageInfos->errorClientOfDylibPath = "<unknown>";
+ allImageInfos->errorTargetDylibPath = "<unknown>";
+ allImageInfos->errorSymbol = "<unknown>";
+
+ halt("missing lazy symbol called");
}
const char* _dyld_get_objc_selector(const char* selName)
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;
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;
return (raw != rhs.raw);
}
};
- static_assert(sizeof(ResolvedSymbolTarget) == 8);
+ static_assert(sizeof(ResolvedSymbolTarget) == 8, "Invalid size");
// ObjC optimisations
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);
usedFallbackPaths : 1,
initImageCount : 16,
hasInsertedLibraries : 1,
+ hasProgVars : 1,
usedInterposing : 1,
- padding : 12;
+ padding : 11;
};
const Flags& getFlags() const;
};
case AtPath::all:
break;
}
- if ( strncmp(loadPath, "@loader_path/", 13) != 0 )
- return false;
-
- strlcpy(fixedPath, loadedImage.path(), PATH_MAX);
- char* lastSlash = strrchr(fixedPath, '/');
- if ( lastSlash != nullptr ) {
- strcpy(lastSlash+1, &loadPath[13]);
- return true;
+ if ( strncmp(loadPath, "@loader_path/", 13) == 0 ) {
+ strlcpy(fixedPath, loadedImage.path(), PATH_MAX);
+ char* lastSlash = strrchr(fixedPath, '/');
+ if ( lastSlash != nullptr ) {
+ strcpy(lastSlash+1, &loadPath[13]);
+ return true;
+ }
}
+ else if ( fromLCRPATH && (strcmp(loadPath, "@loader_path") == 0) ) {
+ // <rdar://problem/52881387> in LC_RPATH allow "@loader_path" without trailing slash
+ strlcpy(fixedPath, loadedImage.path(), PATH_MAX);
+ char* lastSlash = strrchr(fixedPath, '/');
+ if ( lastSlash != nullptr ) {
+ lastSlash[1] = '\0';
+ return true;
+ }
+ }
+
return false;
}
case AtPath::all:
break;
}
- if ( strncmp(loadPath, "@executable_path/", 17) != 0 )
- return false;
- if ( _atPathHandling != AtPath::all )
- return false;
-
- strlcpy(fixedPath, _mainProgLoadPath, PATH_MAX);
- char* lastSlash = strrchr(fixedPath, '/');
- if ( lastSlash != nullptr ) {
- strcpy(lastSlash+1, &loadPath[17]);
- return true;
+ if ( strncmp(loadPath, "@executable_path/", 17) == 0 ) {
+ strlcpy(fixedPath, _mainProgLoadPath, PATH_MAX);
+ char* lastSlash = strrchr(fixedPath, '/');
+ if ( lastSlash != nullptr ) {
+ strcpy(lastSlash+1, &loadPath[17]);
+ return true;
+ }
+ }
+ else if ( fromLCRPATH && (strcmp(loadPath, "@executable_path") == 0) ) {
+ // <rdar://problem/52881387> in LC_RPATH allow "@executable_path" without trailing slash
+ strlcpy(fixedPath, _mainProgLoadPath, PATH_MAX);
+ char* lastSlash = strrchr(fixedPath, '/');
+ if ( lastSlash != nullptr ) {
+ lastSlash[1] = '\0';
+ return true;
+ }
}
+
return false;
}
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;
}
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) {
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:
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) {
return "warning";
case TypedBytes::Type::duplicateClassesTable:
return "duplicateClassesTable";
+ case TypedBytes::Type::progVars:
+ return "programVars";
}
}
root.map["objc-duplicate-class-warnings"].array.push_back(warningNode);
});
+ // add program vars info for old macOS binaries
+ uint32_t progVarsOffset;
+ if ( closure->hasProgramVars(progVarsOffset) )
+ root.map["program-vars-offset"].value = hex8(progVarsOffset);
+
#if 0
append(TypedBytes::Type::bootUUID, temp, paddedSize);
}
+void LaunchClosureWriter::setHasProgramVars(uint32_t offset)
+{
+ getFlags().hasProgVars = true;
+ append(TypedBytes::Type::progVars, &offset, sizeof(uint32_t));
+}
+
void LaunchClosureWriter::setObjCSelectorInfo(const Array<uint8_t>& hashTable, const Array<Image::ObjCSelectorImage>& hashTableImages) {
uint32_t count = (uint32_t)hashTableImages.count();
uint32_t totalSize = (uint32_t)(sizeof(count) + (sizeof(Image::ObjCSelectorImage) * count) + hashTable.count());
void setObjCClassAndProtocolInfo(const Array<uint8_t>& classHashTable, const Array<uint8_t>& protocolHashTable,
const Array<Image::ObjCClassImage>& hashTableImages);
void setObjCDuplicateClassesInfo(const Array<uint8_t>& hashTable);
+ void setHasProgramVars(uint32_t offset);
private:
LaunchClosure::Flags& getFlags();
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;
// 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)
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) )
}
}
- 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;
}
}
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;
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;
}
}
}
+ 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);
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 ) {
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;
}
}
}
+ 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);
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);
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);
}
}
-
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;
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");
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");
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");
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;
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;
}
if ( !(isArch("x86_64") || isArch("x86_64h")) )
return true;
+ if ( hasChainedFixups() )
+ return true;
+
__block bool rebasesOk = true;
Diagnostics diag;
uint64_t startVMAddr = preferredLoadAddress();
}
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
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;
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;
}
}
}
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;
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;
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;
}
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;
}
}
}
+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 {
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;
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;
// 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;
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 {
uint64_t nameLocationVMAddr;
};
+ struct ObjCProperty {
+ uint64_t nameVMAddr; // & const char *
+ uint64_t attributesVMAddr; // & const char *
+ };
+
struct ObjCCategory {
uint64_t nameVMAddr;
uint64_t clsVMAddr;
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;
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;
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;
}
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));
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
{
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 ) {
}
break;
case DYLD_CHAINED_PTR_64:
+ case DYLD_CHAINED_PTR_64_OFFSET:
if ( !this->generic64.bind.bind )
return false;
bindOrdinal = this->generic64.bind.ordinal;
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 ) {
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 ) {
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
#include "MachOFile.h"
-class CacheBuilder;
+class SharedCacheBuilder;
namespace dyld3 {
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 };
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;
::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;
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 = {
&entry_setRestrictions,
&entry_setNotifyMonitoringDyldMain,
&entry_setNotifyMonitoringDyld,
- &entry_setHasCacheOverrides
+ &entry_setHasCacheOverrides,
+ &entry_setProgramVars,
};
VIS_HIDDEN void _dyld_atfork_prepare()
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 };
const char* imagePaths[]));
// added in version 7
void (*setHasCacheOverrides)(bool someCacheImageOverriden);
+
+ // added in version 8
+ void (*setProgramVars)(struct ProgramVars* progVars);
};
extern const LibDyldEntryVector entryVectorForDyld;
#include "MachOFileAbstraction.hpp"
#include "MachOLoaded.h"
#include "MachOAnalyzer.h"
+#include "mach-o/fixup-chains.h"
#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker,
uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress);
void adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker);
+ void adjustRebaseChains(CacheBuilder::ASLR_Tracker& aslrTracker);
void slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker);
void adjustSymbolTable();
void adjustChainedFixups();
void 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;
macho_linkedit_data_command<P>* _dataInCodeCmd = nullptr;
macho_linkedit_data_command<P>* _exportTrieCmd = nullptr;
macho_linkedit_data_command<P>* _chainedFixupsCmd = nullptr;
+ uint16_t _chainedFixupsFormat = 0;
std::vector<uint64_t> _segOrigStartAddresses;
std::vector<uint64_t> _segSlides;
std::vector<macho_segment_command<P>*> _segCmds;
break;
case LC_DYLD_CHAINED_FIXUPS:
_chainedFixupsCmd = (macho_linkedit_data_command<P>*)cmd;
+ _chainedFixupsFormat = dyld3::MachOAnalyzer::chainedPointerFormat((dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff()]);
break;
case LC_DYLD_EXPORTS_TRIE:
_exportTrieCmd = (macho_linkedit_data_command<P>*)cmd;
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);
adjustSymbolTable();
if ( _diagnostics.hasError() )
return;
- adjustChainedFixups();
if ( _diagnostics.hasError() )
return;
rebuildLinkEditAndLoadCommands(textCoalescer);
return (instruction & 0xFFF0F000) | (imm4 << 16) | imm12;
}
+
+template <typename P>
+void Adjustor<P>::convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide)
+{
+ assert(chainPtr->arm64e.authRebase.bind == 0);
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgPtr = *chainPtr;
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp;
+ if ( chainPtr->arm64e.authRebase.auth ) {
+ uint64_t targetVMAddr = orgPtr.arm64e.authRebase.target + _segOrigStartAddresses[0] + targetSlide;
+ // we need to change the rebase to point to the new address in the dyld cache, but it may not fit
+ tmp.arm64e.authRebase.target = targetVMAddr;
+ if ( tmp.arm64e.authRebase.target == targetVMAddr ) {
+ // everything fits, just update target
+ chainPtr->arm64e.authRebase.target = targetVMAddr;
+ return;
+ }
+ // see if it fits in a plain rebase
+ tmp.arm64e.rebase.target = targetVMAddr;
+ if ( tmp.arm64e.rebase.target == targetVMAddr ) {
+ // does fit in plain rebase, so convert to that and store auth data in side table
+ aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key);
+ chainPtr->arm64e.rebase.target = targetVMAddr;
+ chainPtr->arm64e.rebase.high8 = 0;
+ chainPtr->arm64e.rebase.next = orgPtr.arm64e.rebase.next;
+ chainPtr->arm64e.rebase.bind = 0;
+ chainPtr->arm64e.rebase.auth = 0;
+ return;
+ }
+ // target cannot fit into rebase chain, so store target in side table
+ aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key);
+ aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
+ chainPtr->arm64e.rebase.target = CacheBuilder::kRebaseTargetInSideTableArm64e; // magic value that means look in side table
+ chainPtr->arm64e.rebase.high8 = 0;
+ chainPtr->arm64e.rebase.next = orgPtr.arm64e.rebase.next;
+ chainPtr->arm64e.rebase.bind = 0;
+ chainPtr->arm64e.rebase.auth = 0;
+ return;
+ }
+ else {
+ uint64_t targetVMAddr = orgPtr.arm64e.rebase.target + targetSlide;
+ tmp.arm64e.rebase.target = targetVMAddr;
+ if ( tmp.arm64e.rebase.target == targetVMAddr ) {
+ // target dyld cache address fits in plain rebase, so all we need to do is adjust that
+ chainPtr->arm64e.rebase.target = targetVMAddr;
+ return;
+ }
+ // target cannot fit into rebase chain, so store target in side table
+ aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
+ chainPtr->arm64e.rebase.target = CacheBuilder::kRebaseTargetInSideTableArm64e; // magic value that means look in side table
+ }
+}
+
+
+template <typename P>
+void Adjustor<P>::convertGeneric64RebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide)
+{
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgPtr = *chainPtr;
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp;
+
+ uint64_t targetVMAddr = orgPtr.generic64.rebase.target + targetSlide;
+ // we need to change the rebase to point to the new address in the dyld cache, but it may not fit
+ tmp.generic64.rebase.target = targetVMAddr;
+ if ( tmp.generic64.rebase.target == targetVMAddr ) {
+ // everything fits, just update target
+ chainPtr->generic64.rebase.target = targetVMAddr;
+ return;
+ }
+
+ // target cannot fit into rebase chain, so store target in side table
+ aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
+ chainPtr->generic64.rebase.target = CacheBuilder::kRebaseTargetInSideTableArm64; // magic value that means look in side table
+}
+
+
template <typename P>
void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress,
int64_t adjust, int64_t targetSlide, uint64_t imageStartAddress, uint64_t imageEndAddress,
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 ) {
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:
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;
}
+
+template <typename P>
+void Adjustor<P>::adjustRebaseChains(CacheBuilder::ASLR_Tracker& aslrTracker)
+{
+ const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)_mh;
+ const dyld_chained_fixups_header* chainHeader = (dyld_chained_fixups_header*)(&_linkeditBias[_chainedFixupsCmd->dataoff()]);
+ const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)chainHeader + chainHeader->starts_offset);
+ ma->forEachFixupInAllChains(_diagnostics, startsInfo, false,
+ ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+ switch ( segInfo->pointer_format ) {
+ case DYLD_CHAINED_PTR_64:
+ // only look at rebases
+ if ( fixupLoc->generic64.rebase.bind == 0 ) {
+ uint64_t rebaseTargetInDylib = fixupLoc->generic64.rebase.target;
+ uint64_t rebaseTargetInDyldcache = fixupLoc->generic64.rebase.target + slideForOrigAddress(rebaseTargetInDylib);
+ convertGeneric64RebaseToIntermediate(fixupLoc, aslrTracker, rebaseTargetInDyldcache);
+ aslrTracker.add(fixupLoc);
+ }
+ break;
+ case DYLD_CHAINED_PTR_64_OFFSET:
+ _diagnostics.error("unhandled 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _installName);
+ break;
+ default:
+ _diagnostics.error("unsupported chained fixup format %d", segInfo->pointer_format);
+ stop = true;
+ }
+ });
+}
+
+
template <typename P>
void Adjustor<P>::adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker)
{
void CacheBuilder::adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const
{
DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
- if ( _archLayout->is64 ) {
+ if ( _is64 ) {
Adjustor<Pointer64<LittleEndian>> adjustor64(cache, (macho_header<Pointer64<LittleEndian>>*)dylib.cacheLocation[0].dstSegment, dylib.cacheLocation, diag);
adjustor64.adjustImageForNewSegmentLocations(_aslrTracker, _lohTracker, _coalescedText, dylib.textCoalescer);
}
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * Copyright (c) 2017 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#include <set>
-#include <array>
-#include <string>
-#include <sstream>
-#include <iomanip> // std::setfill, std::setw
-#include <pthread.h>
-#include <mach/mach.h>
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <dispatch/dispatch.h>
-
-#include <Bom/Bom.h>
-#include <Security/Security.h>
-#include <Security/SecCodeSigner.h>
-#include <CommonCrypto/CommonCrypto.h>
-
-#include "Manifest.h"
-#include "Diagnostics.h"
-#include "FileUtils.h"
-
-#include "BuilderUtils.h"
-
-static dispatch_queue_t write_queue = dispatch_queue_create("com.apple.dyld.cache-builder.write", DISPATCH_QUEUE_CONCURRENT);
-static dispatch_group_t build_group = dispatch_group_create();
-
-dispatch_group_t buildGroup() {
- return build_group;
-}
-
-void insertFileInBom(const std::string& path, BOMBom bom)
-{
- std::vector<std::string> components;
- std::vector<std::string> processed_components;
- std::stringstream ss(path);
- std::string item;
-
- while (std::getline(ss, item, '/')) {
- if (!item.empty()) {
- components.push_back(item);
- }
- }
-
- std::string partialPath = ".";
- std::string lastComponent = components.back();
- components.pop_back();
- BOMFSObject fso = BOMFSObjectNew(BOMDirectoryType);
- BOMFSObjectSetFlags(fso, B_PATHONLY);
- BOMFSObjectSetPathName(fso, ".", true);
- BOMFSObjectSetShortName(fso, ".", true);
- (void)BOMBomInsertFSObject(bom, fso, false);
- BOMFSObjectFree(fso);
-
- for (const auto& component : components) {
- partialPath = partialPath + "/" + component;
- fso = BOMFSObjectNew(BOMDirectoryType);
- BOMFSObjectSetFlags(fso, B_PATHONLY);
- BOMFSObjectSetPathName(fso, partialPath.c_str(), true);
- BOMFSObjectSetShortName(fso, component.c_str(), true);
- (void)BOMBomInsertFSObject(bom, fso, false);
- BOMFSObjectFree(fso);
- }
-
- partialPath = partialPath + "/" + lastComponent;
- fso = BOMFSObjectNew(BOMFileType);
- BOMFSObjectSetFlags(fso, B_PATHONLY);
- BOMFSObjectSetPathName(fso, partialPath.c_str(), true);
- BOMFSObjectSetShortName(fso, lastComponent.c_str(), true);
- (void)BOMBomInsertFSObject(bom, fso, false);
- BOMFSObjectFree(fso);
-}
-
-void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot)
-{
- mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755);
-
- manifest.forEachConfiguration([&manifest, &masterDstRoot](const std::string& configName) {
- auto config = manifest.configuration(configName);
- std::vector<std::string> prodBomPaths;
- std::vector<std::string> devBomPaths;
-
- std::string runtimePath = "/System/Library/Caches/com.apple.dyld/";
- if (manifest.platform() == dyld3::Platform::macOS) {
- runtimePath = "/private/var/db/dyld/";
- }
-
- for (auto& arch : config.architectures) {
- std::string cachePath = "dyld_shared_cache_" + arch.first;
- prodBomPaths.push_back(cachePath);
- if (manifest.platform() != dyld3::Platform::macOS) {
- cachePath += ".development";
- }
- devBomPaths.push_back(cachePath);
- char buffer[MAXPATHLEN];
- sprintf(buffer, "%s/Boms/%s.prod.bom", masterDstRoot.c_str(), configName.c_str());
- BOMBom bom = BOMBomNew(buffer);
- for (auto& path : prodBomPaths) {
- insertFileInBom(runtimePath + path, bom);
- }
- BOMBomFree(bom);
-
- sprintf(buffer, "%s/Boms/%s.dev.bom", masterDstRoot.c_str(), configName.c_str());
- bom = BOMBomNew(buffer);
- for (auto& path : devBomPaths) {
- insertFileInBom(runtimePath + path, bom);
- }
- BOMBomFree(bom);
-
- sprintf(buffer, "%s/Boms/%s.full.bom", masterDstRoot.c_str(), configName.c_str());
- bom = BOMBomNew(buffer);
- for (auto& path : prodBomPaths) {
- insertFileInBom(runtimePath + path, bom);
- }
- for (auto& path : devBomPaths) {
- insertFileInBom(runtimePath + path, bom);
- }
- BOMBomFree(bom);
- }
- });
-}
-
-bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose,
- bool skipWrites, bool agileChooseSHA256CdHash, bool emitDevCaches, bool isLocallyBuiltCache)
-{
- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_queue_t warningQueue = dispatch_queue_create("com.apple.dyld.cache-builder.warnings", DISPATCH_QUEUE_SERIAL);
- std::vector<std::set<std::string>> dedupedCacheSets;
- if (dedupe) {
- manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) {
- auto config = manifest.configuration(configName);
- bool dupeFound = false;
-
- for (auto& cacheSet : dedupedCacheSets) {
- if (config == manifest.configuration(*cacheSet.begin())) {
- cacheSet.insert(configName);
- dupeFound = true;
- break;
- }
- }
-
- if (!dupeFound) {
- std::set<std::string> temp;
- temp.insert(configName);
- dedupedCacheSets.push_back(temp);
- }
- });
- } else {
- manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) {
- std::set<std::string> temp;
- temp.insert(configName);
- dedupedCacheSets.push_back(temp);
- });
- }
-
- std::vector<dyld3::BuildQueueEntry> buildQueue;
-
- for (auto& cacheSet : dedupedCacheSets) {
- //FIXME we may want to consider moving to hashes of UUID sets
- std::string setName;
-
- for (auto& archName : cacheSet) {
- if (!setName.empty()) {
- setName += "|";
- }
- setName += archName;
- }
-
- std::stringstream fileNameStream;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- std::array<uint8_t, CC_SHA1_DIGEST_LENGTH> digest = { 0 };
- CC_SHA1(setName.c_str(), (unsigned int)setName.length(), &digest[0]);
-#pragma clang diagnostic pop
-
- fileNameStream << std::hex << std::uppercase << std::setfill('0');
- for (int c : digest) {
- fileNameStream << std::setw(2) << c;
- }
-
- std::string fileName(fileNameStream.str());
-
- if (dedupe) {
- for (auto& config : cacheSet) {
- if (!skipWrites) {
- int err = symlink(("DedupedConfigs/" + fileName).c_str(), (masterDstRoot + "/" + config).c_str());
- if (err) {
- diags.warning("Could not create symlink '%s' -> 'DedupedConfigs/%s' (%d)", config.c_str(), fileName.c_str(), err);
- }
- }
- }
- }
-
- manifest.configuration(*cacheSet.begin()).forEachArchitecture([&masterDstRoot, &dedupe, &fileName, &setName, &manifest,
- &buildQueue, &cacheSet, skipWrites, verbose, emitDevCaches, isLocallyBuiltCache](const std::string& arch) {
- std::string configPath;
- std::string runtimePath = "/System/Library/Caches/com.apple.dyld/";
- if (manifest.platform() == dyld3::Platform::macOS) {
- runtimePath = "/private/var/db/dyld/";
- }
- if (dedupe) {
- configPath = masterDstRoot + "/DedupedConfigs/" + fileName + runtimePath;
- } else {
- configPath = masterDstRoot + runtimePath;
- }
-
- mkpath_np(configPath.c_str(), 0755);
- if (manifest.platform() == dyld3::Platform::macOS) {
- buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, false, setName + "/" + arch,
- isLocallyBuiltCache, skipWrites, verbose));
- } else {
- if (emitDevCaches)
- buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch + ".development", cacheSet, arch, false, setName + "/" + arch,
- isLocallyBuiltCache, skipWrites, verbose));
- buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, true, setName + "/" + arch,
- isLocallyBuiltCache, skipWrites, verbose));
- }
- });
- }
-
- __block bool cacheBuildFailure = false;
- __block std::set<std::string> warnings;
- __block std::set<std::string> errors;
-
- dispatch_sync(warningQueue, ^{
- auto manifestWarnings = diags.warnings();
- //warnings.insert(manifestWarnings.begin(), manifestWarnings.end());
- });
-
- bool requuiresConcurrencyLimit = false;
- dispatch_semaphore_t concurrencyLimit = NULL;
- // Limit cuncurrency to 8 threads for machines with 32GB of RAM and to 1 thread if we have 4GB or less of memory
- uint64_t memSize = 0;
- size_t sz = sizeof(memSize);;
- if ( sysctlbyname("hw.memsize", &memSize, &sz, NULL, 0) == 0 ) {
- if ( memSize <= 0x100000000ULL ) {
- fprintf(stderr, "Detected 4Gb or less of memory, limiting concurrency to 1 thread\n");
- requuiresConcurrencyLimit = true;
- concurrencyLimit = dispatch_semaphore_create(1);
- } else if ( memSize <= 0x800000000ULL ) {
- fprintf(stderr, "Detected 32Gb or less of memory, limiting concurrency to 8 threads\n");
- requuiresConcurrencyLimit = true;
- concurrencyLimit = dispatch_semaphore_create(8);
- }
- }
-
- dispatch_apply(buildQueue.size(), queue, ^(size_t index) {
- auto queueEntry = buildQueue[index];
- pthread_setname_np(queueEntry.options.loggingPrefix.substr(0, MAXTHREADNAMESIZE - 1).c_str());
-
- // Horrible hack to limit concurrency in low spec build machines.
- if (requuiresConcurrencyLimit) { dispatch_semaphore_wait(concurrencyLimit, DISPATCH_TIME_FOREVER); }
- DyldSharedCache::CreateResults results = DyldSharedCache::create(queueEntry.options, queueEntry.fileSystem, queueEntry.dylibsForCache, queueEntry.otherDylibsAndBundles, queueEntry.mainExecutables);
- if (requuiresConcurrencyLimit) { dispatch_semaphore_signal(concurrencyLimit); }
-
- dispatch_sync(warningQueue, ^{
- warnings.insert(results.warnings.begin(), results.warnings.end());
- bool chooseSecondCdHash = agileChooseSHA256CdHash;
- if (agileChooseSHA256CdHash && !results.agileSignature) {
- // Ignore this option for caches that are not signed agile (which is the majority).
- chooseSecondCdHash = false;
- }
- for (const auto& configName : queueEntry.configNames) {
- auto& configResults = manifest.configuration(configName).architecture(queueEntry.options.archs->name()).results;
- for (const auto& mh : results.evictions) {
- configResults.exclude(mh, "VM overflow, evicting");
- }
- configResults.warnings = results.warnings;
- if (queueEntry.options.optimizeStubs) {
- configResults.productionCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
- } else {
- configResults.developmentCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
- }
- }
- });
- if (!results.errorMessage.empty()) {
- fprintf(stderr, "[%s] ERROR: %s\n", queueEntry.options.loggingPrefix.c_str(), results.errorMessage.c_str());
- cacheBuildFailure = true;
- } else if (skipWrites) {
- fprintf(stderr, "[%s] Skipped writing cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str());
- }
- });
-
- // print any warnings
- for (const std::string& warn : warnings) {
- fprintf(stderr, "[WARNING] %s\n", warn.c_str());
- }
-
- int err = sync_volume_np(masterDstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT);
- if (err) {
- fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err));
- }
-
- return !cacheBuildFailure;
-}
* @APPLE_LICENSE_HEADER_END@
*/
+#include <assert.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/errno.h>
-#include <sys/fcntl.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <mach/mach_vm.h>
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-#include <mach/shared_region.h>
-#include <assert.h>
-#include <CommonCrypto/CommonHMAC.h>
-#include <CommonCrypto/CommonDigest.h>
-#include <CommonCrypto/CommonDigestSPI.h>
-#include <pthread/pthread.h>
-#include <apfs/apfs_fsctl.h>
-
-#include <string>
-#include <vector>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "MachOFileAbstraction.hpp"
-#include "CodeSigningTypes.h"
-#include "DyldSharedCache.h"
-#include "CacheBuilder.h"
-#include "FileAbstraction.hpp"
-#include "Trie.hpp"
-#include "FileUtils.h"
-#include "Diagnostics.h"
-#include "ClosureBuilder.h"
-#include "Closure.h"
-#include "ClosureFileSystemNull.h"
-#include "StringUtils.h"
-
-#if __has_include("dyld_cache_config.h")
- #include "dyld_cache_config.h"
-#else
- #define ARM_SHARED_REGION_START 0x1A000000ULL
- #define ARM_SHARED_REGION_SIZE 0x26000000ULL
- #define ARM64_SHARED_REGION_START 0x180000000ULL
- #define ARM64_SHARED_REGION_SIZE 0x100000000ULL
-#endif
-
-#ifndef ARM64_32_SHARED_REGION_START
- #define ARM64_32_SHARED_REGION_START 0x1A000000ULL
- #define ARM64_32_SHARED_REGION_SIZE 0x26000000ULL
-#endif
-
-#if ARM_SHARED_REGION_SIZE > 0x26000000ULL
- #define ARMV7K_CHAIN_BITS 0xC0000000
- #define ARMV7K_MAX 0x0
-#else
- #define ARMV7K_CHAIN_BITS 0xE0000000
- #define ARMV7K_MAX 0x20000000
-#endif
-
-const CacheBuilder::ArchLayout CacheBuilder::_s_archLayout[] = {
- { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x0, 0x40000000, 0x00FFFF0000000000, "x86_64", CS_PAGE_SIZE_4K, 12, 2, true, true, true },
- { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x0, 0x40000000, 0x00FFFF0000000000, "x86_64h", CS_PAGE_SIZE_4K, 12, 2, true, true, true },
- { SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386, 0x0, 0x00200000, 0x0, "i386", CS_PAGE_SIZE_4K, 12, 0, false, false, true },
- { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x0, 0x02000000, 0x00FFFF0000000000, "arm64", CS_PAGE_SIZE_4K, 14, 2, false, true, false },
-#if SUPPORT_ARCH_arm64e
- { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x0, 0x02000000, 0x00FFFF0000000000, "arm64e", CS_PAGE_SIZE_16K, 14, 2, false, true, false },
-#endif
-#if SUPPORT_ARCH_arm64_32
- { ARM64_32_SHARED_REGION_START, ARM64_32_SHARED_REGION_SIZE,0x0, 0x02000000, 0xC0000000, "arm64_32", CS_PAGE_SIZE_16K, 14, 6, false, false, true },
-#endif
- { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x0, 0x02000000, 0xE0000000, "armv7s", CS_PAGE_SIZE_4K, 14, 4, false, false, true },
- { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, ARMV7K_MAX, 0x00400000, ARMV7K_CHAIN_BITS, "armv7k", CS_PAGE_SIZE_4K, 14, 4, false, false, true },
- { 0x40000000, 0x40000000, 0x0, 0x02000000, 0x0, "sim-x86", CS_PAGE_SIZE_4K, 14, 0, false, false, true }
-};
-
-
-// These are dylibs that may be interposed, so stubs calling into them should never be bypassed
-const char* const CacheBuilder::_s_neverStubEliminateDylibs[] = {
- "/usr/lib/system/libdispatch.dylib",
- nullptr
-};
-
-// These are functions that are interposed by Instruments.app or ASan
-const char* const CacheBuilder::_s_neverStubEliminateSymbols[] = {
- "___bzero",
- "___cxa_atexit",
- "___cxa_throw",
- "__longjmp",
- "__objc_autoreleasePoolPop",
- "_accept",
- "_access",
- "_asctime",
- "_asctime_r",
- "_asprintf",
- "_atoi",
- "_atol",
- "_atoll",
- "_calloc",
- "_chmod",
- "_chown",
- "_close",
- "_confstr",
- "_ctime",
- "_ctime_r",
- "_dispatch_after",
- "_dispatch_after_f",
- "_dispatch_async",
- "_dispatch_async_f",
- "_dispatch_barrier_async_f",
- "_dispatch_group_async",
- "_dispatch_group_async_f",
- "_dispatch_source_set_cancel_handler",
- "_dispatch_source_set_event_handler",
- "_dispatch_sync_f",
- "_dlclose",
- "_dlopen",
- "_dup",
- "_dup2",
- "_endgrent",
- "_endpwent",
- "_ether_aton",
- "_ether_hostton",
- "_ether_line",
- "_ether_ntoa",
- "_ether_ntohost",
- "_fchmod",
- "_fchown",
- "_fclose",
- "_fdopen",
- "_fflush",
- "_fopen",
- "_fork",
- "_fprintf",
- "_free",
- "_freopen",
- "_frexp",
- "_frexpf",
- "_frexpl",
- "_fscanf",
- "_fstat",
- "_fstatfs",
- "_fstatfs64",
- "_fsync",
- "_ftime",
- "_getaddrinfo",
- "_getattrlist",
- "_getcwd",
- "_getgrent",
- "_getgrgid",
- "_getgrgid_r",
- "_getgrnam",
- "_getgrnam_r",
- "_getgroups",
- "_gethostbyaddr",
- "_gethostbyname",
- "_gethostbyname2",
- "_gethostent",
- "_getifaddrs",
- "_getitimer",
- "_getnameinfo",
- "_getpass",
- "_getpeername",
- "_getpwent",
- "_getpwnam",
- "_getpwnam_r",
- "_getpwuid",
- "_getpwuid_r",
- "_getsockname",
- "_getsockopt",
- "_gmtime",
- "_gmtime_r",
- "_if_indextoname",
- "_if_nametoindex",
- "_index",
- "_inet_aton",
- "_inet_ntop",
- "_inet_pton",
- "_initgroups",
- "_ioctl",
- "_lchown",
- "_lgamma",
- "_lgammaf",
- "_lgammal",
- "_link",
- "_listxattr",
- "_localtime",
- "_localtime_r",
- "_longjmp",
- "_lseek",
- "_lstat",
- "_malloc",
- "_malloc_create_zone",
- "_malloc_default_purgeable_zone",
- "_malloc_default_zone",
- "_malloc_good_size",
- "_malloc_make_nonpurgeable",
- "_malloc_make_purgeable",
- "_malloc_set_zone_name",
- "_mbsnrtowcs",
- "_mbsrtowcs",
- "_mbstowcs",
- "_memchr",
- "_memcmp",
- "_memcpy",
- "_memmove",
- "_memset",
- "_mktime",
- "_mlock",
- "_mlockall",
- "_modf",
- "_modff",
- "_modfl",
- "_munlock",
- "_munlockall",
- "_objc_autoreleasePoolPop",
- "_objc_setProperty",
- "_objc_setProperty_atomic",
- "_objc_setProperty_atomic_copy",
- "_objc_setProperty_nonatomic",
- "_objc_setProperty_nonatomic_copy",
- "_objc_storeStrong",
- "_open",
- "_opendir",
- "_poll",
- "_posix_memalign",
- "_pread",
- "_printf",
- "_pthread_attr_getdetachstate",
- "_pthread_attr_getguardsize",
- "_pthread_attr_getinheritsched",
- "_pthread_attr_getschedparam",
- "_pthread_attr_getschedpolicy",
- "_pthread_attr_getscope",
- "_pthread_attr_getstack",
- "_pthread_attr_getstacksize",
- "_pthread_condattr_getpshared",
- "_pthread_create",
- "_pthread_getschedparam",
- "_pthread_join",
- "_pthread_mutex_lock",
- "_pthread_mutex_unlock",
- "_pthread_mutexattr_getprioceiling",
- "_pthread_mutexattr_getprotocol",
- "_pthread_mutexattr_getpshared",
- "_pthread_mutexattr_gettype",
- "_pthread_rwlockattr_getpshared",
- "_pwrite",
- "_rand_r",
- "_read",
- "_readdir",
- "_readdir_r",
- "_readv",
- "_readv$UNIX2003",
- "_realloc",
- "_realpath",
- "_recv",
- "_recvfrom",
- "_recvmsg",
- "_remquo",
- "_remquof",
- "_remquol",
- "_scanf",
- "_send",
- "_sendmsg",
- "_sendto",
- "_setattrlist",
- "_setgrent",
- "_setitimer",
- "_setlocale",
- "_setpwent",
- "_shm_open",
- "_shm_unlink",
- "_sigaction",
- "_sigemptyset",
- "_sigfillset",
- "_siglongjmp",
- "_signal",
- "_sigpending",
- "_sigprocmask",
- "_sigwait",
- "_snprintf",
- "_sprintf",
- "_sscanf",
- "_stat",
- "_statfs",
- "_statfs64",
- "_strcasecmp",
- "_strcat",
- "_strchr",
- "_strcmp",
- "_strcpy",
- "_strdup",
- "_strerror",
- "_strerror_r",
- "_strlen",
- "_strncasecmp",
- "_strncat",
- "_strncmp",
- "_strncpy",
- "_strptime",
- "_strtoimax",
- "_strtol",
- "_strtoll",
- "_strtoumax",
- "_tempnam",
- "_time",
- "_times",
- "_tmpnam",
- "_tsearch",
- "_unlink",
- "_valloc",
- "_vasprintf",
- "_vfprintf",
- "_vfscanf",
- "_vprintf",
- "_vscanf",
- "_vsnprintf",
- "_vsprintf",
- "_vsscanf",
- "_wait",
- "_wait$UNIX2003",
- "_wait3",
- "_wait4",
- "_waitid",
- "_waitid$UNIX2003",
- "_waitpid",
- "_waitpid$UNIX2003",
- "_wcslen",
- "_wcsnrtombs",
- "_wcsrtombs",
- "_wcstombs",
- "_wordexp",
- "_write",
- "_writev",
- "_writev$UNIX2003",
- // <rdar://problem/22050956> always use stubs for C++ symbols that can be overridden
- "__ZdaPv",
- "__ZdlPv",
- "__Znam",
- "__Znwm",
-
- nullptr
-};
-
-
-CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem)
- : _options(options)
- , _fileSystem(fileSystem)
- , _fullAllocatedBuffer(0)
- , _diagnostics(options.loggingPrefix, options.verbose)
- , _archLayout(nullptr)
- , _aliasCount(0)
- , _slideInfoFileOffset(0)
- , _slideInfoBufferSizeAllocated(0)
- , _allocatedBufferSize(0)
- , _selectorStringsFromExecutables(0)
-{
-
- std::string targetArch = options.archs->name();
- if ( options.forSimulator && (options.archs == &dyld3::GradedArchs::i386) )
- targetArch = "sim-x86";
-
- for (const ArchLayout& layout : _s_archLayout) {
- if ( layout.archName == targetArch ) {
- _archLayout = &layout;
- break;
- }
- }
-
- if (!_archLayout) {
- _diagnostics.error("Tool was built without support for: '%s'", targetArch.c_str());
- }
-}
-
-
-std::string CacheBuilder::errorMessage()
-{
- return _diagnostics.errorMessage();
-}
-
-const std::set<std::string> CacheBuilder::warnings()
-{
- return _diagnostics.warnings();
-}
-
-const std::set<const dyld3::MachOAnalyzer*> CacheBuilder::evictions()
-{
- return _evictions;
-}
-
-void CacheBuilder::deleteBuffer()
-{
- // Cache buffer
- vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _archLayout->sharedMemorySize);
- _fullAllocatedBuffer = 0;
- _allocatedBufferSize = 0;
- // Local symbols buffer
- if ( _localSymbolsRegion.bufferSize != 0 ) {
- vm_deallocate(mach_task_self(), (vm_address_t)_localSymbolsRegion.buffer, _localSymbolsRegion.bufferSize);
- _localSymbolsRegion.buffer = 0;
- _localSymbolsRegion.bufferSize = 0;
- }
- // Code singatures
- vm_deallocate(mach_task_self(), (vm_address_t)_codeSignatureRegion.buffer, _codeSignatureRegion.bufferSize);
- _codeSignatureRegion.buffer = 0;
- _codeSignatureRegion.bufferSize = 0;
-}
-
-
-void CacheBuilder::makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder)
-{
- for (const LoadedMachO& dylib : dylibs) {
- _sortedDylibs.push_back({ &dylib, dylib.mappedFile.runtimePath, {} });
- }
-
- std::sort(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& a, const DylibInfo& b) {
- const auto& orderA = sortOrder.find(a.input->mappedFile.runtimePath);
- const auto& orderB = sortOrder.find(b.input->mappedFile.runtimePath);
- bool foundA = (orderA != sortOrder.end());
- bool foundB = (orderB != sortOrder.end());
-
- // Order all __DATA_DIRTY segments specified in the order file first, in
- // the order specified in the file, followed by any other __DATA_DIRTY
- // segments in lexicographic order.
- if ( foundA && foundB )
- return orderA->second < orderB->second;
- else if ( foundA )
- return true;
- else if ( foundB )
- return false;
-
- // Sort mac before iOSMac
- bool isIOSMacA = strncmp(a.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0;
- bool isIOSMacB = strncmp(b.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0;
- if (isIOSMacA != isIOSMacB)
- return !isIOSMacA;
-
- // Finally sort by path
- return a.input->mappedFile.runtimePath < b.input->mappedFile.runtimePath;
- });
-}
-
-
-inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
-{
- return (uint32_t)(abstime/1000/1000);
-}
-
-struct DylibAndSize
-{
- const CacheBuilder::LoadedMachO* input;
- const char* installName;
- uint64_t size;
-};
-
-uint64_t CacheBuilder::cacheOverflowAmount()
-{
- if ( _archLayout->sharedRegionsAreDiscontiguous ) {
- // for macOS x86_64 cache, need to check each region for overflow
- if ( _readExecuteRegion.sizeInUse > 0x60000000 )
- return (_readExecuteRegion.sizeInUse - 0x60000000);
-
- if ( _readWriteRegion.sizeInUse > 0x40000000 )
- return (_readWriteRegion.sizeInUse - 0x40000000);
-
- if ( _readOnlyRegion.sizeInUse > 0x3FE00000 )
- return (_readOnlyRegion.sizeInUse - 0x3FE00000);
- }
- else if ( _archLayout->textAndDataMaxSize != 0 ) {
- // for armv7k, limit is 512MB of TEX+DATA
- uint64_t totalTextAndData = _readWriteRegion.unslidLoadAddress + _readWriteRegion.sizeInUse - _readExecuteRegion.unslidLoadAddress;
- if ( totalTextAndData < _archLayout->textAndDataMaxSize )
- return 0;
- else
- return totalTextAndData - _archLayout->textAndDataMaxSize;
- }
- else {
- bool alreadyOptimized = (_readOnlyRegion.sizeInUse != _readOnlyRegion.bufferSize);
- uint64_t vmSize = _readOnlyRegion.unslidLoadAddress - _readExecuteRegion.unslidLoadAddress;
- if ( alreadyOptimized )
- vmSize += _readOnlyRegion.sizeInUse;
- else if ( _options.excludeLocalSymbols )
- vmSize += (_readOnlyRegion.sizeInUse * 37/100); // assume locals removal and LINKEDIT optimzation reduces LINKEDITs %37 of original size
- else
- vmSize += (_readOnlyRegion.sizeInUse * 80/100); // assume LINKEDIT optimzation reduces LINKEDITs to %80 of original size
- if ( vmSize > _archLayout->sharedMemorySize )
- return vmSize - _archLayout->sharedMemorySize;
- }
- // fits in shared region
- return 0;
-}
-
-size_t CacheBuilder::evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs)
-{
- // build a reverse map of all dylib dependencies
- __block std::map<std::string, std::set<std::string>> references;
- std::map<std::string, std::set<std::string>>* referencesPtr = &references;
- for (const DylibInfo& dylib : _sortedDylibs) {
- // Esnure we have an entry (even if it is empty)
- if (references.count(dylib.input->mappedFile.mh->installName()) == 0) {
- references[dylib.input->mappedFile.mh->installName()] = std::set<std::string>();
- };
- dylib.input->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
- references[loadPath].insert(dylib.input->mappedFile.mh->installName());
- });
- }
-
- // Find the sizes of all the dylibs
- std::vector<DylibAndSize> dylibsToSort;
- std::vector<DylibAndSize> sortedDylibs;
- for (const DylibInfo& dylib : _sortedDylibs) {
- const char* installName = dylib.input->mappedFile.mh->installName();
- __block uint64_t segsSize = 0;
- dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
- if ( strcmp(info.segName, "__LINKEDIT") != 0 )
- segsSize += info.vmSize;
- });
- dylibsToSort.push_back({ dylib.input, installName, segsSize });
- }
-
- // Build an ordered list of what to remove. At each step we do following
- // 1) Find all dylibs that nothing else depends on
- // 2a) If any of those dylibs are not in the order select the largest one of them
- // 2b) If all the leaf dylibs are in the order file select the last dylib that appears last in the order file
- // 3) Remove all entries to the removed file from the reverse dependency map
- // 4) Go back to one and repeat until there are no more evictable dylibs
- // This results in us always choosing the locally optimal selection, and then taking into account how that impacts
- // the dependency graph for subsequent selections
-
- bool candidateFound = true;
- while (candidateFound) {
- candidateFound = false;
- DylibAndSize candidate;
- uint64_t candidateOrder = 0;
- for(const auto& dylib : dylibsToSort) {
- const auto &i = referencesPtr->find(dylib.installName);
- assert(i != referencesPtr->end());
- if (!i->second.empty()) {
- continue;
- }
- const auto& j = _options.dylibOrdering.find(dylib.input->mappedFile.runtimePath);
- uint64_t order = 0;
- if (j != _options.dylibOrdering.end()) {
- order = j->second;
- } else {
- // Not in the order file, set order sot it goes to the front of the list
- order = UINT64_MAX;
- }
- if (order > candidateOrder ||
- (order == UINT64_MAX && candidate.size < dylib.size)) {
- // The new file is either a lower priority in the order file
- // or the same priority as the candidate but larger
- candidate = dylib;
- candidateOrder = order;
- candidateFound = true;
- }
- }
- if (candidateFound) {
- sortedDylibs.push_back(candidate);
- referencesPtr->erase(candidate.installName);
- for (auto& dependent : references) {
- (void)dependent.second.erase(candidate.installName);
- }
- auto j = std::find_if(dylibsToSort.begin(), dylibsToSort.end(), [&candidate](const DylibAndSize& dylib) {
- return (strcmp(candidate.installName, dylib.installName) == 0);
- });
- if (j != dylibsToSort.end()) {
- dylibsToSort.erase(j);
- }
- }
- }
-
- // build set of dylibs that if removed will allow cache to build
- for (DylibAndSize& dylib : sortedDylibs) {
- if ( _options.verbose )
- _diagnostics.warning("to prevent cache overflow, not caching %s", dylib.installName);
- _evictions.insert(dylib.input->mappedFile.mh);
- // Track the evicted dylibs so we can try build "other" dlopen closures for them.
- overflowDylibs.push_back(dylib.input);
- if ( dylib.size > reductionTarget )
- break;
- reductionTarget -= dylib.size;
- }
-
- // prune _sortedDylibs
- _sortedDylibs.erase(std::remove_if(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& dylib) {
- return (_evictions.count(dylib.input->mappedFile.mh) != 0);
- }),_sortedDylibs.end());
-
- return _evictions.size();
-}
-
-// Handles building a list of input files to the CacheBuilder itself.
-class CacheInputBuilder {
-public:
- CacheInputBuilder(const dyld3::closure::FileSystem& fileSystem,
- const dyld3::GradedArchs& archs, dyld3::Platform reqPlatform)
- : fileSystem(fileSystem), reqArchs(archs), reqPlatform(reqPlatform) { }
-
- // Loads and maps any MachOs in the given list of files.
- void loadMachOs(std::vector<CacheBuilder::InputFile>& inputFiles,
- std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
- std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
- std::vector<CacheBuilder::LoadedMachO>& executables,
- std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles) {
-
- std::map<std::string, uint64_t> dylibInstallNameMap;
- for (CacheBuilder::InputFile& inputFile : inputFiles) {
- char realerPath[MAXPATHLEN];
- dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchs, reqPlatform, realerPath);
- const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
- if (ma == nullptr) {
- couldNotLoadFiles.emplace_back((CacheBuilder::LoadedMachO){ DyldSharedCache::MappedMachO(), loadedFileInfo, &inputFile });
- continue;
- }
-
- DyldSharedCache::MappedMachO mappedFile(inputFile.path, ma, loadedFileInfo.sliceLen, false, false,
- loadedFileInfo.sliceOffset, loadedFileInfo.mtime, loadedFileInfo.inode);
-
- // The file can be loaded with the given slice, but we may still want to exlude it from the cache.
- if (ma->isDylib()) {
- std::string installName = ma->installName();
-
- // Let the platform exclude the file before we do anything else.
- if (platformExcludesInstallName(installName)) {
- inputFile.diag.verbose("Platform excluded file\n");
- fileSystem.unloadFile(loadedFileInfo);
- continue;
- }
-
- if (!ma->canBePlacedInDyldCache(inputFile.path, ^(const char* msg) {
- inputFile.diag.warning("Dylib located at '%s' cannot be placed in cache because: %s", inputFile.path, msg);
- })) {
- // TODO: Add exclusion lists here?
- // Probably not as we already applied the dylib exclusion list.
- if (!ma->canHavePrecomputedDlopenClosure(inputFile.path, ^(const char* msg) {
- inputFile.diag.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile.path, msg);
- }) ) {
- fileSystem.unloadFile(loadedFileInfo);
- continue;
- }
- otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
- continue;
- }
-
- // Otherwise see if we have another file with this install name
- auto iteratorAndInserted = dylibInstallNameMap.insert(std::make_pair(installName, dylibsToCache.size()));
- if (iteratorAndInserted.second) {
- // We inserted the dylib so we haven't seen another with this name.
- if (installName[0] != '@' && installName != inputFile.path) {
- inputFile.diag.warning("Dylib located at '%s' has installname '%s'", inputFile.path, installName.c_str());
- }
-
- dylibsToCache.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
- } else {
- // We didn't insert this one so we've seen it before.
- CacheBuilder::LoadedMachO& previousLoadedMachO = dylibsToCache[iteratorAndInserted.first->second];
- inputFile.diag.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), inputFile.path, previousLoadedMachO.mappedFile.runtimePath.c_str());
-
- // This is the "Good" one, overwrite
- if (inputFile.path == installName) {
- // Unload the old one
- fileSystem.unloadFile(previousLoadedMachO.loadedFileInfo);
-
- // And replace with this one.
- previousLoadedMachO.mappedFile = mappedFile;
- previousLoadedMachO.loadedFileInfo = loadedFileInfo;
- }
- }
- } else if (ma->isBundle()) {
- // TODO: Add exclusion lists here?
- if (!ma->canHavePrecomputedDlopenClosure(inputFile.path, ^(const char* msg) {
- inputFile.diag.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile.path, msg);
- }) ) {
- fileSystem.unloadFile(loadedFileInfo);
- continue;
- }
- otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
- } else if (ma->isDynamicExecutable()) {
- if (platformExcludesExecutablePath_macOS(inputFile.path)) {
- inputFile.diag.verbose("Platform excluded file\n");
- fileSystem.unloadFile(loadedFileInfo);
- continue;
- }
- executables.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
- } else {
- inputFile.diag.verbose("Unsupported mach file type\n");
- fileSystem.unloadFile(loadedFileInfo);
- }
- }
- }
-
-private:
-
-
-
- static bool platformExcludesInstallName_macOS(const std::string& installName) {
- return false;
- }
-
- static bool platformExcludesInstallName_iOS(const std::string& installName) {
- if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib" )
- return true;
- if ( installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" )
- return true;
- return false;
- }
-
- static bool platformExcludesInstallName_tvOS(const std::string& installName) {
- return platformExcludesInstallName_iOS(installName);
- }
-
- static bool platformExcludesInstallName_watchOS(const std::string& installName) {
- return platformExcludesInstallName_iOS(installName);
- }
-
- static bool platformExcludesInstallName_bridgeOS(const std::string& installName) {
- return platformExcludesInstallName_iOS(installName);
- }
-
- // Returns true if the current platform requires that this install name be excluded from the shared cache
- // Note that this overrides any exclusion from anywhere else.
- bool platformExcludesInstallName(const std::string& installName) {
- switch (reqPlatform) {
- case dyld3::Platform::unknown:
- return false;
- case dyld3::Platform::macOS:
- return platformExcludesInstallName_macOS(installName);
- case dyld3::Platform::iOS:
- return platformExcludesInstallName_iOS(installName);
- case dyld3::Platform::tvOS:
- return platformExcludesInstallName_tvOS(installName);
- case dyld3::Platform::watchOS:
- return platformExcludesInstallName_watchOS(installName);
- case dyld3::Platform::bridgeOS:
- return platformExcludesInstallName_bridgeOS(installName);
- case dyld3::Platform::iOSMac:
- return false;
- case dyld3::Platform::iOS_simulator:
- return false;
- case dyld3::Platform::tvOS_simulator:
- return false;
- case dyld3::Platform::watchOS_simulator:
- return false;
- case dyld3::Platform::driverKit:
- return false;
- }
- }
-
-
-
-
- static bool platformExcludesExecutablePath_macOS(const std::string& path) {
- return false;
- }
-
- static bool platformExcludesExecutablePath_iOS(const std::string& path) {
- //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends
- if (path == "/sbin/launchd"
- || path == "/usr/local/sbin/launchd.debug"
- || path == "/usr/local/sbin/launchd.development"
- || path == "/usr/libexec/installd") {
- return true;
- }
- return false;
- }
-
- static bool platformExcludesExecutablePath_tvOS(const std::string& path) {
- return platformExcludesExecutablePath_iOS(path);
- }
-
- static bool platformExcludesExecutablePath_watchOS(const std::string& path) {
- return platformExcludesExecutablePath_iOS(path);
- }
-
- static bool platformExcludesExecutablePath_bridgeOS(const std::string& path) {
- return platformExcludesExecutablePath_iOS(path);
- }
-
- // Returns true if the current platform requires that this path be excluded from the shared cache
- // Note that this overrides any exclusion from anywhere else.
- bool platformExcludesExecutablePath(const std::string& path) {
- switch (reqPlatform) {
- case dyld3::Platform::unknown:
- return false;
- case dyld3::Platform::macOS:
- return platformExcludesExecutablePath_macOS(path);
- case dyld3::Platform::iOS:
- return platformExcludesExecutablePath_iOS(path);
- case dyld3::Platform::tvOS:
- return platformExcludesExecutablePath_tvOS(path);
- case dyld3::Platform::watchOS:
- return platformExcludesExecutablePath_watchOS(path);
- case dyld3::Platform::bridgeOS:
- return platformExcludesExecutablePath_bridgeOS(path);
- case dyld3::Platform::iOSMac:
- return false;
- case dyld3::Platform::iOS_simulator:
- return false;
- case dyld3::Platform::tvOS_simulator:
- return false;
- case dyld3::Platform::watchOS_simulator:
- return false;
- case dyld3::Platform::driverKit:
- return false;
- }
- }
-
- const dyld3::closure::FileSystem& fileSystem;
- const dyld3::GradedArchs& reqArchs;
- dyld3::Platform reqPlatform;
-};
-
-static void verifySelfContained(std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
- std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
- std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles)
-{
- // build map of dylibs
- __block std::map<std::string, const CacheBuilder::LoadedMachO*> knownDylibs;
- __block std::map<std::string, const CacheBuilder::LoadedMachO*> allDylibs;
- for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
- knownDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
- allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
- if (const char* installName = dylib.mappedFile.mh->installName()) {
- knownDylibs.insert({ installName, &dylib });
- allDylibs.insert({ installName, &dylib });
- }
- }
-
- for (const CacheBuilder::LoadedMachO& dylib : otherDylibs) {
- allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
- if (const char* installName = dylib.mappedFile.mh->installName())
- allDylibs.insert({ installName, &dylib });
- }
-
- for (const CacheBuilder::LoadedMachO& dylib : couldNotLoadFiles) {
- allDylibs.insert({ dylib.inputFile->path, &dylib });
- }
-
- // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
- __block std::map<std::string, std::set<std::string>> badDylibs;
- __block bool doAgain = true;
- while ( doAgain ) {
- doAgain = false;
- // scan dylib list making sure all dependents are in dylib list
- for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
- if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
- continue;
- dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- if (isWeak)
- return;
- if ( knownDylibs.count(loadPath) == 0 ) {
- badDylibs[dylib.mappedFile.runtimePath].insert(std::string("Could not find dependency '") + loadPath + "'");
- knownDylibs.erase(dylib.mappedFile.runtimePath);
- knownDylibs.erase(dylib.mappedFile.mh->installName());
- doAgain = true;
- }
- });
- }
- }
-
- // Now walk the dylibs which depend on missing dylibs and see if any of them are required binaries.
- for (auto badDylibsIterator : badDylibs) {
- const std::string& dylibRuntimePath = badDylibsIterator.first;
- auto requiredDylibIterator = allDylibs.find(dylibRuntimePath);
- if (requiredDylibIterator == allDylibs.end())
- continue;
- if (!requiredDylibIterator->second->inputFile->mustBeIncluded())
- continue;
- // This dylib is required so mark all dependencies as requried too
- __block std::vector<const CacheBuilder::LoadedMachO*> worklist;
- worklist.push_back(requiredDylibIterator->second);
- while (!worklist.empty()) {
- const CacheBuilder::LoadedMachO* dylib = worklist.back();
- worklist.pop_back();
- if (!dylib->mappedFile.mh)
- continue;
- dylib->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- if (isWeak)
- return;
- auto dylibIterator = allDylibs.find(loadPath);
- if (dylibIterator != allDylibs.end()) {
- if (dylibIterator->second->inputFile->state == CacheBuilder::InputFile::Unset) {
- dylibIterator->second->inputFile->state = CacheBuilder::InputFile::MustBeIncludedForDependent;
- worklist.push_back(dylibIterator->second);
- }
- }
- });
- }
- }
-
- // FIXME: Make this an option we can pass in
- const bool evictLeafDylibs = true;
- if (evictLeafDylibs) {
- doAgain = true;
- while ( doAgain ) {
- doAgain = false;
-
- // build count of how many references there are to each dylib
- __block std::set<std::string> referencedDylibs;
- for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
- if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
- continue;
- dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
- referencedDylibs.insert(loadPath);
- });
- }
-
- // find all dylibs not referenced
- for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
- if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
- continue;
- const char* installName = dylib.mappedFile.mh->installName();
- if ( (referencedDylibs.count(installName) == 0) && (dylib.inputFile->state == CacheBuilder::InputFile::MustBeExcludedIfUnused) ) {
- badDylibs[dylib.mappedFile.runtimePath].insert(std::string("It has been explicitly excluded as it is unused"));
- doAgain = true;
- }
- }
- }
- }
-
- // Move bad dylibs from dylibs to cache to other dylibs.
- for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
- auto i = badDylibs.find(dylib.mappedFile.runtimePath);
- if ( i != badDylibs.end()) {
- otherDylibs.push_back(dylib);
- for (const std::string& reason : i->second )
- otherDylibs.back().inputFile->diag.warning("Dylib located at '%s' not placed in shared cache because: %s", dylib.mappedFile.runtimePath.c_str(), reason.c_str());
- }
- }
-
- const auto& badDylibsLambdaRef = badDylibs;
- dylibsToCache.erase(std::remove_if(dylibsToCache.begin(), dylibsToCache.end(), [&](const CacheBuilder::LoadedMachO& dylib) {
- if (badDylibsLambdaRef.find(dylib.mappedFile.runtimePath) != badDylibsLambdaRef.end())
- return true;
- return false;
- }), dylibsToCache.end());
-}
-
-// This is the new build API which takes the raw files (which could be FAT) and tries to build a cache from them.
-// We should remove the other build() method, or make it private so that this can wrap it.
-void CacheBuilder::build(std::vector<CacheBuilder::InputFile>& inputFiles,
- std::vector<DyldSharedCache::FileAlias>& aliases) {
- // First filter down to files which are actually MachO's
- CacheInputBuilder cacheInputBuilder(_fileSystem, *_options.archs, _options.platform);
-
- std::vector<LoadedMachO> dylibsToCache;
- std::vector<LoadedMachO> otherDylibs;
- std::vector<LoadedMachO> executables;
- std::vector<LoadedMachO> couldNotLoadFiles;
- cacheInputBuilder.loadMachOs(inputFiles, dylibsToCache, otherDylibs, executables, couldNotLoadFiles);
-
- verifySelfContained(dylibsToCache, otherDylibs, couldNotLoadFiles);
-
- // Check for required binaries before we try to build the cache
- if (!_diagnostics.hasError()) {
- // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
- std::string errorString;
- for (const LoadedMachO& dylib : otherDylibs) {
- if (dylib.inputFile->mustBeIncluded()) {
- // An error loading a required file must be propagated up to the top level diagnostic handler.
- bool gotWarning = false;
- for (const std::string& warning : dylib.inputFile->diag.warnings()) {
- gotWarning = true;
- std::string message = warning;
- if (message.back() == '\n')
- message.pop_back();
- if (!errorString.empty())
- errorString += "ERROR: ";
- errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + message + "\n";
- }
- if (!gotWarning) {
- if (!errorString.empty())
- errorString += "ERROR: ";
- errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error. Please report to dyld'\n";
- }
- }
- }
- for (const LoadedMachO& dylib : couldNotLoadFiles) {
- if (dylib.inputFile->mustBeIncluded()) {
- if (dylib.inputFile->diag.hasError()) {
- if (!errorString.empty())
- errorString += "ERROR: ";
- errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + dylib.inputFile->diag.errorMessage() + "\n";
- } else {
- if (!errorString.empty())
- errorString += "ERROR: ";
- errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error. Please report to dyld'\n";
-
- }
- }
- }
- if (!errorString.empty()) {
- _diagnostics.error("%s", errorString.c_str());
- }
- }
-
- if (!_diagnostics.hasError())
- build(dylibsToCache, otherDylibs, executables, aliases);
-
- if (!_diagnostics.hasError()) {
- // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
- std::string errorString;
- for (CacheBuilder::InputFile& inputFile : inputFiles) {
- if (inputFile.mustBeIncluded() && inputFile.diag.hasError()) {
- // An error loading a required file must be propagated up to the top level diagnostic handler.
- std::string message = inputFile.diag.errorMessage();
- if (message.back() == '\n')
- message.pop_back();
- errorString += "Required binary was not included in the shared cache '" + std::string(inputFile.path) + "' because: " + message + "\n";
- }
- }
- if (!errorString.empty()) {
- _diagnostics.error("%s", errorString.c_str());
- }
- }
-
- // Add all the warnings from the input files to the top level warnings on the main diagnostics object.
- for (CacheBuilder::InputFile& inputFile : inputFiles) {
- for (const std::string& warning : inputFile.diag.warnings())
- _diagnostics.warning("%s", warning.c_str());
- }
-
- // Clean up the loaded files
- for (LoadedMachO& loadedMachO : dylibsToCache)
- _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
- for (LoadedMachO& loadedMachO : otherDylibs)
- _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
- for (LoadedMachO& loadedMachO : executables)
- _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
-}
-
-void CacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs,
- const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibsInput,
- const std::vector<DyldSharedCache::MappedMachO>& osExecutables,
- std::vector<DyldSharedCache::FileAlias>& aliases) {
-
- std::vector<LoadedMachO> dylibsToCache;
- std::vector<LoadedMachO> otherDylibs;
- std::vector<LoadedMachO> executables;
-
- for (const DyldSharedCache::MappedMachO& mappedMachO : dylibs) {
- dyld3::closure::LoadedFileInfo loadedFileInfo;
- loadedFileInfo.fileContent = mappedMachO.mh;
- loadedFileInfo.fileContentLen = mappedMachO.length;
- loadedFileInfo.sliceOffset = mappedMachO.sliceFileOffset;
- loadedFileInfo.sliceLen = mappedMachO.length;
- loadedFileInfo.inode = mappedMachO.inode;
- loadedFileInfo.mtime = mappedMachO.modTime;
- loadedFileInfo.path = mappedMachO.runtimePath.c_str();
- dylibsToCache.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
- }
-
- for (const DyldSharedCache::MappedMachO& mappedMachO : otherOsDylibsInput) {
- dyld3::closure::LoadedFileInfo loadedFileInfo;
- loadedFileInfo.fileContent = mappedMachO.mh;
- loadedFileInfo.fileContentLen = mappedMachO.length;
- loadedFileInfo.sliceOffset = mappedMachO.sliceFileOffset;
- loadedFileInfo.sliceLen = mappedMachO.length;
- loadedFileInfo.inode = mappedMachO.inode;
- loadedFileInfo.mtime = mappedMachO.modTime;
- loadedFileInfo.path = mappedMachO.runtimePath.c_str();
- otherDylibs.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
- }
-
- for (const DyldSharedCache::MappedMachO& mappedMachO : osExecutables) {
- dyld3::closure::LoadedFileInfo loadedFileInfo;
- loadedFileInfo.fileContent = mappedMachO.mh;
- loadedFileInfo.fileContentLen = mappedMachO.length;
- loadedFileInfo.sliceOffset = mappedMachO.sliceFileOffset;
- loadedFileInfo.sliceLen = mappedMachO.length;
- loadedFileInfo.inode = mappedMachO.inode;
- loadedFileInfo.mtime = mappedMachO.modTime;
- loadedFileInfo.path = mappedMachO.runtimePath.c_str();
- executables.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
- }
-
- build(dylibsToCache, otherDylibs, executables, aliases);
-}
-
-void CacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
- const std::vector<LoadedMachO>& otherOsDylibsInput,
- const std::vector<LoadedMachO>& osExecutables,
- std::vector<DyldSharedCache::FileAlias>& aliases)
-{
- // <rdar://problem/21317611> error out instead of crash if cache has no dylibs
- // FIXME: plist should specify required vs optional dylibs
- if ( dylibs.size() < 30 ) {
- _diagnostics.error("missing required minimum set of dylibs");
- return;
- }
- uint64_t t1 = mach_absolute_time();
-
- // make copy of dylib list and sort
- makeSortedDylibs(dylibs, _options.dylibOrdering);
-
- // allocate space used by largest possible cache plus room for LINKEDITS before optimization
- _allocatedBufferSize = _archLayout->sharedMemorySize * 1.50;
- if ( vm_allocate(mach_task_self(), &_fullAllocatedBuffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) {
- _diagnostics.error("could not allocate buffer");
- return;
- }
-
- // assign addresses for each segment of each dylib in new cache
- parseCoalescableSegments();
- processSelectorStrings(osExecutables);
- assignSegmentAddresses();
- std::vector<const LoadedMachO*> overflowDylibs;
- while ( cacheOverflowAmount() != 0 ) {
- if ( !_options.evictLeafDylibsOnOverflow ) {
- _diagnostics.error("cache overflow by %lluMB", cacheOverflowAmount() / 1024 / 1024);
- return;
- }
- size_t evictionCount = evictLeafDylibs(cacheOverflowAmount(), overflowDylibs);
- // re-layout cache
- for (DylibInfo& dylib : _sortedDylibs)
- dylib.cacheLocation.clear();
- _coalescedText.clear();
- parseCoalescableSegments();
- processSelectorStrings(osExecutables);
- assignSegmentAddresses();
-
- _diagnostics.verbose("cache overflow, evicted %lu leaf dylibs\n", evictionCount);
- }
- markPaddingInaccessible();
-
- // copy all segments into cache
- uint64_t t2 = mach_absolute_time();
- writeCacheHeader();
- copyRawSegments();
-
- // rebase all dylibs for new location in cache
- uint64_t t3 = mach_absolute_time();
- _aslrTracker.setDataRegion(_readWriteRegion.buffer, _readWriteRegion.sizeInUse);
- if ( !_options.cacheSupportsASLR )
- _aslrTracker.disable();
- adjustAllImagesForNewSegmentLocations();
- if ( _diagnostics.hasError() )
- return;
-
- // build ImageArray for dyld3, which has side effect of binding all cached dylibs
- uint64_t t4 = mach_absolute_time();
- buildImageArray(aliases);
- if ( _diagnostics.hasError() )
- return;
-
- // optimize ObjC
- uint64_t t5 = mach_absolute_time();
- DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
- if ( _options.optimizeObjC )
- optimizeObjC();
- if ( _diagnostics.hasError() )
- return;
-
-
- // optimize away stubs
- uint64_t t6 = mach_absolute_time();
- if ( _options.optimizeStubs )
- optimizeAwayStubs();
-
-
- // FIPS seal corecrypto, This must be done after stub elimination (so that __TEXT,__text is not changed after sealing)
- fipsSign();
-
- // merge and compact LINKEDIT segments
- uint64_t t7 = mach_absolute_time();
- optimizeLinkedit();
-
- // copy ImageArray to end of read-only region
- addImageArray();
- if ( _diagnostics.hasError() )
- return;
- uint64_t t8 = mach_absolute_time();
-
- // don't add dyld3 closures to simulator cache
- if ( !dyld3::MachOFile::isSimulatorPlatform(_options.platform) ) {
- // compute and add dlopen closures for all other dylibs
- addOtherImageArray(otherOsDylibsInput, overflowDylibs);
- if ( _diagnostics.hasError() )
- return;
-
- // compute and add launch closures to end of read-only region
- addClosures(osExecutables);
- if ( _diagnostics.hasError() )
- return;
- }
-
- // update final readOnly region size
- dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCache->header.mappingOffset);
- mappings[2].size = _readOnlyRegion.sizeInUse;
- if ( _options.excludeLocalSymbols )
- dyldCache->header.localSymbolsOffset = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse;
-
- // record max slide now that final size is established
- if ( _archLayout->sharedRegionsAreDiscontiguous ) {
- // special case x86_64 which has three non-contiguous chunks each in their own 1GB regions
- uint64_t maxSlide0 = 0x60000000 - _readExecuteRegion.sizeInUse; // TEXT region has 1.5GB region
- uint64_t maxSlide1 = 0x40000000 - _readWriteRegion.sizeInUse;
- uint64_t maxSlide2 = 0x3FE00000 - _readOnlyRegion.sizeInUse;
- dyldCache->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2);
- }
- else {
- // <rdar://problem/49852839> branch predictor on arm64 currently only looks at low 32-bits, so don't slide cache more than 2GB
- if ( (_archLayout->sharedMemorySize == 0x100000000) && (_readExecuteRegion.sizeInUse < 0x80000000) )
- dyldCache->header.maxSlide = 0x80000000 - _readExecuteRegion.sizeInUse;
- else
- dyldCache->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (_readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
- }
-
- uint64_t t9 = mach_absolute_time();
-
- // fill in slide info at start of region[2]
- // do this last because it modifies pointers in DATA segments
- if ( _options.cacheSupportsASLR ) {
-#if SUPPORT_ARCH_arm64e
- if ( strcmp(_archLayout->archName, "arm64e") == 0 )
- writeSlideInfoV3(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
- else
-#endif
- if ( _archLayout->is64 )
- writeSlideInfoV2<Pointer64<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
-#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
- else if ( _archLayout->pointerDeltaMask == 0xC0000000 )
- writeSlideInfoV4<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
-#endif
- else
- writeSlideInfoV2<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
- }
-
- uint64_t t10 = mach_absolute_time();
-
- // last sanity check on size
- if ( cacheOverflowAmount() != 0 ) {
- _diagnostics.error("cache overflow after optimizations 0x%llX -> 0x%llX", _readExecuteRegion.unslidLoadAddress, _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
- return;
- }
-
- // codesignature is part of file, but is not mapped
- codeSign();
- if ( _diagnostics.hasError() )
- return;
-
- uint64_t t11 = mach_absolute_time();
-
- if ( _options.verbose ) {
- fprintf(stderr, "time to layout cache: %ums\n", absolutetime_to_milliseconds(t2-t1));
- fprintf(stderr, "time to copy cached dylibs into buffer: %ums\n", absolutetime_to_milliseconds(t3-t2));
- fprintf(stderr, "time to adjust segments for new split locations: %ums\n", absolutetime_to_milliseconds(t4-t3));
- fprintf(stderr, "time to bind all images: %ums\n", absolutetime_to_milliseconds(t5-t4));
- fprintf(stderr, "time to optimize Objective-C: %ums\n", absolutetime_to_milliseconds(t6-t5));
- fprintf(stderr, "time to do stub elimination: %ums\n", absolutetime_to_milliseconds(t7-t6));
- fprintf(stderr, "time to optimize LINKEDITs: %ums\n", absolutetime_to_milliseconds(t8-t7));
- fprintf(stderr, "time to build %lu closures: %ums\n", osExecutables.size(), absolutetime_to_milliseconds(t9-t8));
- fprintf(stderr, "time to compute slide info: %ums\n", absolutetime_to_milliseconds(t10-t9));
- fprintf(stderr, "time to compute UUID and codesign cache file: %ums\n", absolutetime_to_milliseconds(t11-t10));
- }
-
- return;
-}
-
-
-void CacheBuilder::writeCacheHeader()
-{
- // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
- std::string magic = "dyld_v1";
- magic.append(15 - magic.length() - strlen(_options.archs->name()), ' ');
- magic.append(_options.archs->name());
- assert(magic.length() == 15);
-
- // fill in header
- dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
- memcpy(dyldCacheHeader->magic, magic.c_str(), 16);
- dyldCacheHeader->mappingOffset = sizeof(dyld_cache_header);
- dyldCacheHeader->mappingCount = 3;
- dyldCacheHeader->imagesOffset = (uint32_t)(dyldCacheHeader->mappingOffset + 3*sizeof(dyld_cache_mapping_info));
- dyldCacheHeader->imagesCount = (uint32_t)_sortedDylibs.size() + _aliasCount;
- dyldCacheHeader->dyldBaseAddress = 0;
- dyldCacheHeader->codeSignatureOffset = 0;
- dyldCacheHeader->codeSignatureSize = 0;
- dyldCacheHeader->slideInfoOffset = _slideInfoFileOffset;
- dyldCacheHeader->slideInfoSize = _slideInfoBufferSizeAllocated;
- dyldCacheHeader->localSymbolsOffset = 0;
- dyldCacheHeader->localSymbolsSize = 0;
- dyldCacheHeader->cacheType = _options.optimizeStubs ? kDyldSharedCacheTypeProduction : kDyldSharedCacheTypeDevelopment;
- dyldCacheHeader->accelerateInfoAddr = 0;
- dyldCacheHeader->accelerateInfoSize = 0;
- bzero(dyldCacheHeader->uuid, 16);// overwritten later by recomputeCacheUUID()
- dyldCacheHeader->branchPoolsOffset = 0;
- dyldCacheHeader->branchPoolsCount = 0;
- dyldCacheHeader->imagesTextOffset = dyldCacheHeader->imagesOffset + sizeof(dyld_cache_image_info)*dyldCacheHeader->imagesCount;
- dyldCacheHeader->imagesTextCount = _sortedDylibs.size();
- dyldCacheHeader->patchInfoAddr = 0;
- dyldCacheHeader->patchInfoSize = 0;
- dyldCacheHeader->otherImageGroupAddrUnused = 0;
- dyldCacheHeader->otherImageGroupSizeUnused = 0;
- dyldCacheHeader->progClosuresAddr = 0;
- dyldCacheHeader->progClosuresSize = 0;
- dyldCacheHeader->progClosuresTrieAddr = 0;
- dyldCacheHeader->progClosuresTrieSize = 0;
- dyldCacheHeader->platform = (uint8_t)_options.platform;
- dyldCacheHeader->formatVersion = dyld3::closure::kFormatVersion;
- dyldCacheHeader->dylibsExpectedOnDisk = !_options.dylibsRemovedDuringMastering;
- dyldCacheHeader->simulator = _options.forSimulator;
- dyldCacheHeader->locallyBuiltCache = _options.isLocallyBuiltCache;
- dyldCacheHeader->builtFromChainedFixups= (strcmp(_options.archs->name(), "arm64e") == 0); // FIXME
- dyldCacheHeader->formatVersion = dyld3::closure::kFormatVersion;
- dyldCacheHeader->sharedRegionStart = _archLayout->sharedMemoryStart;
- dyldCacheHeader->sharedRegionSize = _archLayout->sharedMemorySize;
-
- // fill in mappings
- dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCacheHeader->mappingOffset);
- mappings[0].address = _readExecuteRegion.unslidLoadAddress;
- mappings[0].fileOffset = 0;
- mappings[0].size = _readExecuteRegion.sizeInUse;
- mappings[0].maxProt = VM_PROT_READ | VM_PROT_EXECUTE;
- mappings[0].initProt = VM_PROT_READ | VM_PROT_EXECUTE;
- mappings[1].address = _readWriteRegion.unslidLoadAddress;
- mappings[1].fileOffset = _readExecuteRegion.sizeInUse;
- mappings[1].size = _readWriteRegion.sizeInUse;
- mappings[1].maxProt = VM_PROT_READ | VM_PROT_WRITE;
- mappings[1].initProt = VM_PROT_READ | VM_PROT_WRITE;
- mappings[2].address = _readOnlyRegion.unslidLoadAddress;
- mappings[2].fileOffset = _readExecuteRegion.sizeInUse + _readWriteRegion.sizeInUse;
- mappings[2].size = _readOnlyRegion.sizeInUse;
- mappings[2].maxProt = VM_PROT_READ;
- mappings[2].initProt = VM_PROT_READ;
-
- // fill in image table
- dyld_cache_image_info* images = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
- for (const DylibInfo& dylib : _sortedDylibs) {
- const char* installName = dylib.input->mappedFile.mh->installName();
- images->address = dylib.cacheLocation[0].dstCacheUnslidAddress;
- if ( _options.dylibsRemovedDuringMastering ) {
- images->modTime = 0;
- images->inode = pathHash(installName);
- }
- else {
- images->modTime = dylib.input->mappedFile.modTime;
- images->inode = dylib.input->mappedFile.inode;
- }
- uint32_t installNameOffsetInTEXT = (uint32_t)(installName - (char*)dylib.input->mappedFile.mh);
- images->pathFileOffset = (uint32_t)dylib.cacheLocation[0].dstCacheFileOffset + installNameOffsetInTEXT;
- ++images;
- }
- // append aliases image records and strings
-/*
- for (auto &dylib : _dylibs) {
- if (!dylib->installNameAliases.empty()) {
- for (const std::string& alias : dylib->installNameAliases) {
- images->set_address(_segmentMap[dylib][0].address);
- if (_manifest.platform() == "osx") {
- images->modTime = dylib->lastModTime;
- images->inode = dylib->inode;
- }
- else {
- images->modTime = 0;
- images->inode = pathHash(alias.c_str());
- }
- images->pathFileOffset = offset;
- //fprintf(stderr, "adding alias %s for %s\n", alias.c_str(), dylib->installName.c_str());
- ::strcpy((char*)&_buffer[offset], alias.c_str());
- offset += alias.size() + 1;
- ++images;
- }
- }
- }
-*/
- // calculate start of text image array and trailing string pool
- dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesTextOffset);
- uint32_t stringOffset = (uint32_t)(dyldCacheHeader->imagesTextOffset + sizeof(dyld_cache_image_text_info) * _sortedDylibs.size());
-
- // write text image array and image names pool at same time
- for (const DylibInfo& dylib : _sortedDylibs) {
- dylib.input->mappedFile.mh->getUuid(textImages->uuid);
- textImages->loadAddress = dylib.cacheLocation[0].dstCacheUnslidAddress;
- textImages->textSegmentSize = (uint32_t)dylib.cacheLocation[0].dstCacheSegmentSize;
- textImages->pathOffset = stringOffset;
- const char* installName = dylib.input->mappedFile.mh->installName();
- ::strcpy((char*)_readExecuteRegion.buffer + stringOffset, installName);
- stringOffset += (uint32_t)strlen(installName)+1;
- ++textImages;
- }
-
- // make sure header did not overflow into first mapped image
- const dyld_cache_image_info* firstImage = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
- assert(stringOffset <= (firstImage->address - mappings[0].address));
-}
-
-void CacheBuilder::copyRawSegments()
-{
- const bool log = false;
- dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
- const DylibInfo& dylib = _sortedDylibs[index];
- for (const SegmentMappingInfo& info : dylib.cacheLocation) {
- if (log) fprintf(stderr, "copy %s segment %s (0x%08X bytes) from %p to %p (logical addr 0x%llX) for %s\n",
- _options.archs->name(), info.segName, info.copySegmentSize, info.srcSegment, info.dstSegment, info.dstCacheUnslidAddress, dylib.input->mappedFile.runtimePath.c_str());
- ::memcpy(info.dstSegment, info.srcSegment, info.copySegmentSize);
- }
- });
-
- // Copy the coalesced sections
- const uint64_t numCoalescedSections = sizeof(CacheCoalescedText::SupportedSections) / sizeof(*CacheCoalescedText::SupportedSections);
- dispatch_apply(numCoalescedSections, DISPATCH_APPLY_AUTO, ^(size_t index) {
- const CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData(CacheCoalescedText::SupportedSections[index]);
- if (log) fprintf(stderr, "copy %s __TEXT_COAL section %s (0x%08X bytes) to %p (logical addr 0x%llX)\n",
- _options.archs->name(), CacheCoalescedText::SupportedSections[index],
- cacheStringSection.bufferSize, cacheStringSection.bufferAddr, cacheStringSection.bufferVMAddr);
- for (const auto& stringAndOffset : cacheStringSection.stringsToOffsets)
- ::memcpy(cacheStringSection.bufferAddr + stringAndOffset.second, stringAndOffset.first.data(), stringAndOffset.first.size() + 1);
- });
-}
-
-void CacheBuilder::adjustAllImagesForNewSegmentLocations()
-{
- __block std::vector<Diagnostics> diags;
- diags.resize(_sortedDylibs.size());
-
- if (_options.platform == dyld3::Platform::macOS) {
- dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
- const DylibInfo& dylib = _sortedDylibs[index];
- adjustDylibSegments(dylib, diags[index]);
- });
- } else {
- // Note this has to be done in serial because the LOH Tracker isn't thread safe
- for (size_t index = 0; index != _sortedDylibs.size(); ++index) {
- const DylibInfo& dylib = _sortedDylibs[index];
- adjustDylibSegments(dylib, diags[index]);
- }
- }
-
- for (const Diagnostics& diag : diags) {
- if ( diag.hasError() ) {
- _diagnostics.error("%s", diag.errorMessage().c_str());
- break;
- }
- }
-}
-
-void CacheBuilder::processSelectorStrings(const std::vector<LoadedMachO>& executables) {
- const bool log = false;
-
- _selectorStringsFromExecutables = 0;
-
- // Don't do this optimisation on watchOS where the shared cache is too small
- if (_options.platform == dyld3::Platform::watchOS)
- return;
-
- // Get the method name coalesced section as that is where we need to put these strings
- CacheBuilder::CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData("__objc_methname");
- for (const LoadedMachO& executable : executables) {
- const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)executable.loadedFileInfo.fileContent;
-
- uint64_t sizeBeforeProcessing = cacheStringSection.bufferSize;
-
- ma->forEachObjCMethodName(^(const char *methodName) {
- std::string_view str = methodName;
- auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize });
- if (itAndInserted.second) {
- // If we inserted the string then we need to include it in the total
- cacheStringSection.bufferSize += str.size() + 1;
- // if (log) printf("Selector: %s -> %s\n", ma->installName(), methodName);
- ++_selectorStringsFromExecutables;
- }
- });
-
- uint64_t sizeAfterProcessing = cacheStringSection.bufferSize;
- if ( log && (sizeBeforeProcessing != sizeAfterProcessing) ) {
- printf("Pulled in % 6lld bytes of selectors from %s\n",
- sizeAfterProcessing - sizeBeforeProcessing, executable.loadedFileInfo.path);
- }
- }
-
- _diagnostics.verbose("Pulled in %lld selector strings from executables\n", _selectorStringsFromExecutables);
-}
-
-void CacheBuilder::parseCoalescableSegments() {
- const bool log = false;
-
- for (DylibInfo& dylib : _sortedDylibs)
- _coalescedText.parseCoalescableText(dylib.input->mappedFile.mh, dylib.textCoalescer);
-
- if (log) {
- for (const char* section : CacheCoalescedText::SupportedSections) {
- CacheCoalescedText::StringSection& sectionData = _coalescedText.getSectionData(section);
- printf("Coalesced %s from % 10lld -> % 10d, saving % 10lld bytes\n", section,
- sectionData.bufferSize + sectionData.savedSpace, sectionData.bufferSize, sectionData.savedSpace);
- }
- }
-}
-
-void CacheBuilder::assignSegmentAddresses()
-{
- // calculate size of header info and where first dylib's mach_header should start
- size_t startOffset = sizeof(dyld_cache_header) + 3*sizeof(dyld_cache_mapping_info);
- startOffset += sizeof(dyld_cache_image_info) * _sortedDylibs.size();
- startOffset += sizeof(dyld_cache_image_text_info) * _sortedDylibs.size();
- for (const DylibInfo& dylib : _sortedDylibs) {
- startOffset += (strlen(dylib.input->mappedFile.mh->installName()) + 1);
- }
- //fprintf(stderr, "%s total header size = 0x%08lX\n", _options.archName.c_str(), startOffset);
- startOffset = align(startOffset, 12);
-
- // assign TEXT segment addresses
- _readExecuteRegion.buffer = (uint8_t*)_fullAllocatedBuffer;
- _readExecuteRegion.bufferSize = 0;
- _readExecuteRegion.sizeInUse = 0;
- _readExecuteRegion.unslidLoadAddress = _archLayout->sharedMemoryStart;
- _readExecuteRegion.cacheFileOffset = 0;
- __block uint64_t addr = _readExecuteRegion.unslidLoadAddress + startOffset; // header
- for (DylibInfo& dylib : _sortedDylibs) {
- __block uint64_t textSegVmAddr = 0;
- dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
- if ( strcmp(segInfo.segName, "__TEXT") == 0 )
- textSegVmAddr = segInfo.vmAddr;
- if ( segInfo.protections != (VM_PROT_READ | VM_PROT_EXECUTE) )
- return;
- // We may have coalesced the sections at the end of this segment. In that case, shrink the segment to remove them.
- __block size_t sizeOfSections = 0;
- __block bool foundCoalescedSection = false;
- dylib.input->mappedFile.mh->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §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 <typename P>
-bool CacheBuilder::makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info2* info)
-{
- typedef typename P::uint_t pint_t;
-
- const pint_t deltaMask = (pint_t)(info->delta_mask);
- const pint_t valueMask = ~deltaMask;
- const pint_t valueAdd = (pint_t)(info->value_add);
- const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
- const uint32_t maxDelta = (uint32_t)(deltaMask >> deltaShift);
-
- pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0];
- pint_t lastValue = (pint_t)P::getP(*lastLoc);
- if ( (lastValue - valueAdd) & deltaMask ) {
- std::string dylibName;
- std::string segName;
- findDylibAndSegment((void*)pageContent, dylibName, segName);
- _diagnostics.error("rebase pointer (0x%0lX) does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
- (long)lastValue, lastLocationOffset, segName.c_str(), dylibName.c_str());
- return false;
- }
- if ( offset <= (lastLocationOffset+maxDelta) ) {
- // previous location in range, make link from it
- // encode this location into last value
- pint_t delta = offset - lastLocationOffset;
- pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift);
- //warning(" add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
- // offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
- P::setP(*lastLoc, newLastValue);
- return true;
- }
- //fprintf(stderr, " too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
-
- // distance between rebase locations is too far
- // see if we can make a chain from non-rebase locations
- uint16_t nonRebaseLocationOffsets[1024];
- unsigned nrIndex = 0;
- for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) {
- nonRebaseLocationOffsets[nrIndex] = 0;
- for (int j=maxDelta; j > 0; j -= 4) {
- pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]);
- if ( value == 0 ) {
- // Steal values of 0 to be used in the rebase chain
- nonRebaseLocationOffsets[nrIndex] = i+j;
- break;
- }
- }
- if ( nonRebaseLocationOffsets[nrIndex] == 0 ) {
- lastValue = (pint_t)P::getP(*lastLoc);
- pint_t newValue = ((lastValue - valueAdd) & valueMask);
- //warning(" no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX", lastLocationOffset, (long)value, (long)newValue);
- P::setP(*lastLoc, newValue);
- return false;
- }
- i = nonRebaseLocationOffsets[nrIndex];
- ++nrIndex;
- }
-
- // we can make chain. go back and add each non-rebase location to chain
- uint16_t prevOffset = lastLocationOffset;
- pint_t* prevLoc = (pint_t*)&pageContent[prevOffset];
- for (unsigned n=0; n < nrIndex; ++n) {
- uint16_t nOffset = nonRebaseLocationOffsets[n];
- assert(nOffset != 0);
- pint_t* nLoc = (pint_t*)&pageContent[nOffset];
- uint32_t delta2 = nOffset - prevOffset;
- pint_t value = (pint_t)P::getP(*prevLoc);
- pint_t newValue;
- if ( value == 0 )
- newValue = (delta2 << deltaShift);
- else
- newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift);
- //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
- P::setP(*prevLoc, newValue);
- prevOffset = nOffset;
- prevLoc = nLoc;
- }
- uint32_t delta3 = offset - prevOffset;
- pint_t value = (pint_t)P::getP(*prevLoc);
- pint_t newValue;
- if ( value == 0 )
- newValue = (delta3 << deltaShift);
- else
- newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift);
- //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
- P::setP(*prevLoc, newValue);
-
- return true;
-}
-
-
-template <typename P>
-void CacheBuilder::addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info,
- std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
-{
- typedef typename P::uint_t pint_t;
-
- const pint_t deltaMask = (pint_t)(info->delta_mask);
- const pint_t valueMask = ~deltaMask;
- const uint32_t pageSize = info->page_size;
- const pint_t valueAdd = (pint_t)(info->value_add);
-
- uint16_t startValue = DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE;
- uint16_t lastLocationOffset = 0xFFFF;
- for(uint32_t i=0; i < pageSize/4; ++i) {
- unsigned offset = i*4;
- if ( bitmap[i] ) {
- if ( startValue == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
- // found first rebase location in page
- startValue = i;
- }
- else if ( !makeRebaseChainV2<P>(pageContent, lastLocationOffset, offset, info) ) {
- // can't record all rebasings in one chain
- if ( (startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 ) {
- // switch page_start to "extras" which is a list of chain starts
- unsigned indexInExtras = (unsigned)pageExtras.size();
- if ( indexInExtras > 0x3FFF ) {
- _diagnostics.error("rebase overflow in v2 page extras");
- return;
- }
- pageExtras.push_back(startValue);
- startValue = indexInExtras | DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA;
- }
- pageExtras.push_back(i);
- }
- lastLocationOffset = offset;
- }
- }
- if ( lastLocationOffset != 0xFFFF ) {
- // mark end of chain
- pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset];
- pint_t lastValue = (pint_t)P::getP(*lastLoc);
- pint_t newValue = ((lastValue - valueAdd) & valueMask);
- P::setP(*lastLoc, newValue);
- }
- if ( startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
- // add end bit to extras
- pageExtras.back() |= DYLD_CACHE_SLIDE_PAGE_ATTR_END;
- }
- pageStarts.push_back(startValue);
-}
-
-template <typename P>
-void CacheBuilder::writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount)
-{
- typedef typename P::uint_t pint_t;
- typedef typename P::E E;
- const uint32_t pageSize = 4096;
-
- // fill in fixed info
- assert(_slideInfoFileOffset != 0);
- dyld_cache_slide_info2* info = (dyld_cache_slide_info2*)_readOnlyRegion.buffer;
- info->version = 2;
- info->page_size = pageSize;
- info->delta_mask = _archLayout->pointerDeltaMask;
- info->value_add = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0;
-
- // set page starts and extras for each page
- std::vector<uint16_t> pageStarts;
- std::vector<uint16_t> pageExtras;
- pageStarts.reserve(dataPageCount);
- uint8_t* pageContent = _readWriteRegion.buffer;
- const bool* bitmapForPage = bitmap;
- for (unsigned i=0; i < dataPageCount; ++i) {
- //warning("page[%d]", i);
- addPageStartsV2<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
- if ( _diagnostics.hasError() ) {
- return;
- }
- pageContent += pageSize;
- bitmapForPage += (sizeof(bool)*(pageSize/4));
- }
-
- // fill in computed info
- info->page_starts_offset = sizeof(dyld_cache_slide_info2);
- info->page_starts_count = (unsigned)pageStarts.size();
- info->page_extras_offset = (unsigned)(sizeof(dyld_cache_slide_info2)+pageStarts.size()*sizeof(uint16_t));
- info->page_extras_count = (unsigned)pageExtras.size();
- uint16_t* pageStartsBuffer = (uint16_t*)((char*)info + info->page_starts_offset);
- uint16_t* pageExtrasBuffer = (uint16_t*)((char*)info + info->page_extras_offset);
- for (unsigned i=0; i < pageStarts.size(); ++i)
- pageStartsBuffer[i] = pageStarts[i];
- for (unsigned i=0; i < pageExtras.size(); ++i)
- pageExtrasBuffer[i] = pageExtras[i];
- // update header with final size
- uint64_t slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2);
- if ( slideInfoSize > _slideInfoBufferSizeAllocated ) {
- _diagnostics.error("kernel slide info overflow buffer");
- }
- ((dyld_cache_header*)_readExecuteRegion.buffer)->slideInfoSize = slideInfoSize;
- //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
-}
-
-#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
-// fits in to int16_t
-static bool smallValue(uint64_t value)
-{
- uint32_t high = (value & 0xFFFF8000);
- return (high == 0) || (high == 0xFFFF8000);
-}
-
-template <typename P>
-bool CacheBuilder::makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info4* info)
-{
- typedef typename P::uint_t pint_t;
-
- const pint_t deltaMask = (pint_t)(info->delta_mask);
- const pint_t valueMask = ~deltaMask;
- const pint_t valueAdd = (pint_t)(info->value_add);
- const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
- const uint32_t maxDelta = (uint32_t)(deltaMask >> deltaShift);
-
- pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0];
- pint_t lastValue = (pint_t)P::getP(*lastLoc);
- if ( (lastValue - valueAdd) & deltaMask ) {
- std::string dylibName;
- std::string segName;
- findDylibAndSegment((void*)pageContent, dylibName, segName);
- _diagnostics.error("rebase pointer does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
- lastLocationOffset, segName.c_str(), dylibName.c_str());
- return false;
- }
- if ( offset <= (lastLocationOffset+maxDelta) ) {
- // previous location in range, make link from it
- // encode this location into last value
- pint_t delta = offset - lastLocationOffset;
- pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift);
- //warning(" add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
- // offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
- P::setP(*lastLoc, newLastValue);
- return true;
- }
- //fprintf(stderr, " too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
-
- // distance between rebase locations is too far
- // see if we can make a chain from non-rebase locations
- uint16_t nonRebaseLocationOffsets[1024];
- unsigned nrIndex = 0;
- for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) {
- nonRebaseLocationOffsets[nrIndex] = 0;
- for (int j=maxDelta; j > 0; j -= 4) {
- pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]);
- if ( smallValue(value) ) {
- // Steal values of 0 to be used in the rebase chain
- nonRebaseLocationOffsets[nrIndex] = i+j;
- break;
- }
- }
- if ( nonRebaseLocationOffsets[nrIndex] == 0 ) {
- lastValue = (pint_t)P::getP(*lastLoc);
- pint_t newValue = ((lastValue - valueAdd) & valueMask);
- //fprintf(stderr, " no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX\n",
- // lastLocationOffset, (long)lastValue, (long)newValue);
- P::setP(*lastLoc, newValue);
- return false;
- }
- i = nonRebaseLocationOffsets[nrIndex];
- ++nrIndex;
- }
-
- // we can make chain. go back and add each non-rebase location to chain
- uint16_t prevOffset = lastLocationOffset;
- pint_t* prevLoc = (pint_t*)&pageContent[prevOffset];
- for (unsigned n=0; n < nrIndex; ++n) {
- uint16_t nOffset = nonRebaseLocationOffsets[n];
- assert(nOffset != 0);
- pint_t* nLoc = (pint_t*)&pageContent[nOffset];
- uint32_t delta2 = nOffset - prevOffset;
- pint_t value = (pint_t)P::getP(*prevLoc);
- pint_t newValue;
- if ( smallValue(value) )
- newValue = (value & valueMask) | (delta2 << deltaShift);
- else
- newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift);
- //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
- P::setP(*prevLoc, newValue);
- prevOffset = nOffset;
- prevLoc = nLoc;
- }
- uint32_t delta3 = offset - prevOffset;
- pint_t value = (pint_t)P::getP(*prevLoc);
- pint_t newValue;
- if ( smallValue(value) )
- newValue = (value & valueMask) | (delta3 << deltaShift);
- else
- newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift);
- //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
- P::setP(*prevLoc, newValue);
-
- return true;
-}
-
-
-template <typename P>
-void CacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info,
- std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
-{
- typedef typename P::uint_t pint_t;
-
- const pint_t deltaMask = (pint_t)(info->delta_mask);
- const pint_t valueMask = ~deltaMask;
- const uint32_t pageSize = info->page_size;
- const pint_t valueAdd = (pint_t)(info->value_add);
-
- uint16_t startValue = DYLD_CACHE_SLIDE4_PAGE_NO_REBASE;
- uint16_t lastLocationOffset = 0xFFFF;
- for(uint32_t i=0; i < pageSize/4; ++i) {
- unsigned offset = i*4;
- if ( bitmap[i] ) {
- if ( startValue == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
- // found first rebase location in page
- startValue = i;
- }
- else if ( !makeRebaseChainV4<P>(pageContent, lastLocationOffset, offset, info) ) {
- // can't record all rebasings in one chain
- if ( (startValue & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) == 0 ) {
- // switch page_start to "extras" which is a list of chain starts
- unsigned indexInExtras = (unsigned)pageExtras.size();
- if ( indexInExtras >= DYLD_CACHE_SLIDE4_PAGE_INDEX ) {
- _diagnostics.error("rebase overflow in v4 page extras");
- return;
- }
- pageExtras.push_back(startValue);
- startValue = indexInExtras | DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA;
- }
- pageExtras.push_back(i);
- }
- lastLocationOffset = offset;
- }
- }
- if ( lastLocationOffset != 0xFFFF ) {
- // mark end of chain
- pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset];
- pint_t lastValue = (pint_t)P::getP(*lastLoc);
- pint_t newValue = ((lastValue - valueAdd) & valueMask);
- P::setP(*lastLoc, newValue);
- }
- if ( startValue & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
- // add end bit to extras
- pageExtras.back() |= DYLD_CACHE_SLIDE4_PAGE_EXTRA_END;
- }
- pageStarts.push_back(startValue);
-}
-
-
-
-template <typename P>
-void CacheBuilder::writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount)
-{
- typedef typename P::uint_t pint_t;
- typedef typename P::E E;
- const uint32_t pageSize = 4096;
-
- // fill in fixed info
- assert(_slideInfoFileOffset != 0);
- dyld_cache_slide_info4* info = (dyld_cache_slide_info4*)_readOnlyRegion.buffer;
- info->version = 4;
- info->page_size = pageSize;
- info->delta_mask = _archLayout->pointerDeltaMask;
- info->value_add = info->value_add = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0;
-
- // set page starts and extras for each page
- std::vector<uint16_t> pageStarts;
- std::vector<uint16_t> pageExtras;
- pageStarts.reserve(dataPageCount);
- uint8_t* pageContent = _readWriteRegion.buffer;
- const bool* bitmapForPage = bitmap;
- for (unsigned i=0; i < dataPageCount; ++i) {
- addPageStartsV4<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
- if ( _diagnostics.hasError() ) {
- return;
- }
- pageContent += pageSize;
- bitmapForPage += (sizeof(bool)*(pageSize/4));
- }
- // fill in computed info
- info->page_starts_offset = sizeof(dyld_cache_slide_info4);
- info->page_starts_count = (unsigned)pageStarts.size();
- info->page_extras_offset = (unsigned)(sizeof(dyld_cache_slide_info4)+pageStarts.size()*sizeof(uint16_t));
- info->page_extras_count = (unsigned)pageExtras.size();
- uint16_t* pageStartsBuffer = (uint16_t*)((char*)info + info->page_starts_offset);
- uint16_t* pageExtrasBuffer = (uint16_t*)((char*)info + info->page_extras_offset);
- for (unsigned i=0; i < pageStarts.size(); ++i)
- pageStartsBuffer[i] = pageStarts[i];
- for (unsigned i=0; i < pageExtras.size(); ++i)
- pageExtrasBuffer[i] = pageExtras[i];
- // update header with final size
- uint64_t slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2);
- if ( slideInfoSize > _slideInfoBufferSizeAllocated ) {
- _diagnostics.error("kernel slide info v4 overflow buffer");
- }
- ((dyld_cache_header*)_readExecuteRegion.buffer)->slideInfoSize = slideInfoSize;
- //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
-}
-#endif
-
-/*
-void CacheBuilder::writeSlideInfoV1()
-{
- // build one 128-byte bitmap per page (4096) of DATA
- uint8_t* const dataStart = (uint8_t*)_buffer.get() + regions[1].fileOffset;
- uint8_t* const dataEnd = dataStart + regions[1].size;
- const long bitmapSize = (dataEnd - dataStart)/(4*8);
- uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1);
- for (void* p : _pointersForASLR) {
- if ( (p < dataStart) || ( p > dataEnd) )
- terminate("DATA pointer for sliding, out of range\n");
- long offset = (long)((uint8_t*)p - dataStart);
- if ( (offset % 4) != 0 )
- terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset);
- long byteIndex = offset / (4*8);
- long bitInByte = (offset % 32) >> 2;
- bitmap[byteIndex] |= (1 << bitInByte);
- }
-
- // allocate worst case size block of all slide info
- const unsigned entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes.
- const unsigned toc_count = (unsigned)bitmapSize/entry_size;
- dyld_cache_slide_info* slideInfo = (dyld_cache_slide_info*)((uint8_t*)_buffer + _slideInfoFileOffset);
- slideInfo->version = 1;
- slideInfo->toc_offset = sizeof(dyld_cache_slide_info);
- slideInfo->toc_count = toc_count;
- slideInfo->entries_offset = (slideInfo->toc_offset+2*toc_count+127)&(-128);
- slideInfo->entries_count = 0;
- slideInfo->entries_size = entry_size;
- // append each unique entry
- const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap;
- dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset());
- int entry_count = 0;
- for (int i=0; i < toc_count; ++i) {
- const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i];
- // see if it is same as one already added
- bool found = false;
- for (int j=0; j < entry_count; ++j) {
- if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) {
- slideInfo->set_toc(i, j);
- found = true;
- break;
- }
- }
- if ( !found ) {
- // append to end
- memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size);
- slideInfo->set_toc(i, entry_count++);
- }
- }
- slideInfo->entries_count = entry_count;
- ::free((void*)bitmap);
-
- _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2);
-}
-
-*/
-
-
-
-uint16_t CacheBuilder::pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[])
-{
- const int maxPerPage = pageSize / 4;
- uint16_t result = DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE;
- dyld3::MachOLoaded::ChainedFixupPointerOnDisk* lastLoc = nullptr;
- for (int i=0; i < maxPerPage; ++i) {
- if ( bitmap[i] ) {
- if ( result == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) {
- // found first rebase location in page
- result = i * 4;
- }
- dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)(pageContent + i*4);;
- if ( lastLoc != nullptr ) {
- // update chain (original chain may be wrong because of segment packing)
- lastLoc->arm64e.rebase.next = loc - lastLoc;
- }
- lastLoc = loc;
- }
- }
- if ( lastLoc != nullptr ) {
- // mark last one as end of chain
- lastLoc->arm64e.rebase.next = 0;
- }
- return result;
-}
-
-
-void CacheBuilder::writeSlideInfoV3(const bool bitmap[], unsigned dataPageCount)
-{
- const uint32_t pageSize = 4096;
-
- // fill in fixed info
- assert(_slideInfoFileOffset != 0);
- dyld_cache_slide_info3* info = (dyld_cache_slide_info3*)_readOnlyRegion.buffer;
- info->version = 3;
- info->page_size = pageSize;
- info->page_starts_count = dataPageCount;
- info->auth_value_add = _archLayout->sharedMemoryStart;
-
- // fill in per-page starts
- uint8_t* pageContent = _readWriteRegion.buffer;
- const bool* bitmapForPage = bitmap;
- for (unsigned i=0; i < dataPageCount; ++i) {
- info->page_starts[i] = pageStartV3(pageContent, pageSize, bitmapForPage);
- pageContent += pageSize;
- bitmapForPage += (sizeof(bool)*(pageSize/4));
- }
-
- // update header with final size
- dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
- dyldCacheHeader->slideInfoSize = align(__offsetof(dyld_cache_slide_info3, page_starts[dataPageCount]), _archLayout->sharedRegionAlignP2);
- if ( dyldCacheHeader->slideInfoSize > _slideInfoBufferSizeAllocated ) {
- _diagnostics.error("kernel slide info overflow buffer");
- }
-}
-
-
-void CacheBuilder::fipsSign()
-{
- // find libcorecrypto.dylib in cache being built
- DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
- __block const dyld3::MachOLoaded* ml = nullptr;
- dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
- if ( strcmp(installName, "/usr/lib/system/libcorecrypto.dylib") == 0 )
- ml = (dyld3::MachOLoaded*)mh;
- });
- if ( ml == nullptr ) {
- _diagnostics.warning("Could not find libcorecrypto.dylib, skipping FIPS sealing");
- return;
- }
-
- // find location in libcorecrypto.dylib to store hash of __text section
- uint64_t hashStoreSize;
- const void* hashStoreLocation = ml->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize);
- if ( hashStoreLocation == nullptr ) {
- _diagnostics.warning("Could not find __TEXT/__fips_hmacs section in libcorecrypto.dylib, skipping FIPS sealing");
- return;
- }
- if ( hashStoreSize != 32 ) {
- _diagnostics.warning("__TEXT/__fips_hmacs section in libcorecrypto.dylib is not 32 bytes in size, skipping FIPS sealing");
- return;
- }
-
- // compute hmac hash of __text section
- uint64_t textSize;
- const void* textLocation = ml->findSectionContent("__TEXT", "__text", textSize);
- if ( textLocation == nullptr ) {
- _diagnostics.warning("Could not find __TEXT/__text section in libcorecrypto.dylib, skipping FIPS sealing");
- return;
- }
- unsigned char hmac_key = 0;
- CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, textLocation, textSize, (void*)hashStoreLocation); // store hash directly into hashStoreLocation
-}
-
-void CacheBuilder::codeSign()
-{
- uint8_t dscHashType;
- uint8_t dscHashSize;
- uint32_t dscDigestFormat;
- bool agile = false;
-
- // select which codesigning hash
- switch (_options.codeSigningDigestMode) {
- case DyldSharedCache::Agile:
- agile = true;
- // Fall through to SHA1, because the main code directory remains SHA1 for compatibility.
- [[clang::fallthrough]];
- case DyldSharedCache::SHA1only:
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- dscHashType = CS_HASHTYPE_SHA1;
- dscHashSize = CS_HASH_SIZE_SHA1;
- dscDigestFormat = kCCDigestSHA1;
-#pragma clang diagnostic pop
- break;
- case DyldSharedCache::SHA256only:
- dscHashType = CS_HASHTYPE_SHA256;
- dscHashSize = CS_HASH_SIZE_SHA256;
- dscDigestFormat = kCCDigestSHA256;
- break;
- default:
- _diagnostics.error("codeSigningDigestMode has unknown, unexpected value %d, bailing out.",
- _options.codeSigningDigestMode);
- return;
- }
-
- std::string cacheIdentifier = "com.apple.dyld.cache.";
- cacheIdentifier += _options.archs->name();
- if ( _options.dylibsRemovedDuringMastering ) {
- if ( _options.optimizeStubs )
- cacheIdentifier += ".release";
- else
- cacheIdentifier += ".development";
- }
- // get pointers into shared cache buffer
- size_t inBbufferSize = _readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse;
-
- const uint16_t pageSize = _archLayout->csPageSize;
-
- // layout code signature contents
- uint32_t blobCount = agile ? 4 : 3;
- size_t idSize = cacheIdentifier.size()+1; // +1 for terminating 0
- uint32_t slotCount = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize);
- uint32_t xSlotCount = CSSLOT_REQUIREMENTS;
- size_t idOffset = offsetof(CS_CodeDirectory, end_withExecSeg);
- size_t hashOffset = idOffset+idSize + dscHashSize*xSlotCount;
- size_t hash256Offset = idOffset+idSize + CS_HASH_SIZE_SHA256*xSlotCount;
- size_t cdSize = hashOffset + (slotCount * dscHashSize);
- size_t cd256Size = agile ? hash256Offset + (slotCount * CS_HASH_SIZE_SHA256) : 0;
- size_t reqsSize = 12;
- size_t cmsSize = sizeof(CS_Blob);
- size_t cdOffset = sizeof(CS_SuperBlob) + blobCount*sizeof(CS_BlobIndex);
- size_t cd256Offset = cdOffset + cdSize;
- size_t reqsOffset = cd256Offset + cd256Size; // equals cdOffset + cdSize if not agile
- size_t cmsOffset = reqsOffset + reqsSize;
- size_t sbSize = cmsOffset + cmsSize;
- size_t sigSize = align(sbSize, 14); // keep whole cache 16KB aligned
-
- // allocate space for blob
- vm_address_t codeSigAlloc;
- if ( vm_allocate(mach_task_self(), &codeSigAlloc, sigSize, VM_FLAGS_ANYWHERE) != 0 ) {
- _diagnostics.error("could not allocate code signature buffer");
- return;
- }
- _codeSignatureRegion.buffer = (uint8_t*)codeSigAlloc;
- _codeSignatureRegion.bufferSize = sigSize;
- _codeSignatureRegion.sizeInUse = sigSize;
-
- // create overall code signature which is a superblob
- CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(_codeSignatureRegion.buffer);
- sb->magic = htonl(CSMAGIC_EMBEDDED_SIGNATURE);
- sb->length = htonl(sbSize);
- sb->count = htonl(blobCount);
- sb->index[0].type = htonl(CSSLOT_CODEDIRECTORY);
- sb->index[0].offset = htonl(cdOffset);
- sb->index[1].type = htonl(CSSLOT_REQUIREMENTS);
- sb->index[1].offset = htonl(reqsOffset);
- sb->index[2].type = htonl(CSSLOT_CMS_SIGNATURE);
- sb->index[2].offset = htonl(cmsOffset);
- if ( agile ) {
- sb->index[3].type = htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES + 0);
- sb->index[3].offset = htonl(cd256Offset);
- }
-
- // fill in empty requirements
- CS_RequirementsBlob* reqs = (CS_RequirementsBlob*)(((char*)sb)+reqsOffset);
- reqs->magic = htonl(CSMAGIC_REQUIREMENTS);
- reqs->length = htonl(sizeof(CS_RequirementsBlob));
- reqs->data = 0;
-
- // initialize fixed fields of Code Directory
- CS_CodeDirectory* cd = (CS_CodeDirectory*)(((char*)sb)+cdOffset);
- cd->magic = htonl(CSMAGIC_CODEDIRECTORY);
- cd->length = htonl(cdSize);
- cd->version = htonl(0x20400); // supports exec segment
- cd->flags = htonl(kSecCodeSignatureAdhoc);
- cd->hashOffset = htonl(hashOffset);
- cd->identOffset = htonl(idOffset);
- cd->nSpecialSlots = htonl(xSlotCount);
- cd->nCodeSlots = htonl(slotCount);
- cd->codeLimit = htonl(inBbufferSize);
- cd->hashSize = dscHashSize;
- cd->hashType = dscHashType;
- cd->platform = 0; // not platform binary
- cd->pageSize = __builtin_ctz(pageSize); // log2(CS_PAGE_SIZE);
- cd->spare2 = 0; // unused (must be zero)
- cd->scatterOffset = 0; // not supported anymore
- cd->teamOffset = 0; // no team ID
- cd->spare3 = 0; // unused (must be zero)
- cd->codeLimit64 = 0; // falls back to codeLimit
-
- // executable segment info
- cd->execSegBase = htonll(_readExecuteRegion.cacheFileOffset); // base of TEXT segment
- cd->execSegLimit = htonll(_readExecuteRegion.sizeInUse); // size of TEXT segment
- cd->execSegFlags = 0; // not a main binary
-
- // initialize dynamic fields of Code Directory
- strcpy((char*)cd + idOffset, cacheIdentifier.c_str());
-
- // add special slot hashes
- uint8_t* hashSlot = (uint8_t*)cd + hashOffset;
- uint8_t* reqsHashSlot = &hashSlot[-CSSLOT_REQUIREMENTS*dscHashSize];
- CCDigest(dscDigestFormat, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHashSlot);
-
- CS_CodeDirectory* cd256;
- uint8_t* hash256Slot;
- uint8_t* reqsHash256Slot;
- if ( agile ) {
- // Note that the assumption here is that the size up to the hashes is the same as for
- // sha1 code directory, and that they come last, after everything else.
-
- cd256 = (CS_CodeDirectory*)(((char*)sb)+cd256Offset);
- cd256->magic = htonl(CSMAGIC_CODEDIRECTORY);
- cd256->length = htonl(cd256Size);
- cd256->version = htonl(0x20400); // supports exec segment
- cd256->flags = htonl(kSecCodeSignatureAdhoc);
- cd256->hashOffset = htonl(hash256Offset);
- cd256->identOffset = htonl(idOffset);
- cd256->nSpecialSlots = htonl(xSlotCount);
- cd256->nCodeSlots = htonl(slotCount);
- cd256->codeLimit = htonl(inBbufferSize);
- cd256->hashSize = CS_HASH_SIZE_SHA256;
- cd256->hashType = CS_HASHTYPE_SHA256;
- cd256->platform = 0; // not platform binary
- cd256->pageSize = __builtin_ctz(pageSize); // log2(CS_PAGE_SIZE);
- cd256->spare2 = 0; // unused (must be zero)
- cd256->scatterOffset = 0; // not supported anymore
- cd256->teamOffset = 0; // no team ID
- cd256->spare3 = 0; // unused (must be zero)
- cd256->codeLimit64 = 0; // falls back to codeLimit
-
- // executable segment info
- cd256->execSegBase = cd->execSegBase;
- cd256->execSegLimit = cd->execSegLimit;
- cd256->execSegFlags = cd->execSegFlags;
-
- // initialize dynamic fields of Code Directory
- strcpy((char*)cd256 + idOffset, cacheIdentifier.c_str());
-
- // add special slot hashes
- hash256Slot = (uint8_t*)cd256 + hash256Offset;
- reqsHash256Slot = &hash256Slot[-CSSLOT_REQUIREMENTS*CS_HASH_SIZE_SHA256];
- CCDigest(kCCDigestSHA256, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHash256Slot);
- }
- else {
- cd256 = NULL;
- hash256Slot = NULL;
- reqsHash256Slot = NULL;
- }
-
- // fill in empty CMS blob for ad-hoc signing
- CS_Blob* cms = (CS_Blob*)(((char*)sb)+cmsOffset);
- cms->magic = htonl(CSMAGIC_BLOBWRAPPER);
- cms->length = htonl(sizeof(CS_Blob));
-
-
- // alter header of cache to record size and location of code signature
- // do this *before* hashing each page
- dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer;
- cache->codeSignatureOffset = inBbufferSize;
- cache->codeSignatureSize = sigSize;
-
- const uint32_t rwSlotStart = (uint32_t)(_readExecuteRegion.sizeInUse / pageSize);
- const uint32_t roSlotStart = (uint32_t)(rwSlotStart + _readWriteRegion.sizeInUse / pageSize);
- const uint32_t localsSlotStart = (uint32_t)(roSlotStart + _readOnlyRegion.sizeInUse / pageSize);
- auto codeSignPage = ^(size_t i) {
- const uint8_t* code = nullptr;
- // move to correct region
- if ( i < rwSlotStart )
- code = _readExecuteRegion.buffer + (i * pageSize);
- else if ( i >= rwSlotStart && i < roSlotStart )
- code = _readWriteRegion.buffer + ((i - rwSlotStart) * pageSize);
- else if ( i >= roSlotStart && i < localsSlotStart )
- code = _readOnlyRegion.buffer + ((i - roSlotStart) * pageSize);
- else
- code = _localSymbolsRegion.buffer + ((i - localsSlotStart) * pageSize);
-
- CCDigest(dscDigestFormat, code, pageSize, hashSlot + (i * dscHashSize));
-
- if ( agile ) {
- CCDigest(kCCDigestSHA256, code, pageSize, hash256Slot + (i * CS_HASH_SIZE_SHA256));
- }
- };
-
- // compute hashes
- dispatch_apply(slotCount, DISPATCH_APPLY_AUTO, ^(size_t i) {
- codeSignPage(i);
- });
-
- // Now that we have a code signature, compute a cache UUID by hashing the code signature blob
- {
- uint8_t* uuidLoc = cache->uuid;
- assert(uuid_is_null(uuidLoc));
- static_assert(offsetof(dyld_cache_header, uuid) / CS_PAGE_SIZE_4K == 0, "uuid is expected in the first page of the cache");
- uint8_t fullDigest[CC_SHA256_DIGEST_LENGTH];
- CC_SHA256((const void*)cd, (unsigned)cdSize, fullDigest);
- memcpy(uuidLoc, fullDigest, 16);
- // <rdar://problem/6723729> uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
- uuidLoc[6] = ( uuidLoc[6] & 0x0F ) | ( 3 << 4 );
- uuidLoc[8] = ( uuidLoc[8] & 0x3F ) | 0x80;
-
- // Now codesign page 0 again, because we modified it by setting uuid in header
- codeSignPage(0);
- }
-
- // hash of entire code directory (cdHash) uses same hash as each page
- uint8_t fullCdHash[dscHashSize];
- CCDigest(dscDigestFormat, (const uint8_t*)cd, cdSize, fullCdHash);
- // Note: cdHash is defined as first 20 bytes of hash
- memcpy(_cdHashFirst, fullCdHash, 20);
- if ( agile ) {
- uint8_t fullCdHash256[CS_HASH_SIZE_SHA256];
- CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, cd256Size, fullCdHash256);
- // Note: cdHash is defined as first 20 bytes of hash, even for sha256
- memcpy(_cdHashSecond, fullCdHash256, 20);
- }
- else {
- memset(_cdHashSecond, 0, 20);
- }
-}
-
-const bool CacheBuilder::agileSignature()
-{
- return _options.codeSigningDigestMode == DyldSharedCache::Agile;
-}
-
-static const std::string cdHash(uint8_t hash[20])
-{
- char buff[48];
- for (int i = 0; i < 20; ++i)
- sprintf(&buff[2*i], "%2.2x", hash[i]);
- return buff;
-}
-
-const std::string CacheBuilder::cdHashFirst()
-{
- return cdHash(_cdHashFirst);
-}
+#include "MachOFileAbstraction.hpp"
+#include "DyldSharedCache.h"
+#include "CacheBuilder.h"
+#include "Diagnostics.h"
-const std::string CacheBuilder::cdHashSecond()
-{
- return cdHash(_cdHashSecond);
-}
-const std::string CacheBuilder::uuid() const
+CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem)
+ : _options(options)
+ , _fileSystem(fileSystem)
+ , _fullAllocatedBuffer(0)
+ , _diagnostics(options.loggingPrefix, options.verbose)
+ , _allocatedBufferSize(0)
{
- dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer;
- uuid_string_t uuidStr;
- uuid_unparse(cache->uuid, uuidStr);
- return uuidStr;
-}
-
-static dyld_cache_patchable_location makePatchLocation(size_t cacheOff, uint64_t ad) {
- int64_t signedAddend = (int64_t)ad;
- assert(((signedAddend << 52) >> 52) == signedAddend);
- dyld_cache_patchable_location patch;
- patch.cacheOffset = cacheOff;
- patch.addend = ad;
- patch.authenticated = 0;
- patch.usesAddressDiversity = 0;
- patch.key = 0;
- patch.discriminator = 0;
- return patch;
-}
-
-static dyld_cache_patchable_location makePatchLocation(size_t cacheOff, uint64_t ad,
- dyld3::MachOLoaded::ChainedFixupPointerOnDisk authInfo) {
- int64_t signedAddend = (int64_t)ad;
- assert(((signedAddend << 52) >> 52) == signedAddend);
- dyld_cache_patchable_location patch;
- patch.cacheOffset = cacheOff;
- patch.addend = ad;
- patch.authenticated = authInfo.arm64e.authBind.auth;
- patch.usesAddressDiversity = authInfo.arm64e.authBind.addrDiv;
- patch.key = authInfo.arm64e.authBind.key;
- patch.discriminator = authInfo.arm64e.authBind.diversity;
- return patch;
}
-void CacheBuilder::buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases)
+std::string CacheBuilder::errorMessage()
{
- typedef dyld3::closure::ClosureBuilder::CachedDylibInfo CachedDylibInfo;
-
- // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray()
- __block std::vector<CachedDylibInfo> dylibInfos;
- __block std::unordered_map<dyld3::closure::ImageNum, const dyld3::MachOLoaded*> imageNumToML;
- DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
- cache->forEachImage(^(const mach_header* mh, const char* installName) {
- uint64_t mtime;
- uint64_t inode;
- cache->getIndexedImageEntry((uint32_t)dylibInfos.size(), mtime, inode);
- CachedDylibInfo entry;
- entry.fileInfo.fileContent = mh;
- entry.fileInfo.path = installName;
- entry.fileInfo.sliceOffset = 0;
- entry.fileInfo.inode = inode;
- entry.fileInfo.mtime = mtime;
- dylibInfos.push_back(entry);
- imageNumToML[(dyld3::closure::ImageNum)(dylibInfos.size())] = (dyld3::MachOLoaded*)mh;
- });
-
- // Convert symlinks from STL to simple char pointers.
- std::vector<dyld3::closure::ClosureBuilder::CachedDylibAlias> dylibAliases;
- dylibAliases.reserve(aliases.size());
- for (const auto& alias : aliases)
- dylibAliases.push_back({ alias.realPath.c_str(), alias.aliasPath.c_str() });
-
- dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers handlers;
-
- handlers.chainedBind = ^(dyld3::closure::ImageNum, const dyld3::MachOLoaded* imageLoadAddress,
- const dyld_chained_starts_in_image* starts,
- const dyld3::Array<dyld3::closure::Image::ResolvedSymbolTarget>& targets,
- const dyld3::Array<dyld3::closure::ClosureBuilder::ResolvedTargetInfo>& targetInfos) {
- imageLoadAddress->forEachFixupInAllChains(_diagnostics, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
- uint64_t offsetInCache;
- dyld3::closure::Image::ResolvedSymbolTarget target;
- const dyld3::closure::ClosureBuilder::ResolvedTargetInfo* targetInfo;
- switch (segInfo->pointer_format) {
- case DYLD_CHAINED_PTR_ARM64E:
- if ( fixupLoc->arm64e.bind.bind ) {
- target = targets[fixupLoc->arm64e.bind.ordinal];
- targetInfo = &targetInfos[fixupLoc->arm64e.bind.ordinal];
- switch ( target.sharedCache.kind ) {
- case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
- offsetInCache = target.sharedCache.offset - targetInfo->addend;
- _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache);
- _exportsToName[offsetInCache] = targetInfo->foundSymbolName;
- if ( fixupLoc->arm64e.authBind.auth ) {
- // turn this auth bind into an auth rebase into the cache
- fixupLoc->arm64e.authRebase.bind = 0;
- fixupLoc->arm64e.authRebase.target = target.sharedCache.offset;
- _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend, *fixupLoc));
- }
- else {
- // turn this plain bind into an plain rebase into the cache
- fixupLoc->arm64e.rebase.bind = 0;
- fixupLoc->arm64e.rebase.target = _archLayout->sharedMemoryStart + target.sharedCache.offset;
- _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend));
- }
- _aslrTracker.add(fixupLoc);
- break;
- case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
- fixupLoc->raw64 = target.absolute.value;
- // don't record absolute targets for ASLR
- // HACK: Split seg may have added a target. Remove it
- _aslrTracker.remove(fixupLoc);
- if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) {
- _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1);
- }
- break;
- default:
- assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
- }
- } else {
- _aslrTracker.add(fixupLoc);
- }
- break;
- case DYLD_CHAINED_PTR_64:
- if ( fixupLoc->generic64.bind.bind ) {
- target = targets[fixupLoc->generic64.bind.ordinal];
- targetInfo = &targetInfos[fixupLoc->generic64.bind.ordinal];
- switch ( target.sharedCache.kind ) {
- case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
- offsetInCache = target.sharedCache.offset - targetInfo->addend;
- _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache);
- _exportsToName[offsetInCache] = targetInfo->foundSymbolName;
- // turn this bind into a rebase into the cache
- fixupLoc->generic64.rebase.bind = 0;
- fixupLoc->generic64.rebase.next = 0; // rechained later
- fixupLoc->generic64.rebase.reserved = 0;
- fixupLoc->generic64.rebase.high8 = 0;
- fixupLoc->generic64.rebase.target = target.sharedCache.offset;
- _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend));
- _aslrTracker.add(fixupLoc);
- break;
- case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
- fixupLoc->raw64 = target.absolute.value;
- // don't record absolute targets for ASLR
- if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) {
- _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1);
- }
- break;
- default:
- assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
- }
- }
- break;
- case DYLD_CHAINED_PTR_32:
- if ( fixupLoc->generic32.bind.bind ) {
- target = targets[fixupLoc->generic32.bind.ordinal];
- targetInfo = &targetInfos[fixupLoc->generic32.bind.ordinal];
- switch ( target.sharedCache.kind ) {
- case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
- offsetInCache = target.sharedCache.offset - targetInfo->addend;
- _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache);
- _exportsToName[offsetInCache] = targetInfo->foundSymbolName;
- // turn this bind into a rebase into the cache
- fixupLoc->cache32.next = 0; // rechained later
- fixupLoc->cache32.target = (uint32_t)(target.sharedCache.offset);
- _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend));
- _aslrTracker.add(fixupLoc);
- break;
- case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
- fixupLoc->raw32 = (uint32_t)target.absolute.value;
- // don't record absolute targets for ASLR
- if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) {
- _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1);
- }
- break;
- default:
- assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
- }
- }
- break;
- default:
- assert(0 && "unsupported chained bind type");
- }
-
- });
- };
-
- handlers.rebase = ^(dyld3::closure::ImageNum imageNum, const dyld3::MachOLoaded* imageToFix, uint32_t runtimeOffset) {
- // record location in aslr tracker so kernel can slide this on page-in
- uint8_t* fixupLoc = (uint8_t*)imageToFix+runtimeOffset;
- _aslrTracker.add(fixupLoc);
- };
-
- handlers.bind = ^(dyld3::closure::ImageNum imageNum, const dyld3::MachOLoaded* mh,
- uint32_t runtimeOffset, dyld3::closure::Image::ResolvedSymbolTarget target,
- const dyld3::closure::ClosureBuilder::ResolvedTargetInfo& targetInfo) {
- uint8_t* fixupLoc = (uint8_t*)mh+runtimeOffset;
-
- // binder is called a second time for weak_bind info, which we ignore when building cache
- const bool weakDefUseAlreadySet = targetInfo.weakBindCoalese && _aslrTracker.has(fixupLoc);
-
- // do actual bind that sets pointer in image to unslid target address
- uint64_t offsetInCache;
- switch ( target.sharedCache.kind ) {
- case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
- offsetInCache = target.sharedCache.offset - targetInfo.addend;
- _dylibToItsExports[targetInfo.foundInDylib].insert(offsetInCache);
- if (targetInfo.isWeakDef)
- _dylibWeakExports.insert({ targetInfo.foundInDylib, offsetInCache });
- _exportsToUses[offsetInCache].push_back(makePatchLocation(fixupLoc - _readExecuteRegion.buffer, targetInfo.addend));
- _exportsToName[offsetInCache] = targetInfo.foundSymbolName;
- if ( !weakDefUseAlreadySet ) {
- if ( _archLayout->is64 )
- *((uint64_t*)fixupLoc) = _archLayout->sharedMemoryStart + target.sharedCache.offset;
- else
- *((uint32_t*)fixupLoc) = (uint32_t)(_archLayout->sharedMemoryStart + target.sharedCache.offset);
- // record location in aslr tracker so kernel can slide this on page-in
- _aslrTracker.add(fixupLoc);
- }
- break;
- case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
- if ( _archLayout->is64 )
- *((uint64_t*)fixupLoc) = target.absolute.value;
- else
- *((uint32_t*)fixupLoc) = (uint32_t)(target.absolute.value);
- // don't record absolute targets for ASLR
- // HACK: Split seg may have added a target. Remove it
- _aslrTracker.remove(fixupLoc);
- if ( (targetInfo.libOrdinal > 0) && (targetInfo.libOrdinal <= (int)(mh->dependentDylibCount())) ) {
- _missingWeakImports[fixupLoc] = mh->dependentDylibLoadPath(targetInfo.libOrdinal - 1);
- }
- break;
- default:
- assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
- }
- };
-
- // build ImageArray for all dylibs in dyld cache
- dyld3::closure::PathOverrides pathOverrides;
- dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstDyldCacheImageNum, _fileSystem, cache, false, *_options.archs, pathOverrides,
- dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform, &handlers);
- dyld3::Array<CachedDylibInfo> dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size());
- const dyld3::Array<dyld3::closure::ClosureBuilder::CachedDylibAlias> aliasesArray(dylibAliases.data(), dylibAliases.size(), dylibAliases.size());
- _imageArray = cb.makeDyldCacheImageArray(_options.optimizeStubs, dylibs, aliasesArray);
- if ( cb.diagnostics().hasError() ) {
- _diagnostics.error("%s", cb.diagnostics().errorMessage().c_str());
- return;
- }
-}
-
-static bool operator==(const dyld_cache_patchable_location& a, const dyld_cache_patchable_location& b) {
- return a.cacheOffset == b.cacheOffset;
+ return _diagnostics.errorMessage();
}
-void CacheBuilder::addImageArray()
+void CacheBuilder::copyRawSegments()
{
- // build trie of dylib paths
- __block std::vector<DylibIndexTrie::Entry> dylibEntrys;
- _imageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
- dylibEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum()-1)));
- image->forEachAlias(^(const char *aliasPath, bool &innerStop) {
- dylibEntrys.push_back(DylibIndexTrie::Entry(aliasPath, DylibIndex(image->imageNum()-1)));
- });
- });
- DylibIndexTrie dylibsTrie(dylibEntrys);
- std::vector<uint8_t> trieBytes;
- dylibsTrie.emit(trieBytes);
- while ( (trieBytes.size() % 4) != 0 )
- trieBytes.push_back(0);
-
- // build set of functions to never stub-eliminate because tools may need to override them
- std::unordered_set<std::string> alwaysGeneratePatch;
- for (const char* const* p=_s_neverStubEliminateSymbols; *p != nullptr; ++p)
- alwaysGeneratePatch.insert(*p);
-
- // Add the patches for the image array.
- __block uint64_t numPatchImages = _imageArray->size();
- __block uint64_t numPatchExports = 0;
- __block uint64_t numPatchLocations = 0;
- __block uint64_t numPatchExportNameBytes = 0;
-
- auto needsPatch = [&](bool dylibNeedsPatching, const dyld3::MachOLoaded* mh,
- CacheOffset offset) -> bool {
- if (dylibNeedsPatching)
- return true;
- if (_dylibWeakExports.find({ mh, offset }) != _dylibWeakExports.end())
- return true;
- const std::string& exportName = _exportsToName[offset];
- return alwaysGeneratePatch.find(exportName) != alwaysGeneratePatch.end();
- };
-
- std::set<std::string> alwaysPatchDylibs;
- for (const char* const* d= _s_neverStubEliminateDylibs; *d != nullptr; ++d)
- alwaysPatchDylibs.insert(*d);
-
- // First calculate how much space we need
- const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
- cache->forEachImage(^(const mach_header* mh, const char* installName) {
- const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh;
- const std::set<CacheOffset>& dylibExports = _dylibToItsExports[ml];
-
- // On a customer cache, only store patch locations for interposable dylibs and weak binding
- bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName);
-
- uint64_t numDylibExports = 0;
- for (CacheOffset exportCacheOffset : dylibExports) {
- if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset))
- continue;
- std::vector<dyld_cache_patchable_location>& uses = _exportsToUses[exportCacheOffset];
- uses.erase(std::unique(uses.begin(), uses.end()), uses.end());
- numPatchLocations += uses.size();
-
- std::string exportName = _exportsToName[exportCacheOffset];
- numPatchExportNameBytes += exportName.size() + 1;
- }
- numPatchExports += numDylibExports;
- });
-
- // Now reserve the space
- __block std::vector<dyld_cache_image_patches> patchImages;
- __block std::vector<dyld_cache_patchable_export> patchExports;
- __block std::vector<dyld_cache_patchable_location> patchLocations;
- __block std::vector<char> patchExportNames;
-
- patchImages.reserve(numPatchImages);
- patchExports.reserve(numPatchExports);
- patchLocations.reserve(numPatchLocations);
- patchExportNames.reserve(numPatchExportNameBytes);
-
- // And now fill it with the patch data
- cache->forEachImage(^(const mach_header* mh, const char* installName) {
- const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh;
- const std::set<CacheOffset>& dylibExports = _dylibToItsExports[ml];
-
- // On a customer cache, only store patch locations for interposable dylibs and weak binding
- bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName);
-
- // Add the patch image which points in to the exports
- dyld_cache_image_patches patchImage;
- patchImage.patchExportsStartIndex = (uint32_t)patchExports.size();
- patchImage.patchExportsCount = 0;
-
- // Then add each export which points to a list of locations and a name
- for (CacheOffset exportCacheOffset : dylibExports) {
- if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset))
- continue;
- ++patchImage.patchExportsCount;
- std::vector<dyld_cache_patchable_location>& uses = _exportsToUses[exportCacheOffset];
-
- dyld_cache_patchable_export cacheExport;
- cacheExport.cacheOffsetOfImpl = (uint32_t)exportCacheOffset;
- cacheExport.patchLocationsStartIndex = (uint32_t)patchLocations.size();
- cacheExport.patchLocationsCount = (uint32_t)uses.size();
- cacheExport.exportNameOffset = (uint32_t)patchExportNames.size();
- patchExports.push_back(cacheExport);
-
- // Now add the list of locations.
- patchLocations.insert(patchLocations.end(), uses.begin(), uses.end());
-
- // And add the export name
- const std::string& exportName = _exportsToName[exportCacheOffset];
- patchExportNames.insert(patchExportNames.end(), &exportName[0], &exportName[0] + exportName.size() + 1);
+ const bool log = false;
+ dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+ const DylibInfo& dylib = _sortedDylibs[index];
+ for (const SegmentMappingInfo& info : dylib.cacheLocation) {
+ if (log) fprintf(stderr, "copy %s segment %s (0x%08X bytes) from %p to %p (logical addr 0x%llX) for %s\n",
+ _options.archs->name(), info.segName, info.copySegmentSize, info.srcSegment, info.dstSegment, info.dstCacheUnslidAddress, dylib.input->mappedFile.runtimePath.c_str());
+ ::memcpy(info.dstSegment, info.srcSegment, info.copySegmentSize);
}
- patchImages.push_back(patchImage);
});
- while ( (patchExportNames.size() % 4) != 0 )
- patchExportNames.push_back('\0');
-
- uint64_t patchInfoSize = sizeof(dyld_cache_patch_info);
- patchInfoSize += sizeof(dyld_cache_image_patches) * patchImages.size();
- patchInfoSize += sizeof(dyld_cache_patchable_export) * patchExports.size();
- patchInfoSize += sizeof(dyld_cache_patchable_location) * patchLocations.size();
- patchInfoSize += patchExportNames.size();
-
- // check for fit
- uint64_t imageArraySize = _imageArray->size();
- size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
- if ( (imageArraySize+trieBytes.size()+patchInfoSize) > freeSpace ) {
- _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, patch size=%lluKB, free space=%ldMB)",
- _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, patchInfoSize/1024, freeSpace/1024/1024);
- return;
- }
-
- // copy into cache and update header
- DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
- dyldCache->header.dylibsImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
- dyldCache->header.dylibsImageArraySize = imageArraySize;
- dyldCache->header.dylibsTrieAddr = dyldCache->header.dylibsImageArrayAddr + imageArraySize;
- dyldCache->header.dylibsTrieSize = trieBytes.size();
- ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, _imageArray, imageArraySize);
- ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
-
- // Also write out the patch info
- dyldCache->header.patchInfoAddr = dyldCache->header.dylibsTrieAddr + dyldCache->header.dylibsTrieSize;
- dyldCache->header.patchInfoSize = patchInfoSize;
- dyld_cache_patch_info patchInfo;
- patchInfo.patchTableArrayAddr = dyldCache->header.patchInfoAddr + sizeof(dyld_cache_patch_info);
- patchInfo.patchTableArrayCount = patchImages.size();
- patchInfo.patchExportArrayAddr = patchInfo.patchTableArrayAddr + (patchInfo.patchTableArrayCount * sizeof(dyld_cache_image_patches));
- patchInfo.patchExportArrayCount = patchExports.size();
- patchInfo.patchLocationArrayAddr = patchInfo.patchExportArrayAddr + (patchInfo.patchExportArrayCount * sizeof(dyld_cache_patchable_export));
- patchInfo.patchLocationArrayCount = patchLocations.size();
- patchInfo.patchExportNamesAddr = patchInfo.patchLocationArrayAddr + (patchInfo.patchLocationArrayCount * sizeof(dyld_cache_patchable_location));
- patchInfo.patchExportNamesSize = patchExportNames.size();
- ::memcpy(_readOnlyRegion.buffer + dyldCache->header.patchInfoAddr - _readOnlyRegion.unslidLoadAddress,
- &patchInfo, sizeof(dyld_cache_patch_info));
- ::memcpy(_readOnlyRegion.buffer + patchInfo.patchTableArrayAddr - _readOnlyRegion.unslidLoadAddress,
- &patchImages[0], sizeof(patchImages[0]) * patchImages.size());
- ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportArrayAddr - _readOnlyRegion.unslidLoadAddress,
- &patchExports[0], sizeof(patchExports[0]) * patchExports.size());
- ::memcpy(_readOnlyRegion.buffer + patchInfo.patchLocationArrayAddr - _readOnlyRegion.unslidLoadAddress,
- &patchLocations[0], sizeof(patchLocations[0]) * patchLocations.size());
- ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportNamesAddr - _readOnlyRegion.unslidLoadAddress,
- &patchExportNames[0], patchExportNames.size());
-
- _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size()+patchInfoSize,14);
-
- // Free the underlying image array buffer
- _imageArray->deallocate();
-}
-
-void CacheBuilder::addOtherImageArray(const std::vector<LoadedMachO>& otherDylibsAndBundles, std::vector<const LoadedMachO*>& overflowDylibs)
-{
- DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
- dyld3::closure::PathOverrides pathOverrides;
- dyld3::closure::FileSystemNull nullFileSystem;
- dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstOtherOSImageNum, nullFileSystem, cache, false, *_options.archs, pathOverrides,
- dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform);
-
- // make ImageArray for other dylibs and bundles
- STACK_ALLOC_ARRAY(dyld3::closure::LoadedFileInfo, others, otherDylibsAndBundles.size() + overflowDylibs.size());
- for (const LoadedMachO& other : otherDylibsAndBundles) {
- if ( !contains(other.loadedFileInfo.path, "staged_system_apps/") )
- others.push_back(other.loadedFileInfo);
- }
-
- for (const LoadedMachO* dylib : overflowDylibs) {
- if (dylib->mappedFile.mh->canHavePrecomputedDlopenClosure(dylib->mappedFile.runtimePath.c_str(), ^(const char*) {}) )
- others.push_back(dylib->loadedFileInfo);
- }
-
- // Sort the others array by name so that it is deterministic
- std::sort(others.begin(), others.end(),
- [](const dyld3::closure::LoadedFileInfo& a, const dyld3::closure::LoadedFileInfo& b) {
- // Sort mac before iOSMac
- bool isIOSMacA = strncmp(a.path, "/System/iOSSupport/", 19) == 0;
- bool isIOSMacB = strncmp(b.path, "/System/iOSSupport/", 19) == 0;
- if (isIOSMacA != isIOSMacB)
- return !isIOSMacA;
- return strcmp(a.path, b.path) < 0;
- });
-
- const dyld3::closure::ImageArray* otherImageArray = cb.makeOtherDylibsImageArray(others, (uint32_t)_sortedDylibs.size());
-
- // build trie of paths
- __block std::vector<DylibIndexTrie::Entry> otherEntrys;
- otherImageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
- if ( !image->isInvalid() )
- otherEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum())));
+ // Copy the coalesced sections
+ const uint64_t numCoalescedSections = sizeof(CacheCoalescedText::SupportedSections) / sizeof(*CacheCoalescedText::SupportedSections);
+ dispatch_apply(numCoalescedSections, DISPATCH_APPLY_AUTO, ^(size_t index) {
+ const CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData(CacheCoalescedText::SupportedSections[index]);
+ if (log) fprintf(stderr, "copy %s __TEXT_COAL section %s (0x%08X bytes) to %p (logical addr 0x%llX)\n",
+ _options.archs->name(), CacheCoalescedText::SupportedSections[index],
+ cacheStringSection.bufferSize, cacheStringSection.bufferAddr, cacheStringSection.bufferVMAddr);
+ for (const auto& stringAndOffset : cacheStringSection.stringsToOffsets)
+ ::memcpy(cacheStringSection.bufferAddr + stringAndOffset.second, stringAndOffset.first.data(), stringAndOffset.first.size() + 1);
});
- DylibIndexTrie dylibsTrie(otherEntrys);
- std::vector<uint8_t> trieBytes;
- dylibsTrie.emit(trieBytes);
- while ( (trieBytes.size() % 4) != 0 )
- trieBytes.push_back(0);
-
- // check for fit
- uint64_t imageArraySize = otherImageArray->size();
- size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
- if ( imageArraySize+trieBytes.size() > freeSpace ) {
- _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, free space=%ldMB)",
- _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, freeSpace/1024/1024);
- return;
- }
-
- // copy into cache and update header
- DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
- dyldCache->header.otherImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
- dyldCache->header.otherImageArraySize = imageArraySize;
- dyldCache->header.otherTrieAddr = dyldCache->header.otherImageArrayAddr + imageArraySize;
- dyldCache->header.otherTrieSize = trieBytes.size();
- ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, otherImageArray, imageArraySize);
- ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
- _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size(),14);
-
- // Free the underlying buffer
- otherImageArray->deallocate();
}
-
-void CacheBuilder::addClosures(const std::vector<LoadedMachO>& osExecutables)
+void CacheBuilder::adjustAllImagesForNewSegmentLocations()
{
- const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
-
- __block std::vector<Diagnostics> osExecutablesDiags;
- __block std::vector<const dyld3::closure::LaunchClosure*> osExecutablesClosures;
- osExecutablesDiags.resize(osExecutables.size());
- osExecutablesClosures.resize(osExecutables.size());
-
- dispatch_apply(osExecutables.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
- const LoadedMachO& loadedMachO = osExecutables[index];
- // don't pre-build closures for staged apps into dyld cache, since they won't run from that location
- if ( startsWith(loadedMachO.mappedFile.runtimePath, "/private/var/staged_system_apps/") ) {
- return;
- }
- dyld3::closure::PathOverrides pathOverrides;
- dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, _fileSystem, dyldCache, false, *_options.archs, pathOverrides,
- dyld3::closure::ClosureBuilder::AtPath::all, false, nullptr, _options.platform, nullptr);
- bool issetuid = false;
- if ( this->_options.platform == dyld3::Platform::macOS || dyld3::MachOFile::isSimulatorPlatform(this->_options.platform) )
- _fileSystem.fileExists(loadedMachO.loadedFileInfo.path, nullptr, nullptr, &issetuid, nullptr);
- const dyld3::closure::LaunchClosure* mainClosure = builder.makeLaunchClosure(loadedMachO.loadedFileInfo, issetuid);
- if ( builder.diagnostics().hasError() ) {
- osExecutablesDiags[index].error("%s", builder.diagnostics().errorMessage().c_str());
- }
- else {
- assert(mainClosure != nullptr);
- osExecutablesClosures[index] = mainClosure;
- }
- });
-
- std::map<std::string, const dyld3::closure::LaunchClosure*> closures;
- for (uint64_t i = 0, e = osExecutables.size(); i != e; ++i) {
- const LoadedMachO& loadedMachO = osExecutables[i];
- const Diagnostics& diag = osExecutablesDiags[i];
- if (diag.hasError()) {
- if ( _options.verbose ) {
- _diagnostics.warning("building closure for '%s': %s", loadedMachO.mappedFile.runtimePath.c_str(), diag.errorMessage().c_str());
- for (const std::string& warn : diag.warnings() )
- _diagnostics.warning("%s", warn.c_str());
- }
- if ( loadedMachO.inputFile && (loadedMachO.inputFile->mustBeIncluded()) ) {
- loadedMachO.inputFile->diag.error("%s", diag.errorMessage().c_str());
- }
- } else {
- // Note, a closure could be null here if it has a path we skip.
- if (osExecutablesClosures[i] != nullptr)
- closures[loadedMachO.mappedFile.runtimePath] = osExecutablesClosures[i];
- }
- }
-
- osExecutablesDiags.clear();
- osExecutablesClosures.clear();
-
- // preflight space needed
- size_t closuresSpace = 0;
- for (const auto& entry : closures) {
- closuresSpace += entry.second->size();
- }
- size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
- if ( closuresSpace > freeSpace ) {
- _diagnostics.error("cache buffer too small to hold all closures (buffer size=%lldMB, closures size=%ldMB, free space=%ldMB)",
- _allocatedBufferSize/1024/1024, closuresSpace/1024/1024, freeSpace/1024/1024);
- return;
- }
- DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
- cache->header.progClosuresAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
- uint8_t* closuresBase = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse;
- std::vector<DylibIndexTrie::Entry> closureEntrys;
- uint32_t currentClosureOffset = 0;
- for (const auto& entry : closures) {
- const dyld3::closure::LaunchClosure* closure = entry.second;
- closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset)));
- size_t size = closure->size();
- assert((size % 4) == 0);
- memcpy(closuresBase+currentClosureOffset, closure, size);
- currentClosureOffset += size;
- freeSpace -= size;
- closure->deallocate();
- }
- cache->header.progClosuresSize = currentClosureOffset;
- _readOnlyRegion.sizeInUse += currentClosureOffset;
- freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
- // build trie of indexes into closures list
- DylibIndexTrie closureTrie(closureEntrys);
- std::vector<uint8_t> trieBytes;
- closureTrie.emit(trieBytes);
- while ( (trieBytes.size() % 8) != 0 )
- trieBytes.push_back(0);
- if ( trieBytes.size() > freeSpace ) {
- _diagnostics.error("cache buffer too small to hold all closures trie (buffer size=%lldMB, trie size=%ldMB, free space=%ldMB)",
- _allocatedBufferSize/1024/1024, trieBytes.size()/1024/1024, freeSpace/1024/1024);
- return;
- }
- memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, &trieBytes[0], trieBytes.size());
- cache->header.progClosuresTrieAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
- cache->header.progClosuresTrieSize = trieBytes.size();
- _readOnlyRegion.sizeInUse += trieBytes.size();
- _readOnlyRegion.sizeInUse = align(_readOnlyRegion.sizeInUse, 14);
-}
-
+ __block std::vector<Diagnostics> diags;
+ diags.resize(_sortedDylibs.size());
-bool CacheBuilder::writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset))
-{
- const dyld_cache_header* cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
- const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + cacheHeader->mappingOffset);
- assert(_readExecuteRegion.sizeInUse == mappings[0].size);
- assert(_readWriteRegion.sizeInUse == mappings[1].size);
- assert(_readOnlyRegion.sizeInUse == mappings[2].size);
- assert(_readExecuteRegion.cacheFileOffset == mappings[0].fileOffset);
- assert(_readWriteRegion.cacheFileOffset == mappings[1].fileOffset);
- assert(_readOnlyRegion.cacheFileOffset == mappings[2].fileOffset);
- assert(_codeSignatureRegion.sizeInUse == cacheHeader->codeSignatureSize);
- assert(cacheHeader->codeSignatureOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse);
- cacheSizeCallback(_readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse+_codeSignatureRegion.sizeInUse);
- bool fullyWritten = copyCallback(_readExecuteRegion.buffer, _readExecuteRegion.sizeInUse, mappings[0].fileOffset);
- fullyWritten &= copyCallback(_readWriteRegion.buffer, _readWriteRegion.sizeInUse, mappings[1].fileOffset);
- fullyWritten &= copyCallback(_readOnlyRegion.buffer, _readOnlyRegion.sizeInUse, mappings[2].fileOffset);
- if ( _localSymbolsRegion.sizeInUse != 0 ) {
- assert(cacheHeader->localSymbolsOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse);
- fullyWritten &= copyCallback(_localSymbolsRegion.buffer, _localSymbolsRegion.sizeInUse, cacheHeader->localSymbolsOffset);
+ // Note this cannot to be done in parallel because the LOH Tracker and aslr tracker are not thread safe
+ for (size_t index = 0; index != _sortedDylibs.size(); ++index) {
+ const DylibInfo& dylib = _sortedDylibs[index];
+ adjustDylibSegments(dylib, diags[index]);
}
- fullyWritten &= copyCallback(_codeSignatureRegion.buffer, _codeSignatureRegion.sizeInUse, cacheHeader->codeSignatureOffset);
- return fullyWritten;
-}
-
-
-void CacheBuilder::writeFile(const std::string& path)
-{
- std::string pathTemplate = path + "-XXXXXX";
- size_t templateLen = strlen(pathTemplate.c_str())+2;
- BLOCK_ACCCESSIBLE_ARRAY(char, pathTemplateSpace, templateLen);
- strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen);
- int fd = mkstemp(pathTemplateSpace);
- if ( fd != -1 ) {
- auto cacheSizeCallback = ^(uint64_t size) {
- // if making macOS dyld cache for current OS into standard location
- if ( (_options.platform == dyld3::Platform::macOS) && startsWith(path, MACOSX_DYLD_SHARED_CACHE_DIR) ) {
- // <rdar://48687550> pin cache file to SSD on fusion drives
- apfs_data_pin_location_t where = APFS_PIN_DATA_TO_MAIN;
- ::fsctl(pathTemplateSpace, APFSIOC_PIN_DATA, &where, 0);
- }
- // set final cache file size (may help defragment file)
- ::ftruncate(fd, size);
- };
- auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
- uint64_t writtenSize = pwrite(fd, src, size, dstOffset);
- return writtenSize == size;
- };
- // <rdar://problem/55370916> TOCTOU: verify path is still a realpath (not changed)
- char tempPath[MAXPATHLEN];
- if ( ::fcntl(fd, F_GETPATH, tempPath) == 0 ) {
- size_t tempPathLen = strlen(tempPath);
- if ( tempPathLen > 7 )
- tempPath[tempPathLen-7] = '\0'; // remove trailing -xxxxxx
- if ( path != tempPath ) {
- _diagnostics.error("output file path changed from: '%s' to: '%s'", path.c_str(), tempPath);
- ::close(fd);
- return;
- }
- }
- else {
- _diagnostics.error("unable to fcntl(fd, F_GETPATH) on output file");
- ::close(fd);
- return;
- }
- bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
- if ( fullyWritten ) {
- ::fchmod(fd, S_IRUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "r--r--r--"
- // <rdar://problem/55370916> TOCTOU: verify path is still a realpath (not changed)
- char resolvedPath[PATH_MAX];
- ::realpath(path.c_str(), resolvedPath);
- // Note: if the target cache file does not already exist, realpath() will return NULL, but still fill in the path buffer
- if ( path != resolvedPath ) {
- _diagnostics.error("output file path changed from: '%s' to: '%s'", path.c_str(), resolvedPath);
- return;
- }
- if ( ::rename(pathTemplateSpace, path.c_str()) == 0) {
- ::close(fd);
- return; // success
- }
- }
- else {
- _diagnostics.error("could not write file %s", pathTemplateSpace);
+ for (const Diagnostics& diag : diags) {
+ if ( diag.hasError() ) {
+ _diagnostics.error("%s", diag.errorMessage().c_str());
+ break;
}
- ::close(fd);
- ::unlink(pathTemplateSpace);
}
- else {
- _diagnostics.error("could not open file %s", pathTemplateSpace);
- }
-}
-
-void CacheBuilder::writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) {
- auto cacheSizeCallback = ^(uint64_t size) {
- buffer = (uint8_t*)malloc(size);
- bufferSize = size;
- };
- auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
- memcpy(buffer + dstOffset, src, size);
- return true;
- };
- bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
- assert(fullyWritten);
-}
-
-void CacheBuilder::writeMapFile(const std::string& path)
-{
- const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
- std::string mapContent = cache->mapFile();
- safeSave(mapContent.c_str(), mapContent.size(), path);
-}
-
-std::string CacheBuilder::getMapFileBuffer(const std::string& cacheDisposition) const
-{
- const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
- return cache->generateJSONMap(cacheDisposition.c_str());
-}
-
-
-void CacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) {
- for (const DylibInfo& dylibInfo : _sortedDylibs)
- callback(dylibInfo.runtimePath);
}
{
_pageCount = (unsigned)(rwRegionSize+_pageSize-1)/_pageSize;
_regionStart = (uint8_t*)rwRegionStart;
- _endStart = (uint8_t*)rwRegionStart + rwRegionSize;
+ _regionEnd = (uint8_t*)rwRegionStart + rwRegionSize;
_bitmap = (bool*)calloc(_pageCount*(_pageSize/4)*sizeof(bool), 1);
}
return;
uint8_t* p = (uint8_t*)loc;
assert(p >= _regionStart);
- assert(p < _endStart);
+ assert(p < _regionEnd);
_bitmap[(p-_regionStart)/4] = true;
}
return;
uint8_t* p = (uint8_t*)loc;
assert(p >= _regionStart);
- assert(p < _endStart);
+ assert(p < _regionEnd);
_bitmap[(p-_regionStart)/4] = false;
}
return true;
uint8_t* p = (uint8_t*)loc;
assert(p >= _regionStart);
- assert(p < _endStart);
+ assert(p < _regionEnd);
return _bitmap[(p-_regionStart)/4];
}
+void CacheBuilder::ASLR_Tracker::setHigh8(void* p, uint8_t high8)
+{
+ _high8Map[p] = high8;
+}
+
+void CacheBuilder::ASLR_Tracker::setAuthData(void* p, uint16_t diversity, bool hasAddrDiv, uint8_t key)
+{
+ _authDataMap[p] = {diversity, hasAddrDiv, key};
+}
+
+void CacheBuilder::ASLR_Tracker::setRebaseTarget32(void*p, uint32_t targetVMAddr)
+{
+ _rebaseTarget32[p] = targetVMAddr;
+}
+
+void CacheBuilder::ASLR_Tracker::setRebaseTarget64(void*p, uint64_t targetVMAddr)
+{
+ _rebaseTarget64[p] = targetVMAddr;
+}
+
+bool CacheBuilder::ASLR_Tracker::hasHigh8(void* p, uint8_t* highByte)
+{
+ auto pos = _high8Map.find(p);
+ if ( pos == _high8Map.end() )
+ return false;
+ *highByte = pos->second;
+ return true;
+}
+
+bool CacheBuilder::ASLR_Tracker::hasAuthData(void* p, uint16_t* diversity, bool* hasAddrDiv, uint8_t* key)
+{
+ auto pos = _authDataMap.find(p);
+ if ( pos == _authDataMap.end() )
+ return false;
+ *diversity = pos->second.diversity;
+ *hasAddrDiv = pos->second.addrDiv;
+ *key = pos->second.key;
+ return true;
+}
+
+bool CacheBuilder::ASLR_Tracker::hasRebaseTarget32(void* p, uint32_t* vmAddr)
+{
+ auto pos = _rebaseTarget32.find(p);
+ if ( pos == _rebaseTarget32.end() )
+ return false;
+ *vmAddr = pos->second;
+ return true;
+}
+
+bool CacheBuilder::ASLR_Tracker::hasRebaseTarget64(void* p, uint64_t* vmAddr)
+{
+ auto pos = _rebaseTarget64.find(p);
+ if ( pos == _rebaseTarget64.end() )
+ return false;
+ *vmAddr = pos->second;
+ return true;
+}
//////////////////////////// DylibTextCoalescer ////////////////////////////////////
InputFile* inputFile;
};
- void build(std::vector<InputFile>& inputFiles,
- std::vector<DyldSharedCache::FileAlias>& aliases);
- void build(const std::vector<LoadedMachO>& dylibs,
- const std::vector<LoadedMachO>& otherOsDylibsInput,
- const std::vector<LoadedMachO>& osExecutables,
- std::vector<DyldSharedCache::FileAlias>& aliases);
- void build(const std::vector<DyldSharedCache::MappedMachO>& dylibsToCache,
- const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibs,
- const std::vector<DyldSharedCache::MappedMachO>& osExecutables,
- std::vector<DyldSharedCache::FileAlias>& aliases);
- void writeFile(const std::string& path);
- void writeBuffer(uint8_t*& buffer, uint64_t& size);
- void writeMapFile(const std::string& path);
- std::string getMapFileBuffer(const std::string& cacheDisposition) const;
- void deleteBuffer();
std::string errorMessage();
- const std::set<std::string> warnings();
- const std::set<const dyld3::MachOAnalyzer*> evictions();
- const bool agileSignature();
- const std::string cdHashFirst();
- const std::string cdHashSecond();
- const std::string uuid() const;
-
- void forEachCacheDylib(void (^callback)(const std::string& path));
struct SegmentMappingInfo {
const void* srcSegment;
void setDataRegion(const void* rwRegionStart, size_t rwRegionSize);
void add(void* p);
+ void setHigh8(void* p, uint8_t high8);
+ void setAuthData(void* p, uint16_t diversity, bool hasAddrDiv, uint8_t key);
+ void setRebaseTarget32(void*p, uint32_t targetVMAddr);
+ void setRebaseTarget64(void*p, uint64_t targetVMAddr);
void remove(void* p);
bool has(void* p);
const bool* bitmap() { return _bitmap; }
unsigned dataPageCount() { return _pageCount; }
- void disable() { _enabled = false; };
+ void disable() { _enabled = false; };
+ bool hasHigh8(void* p, uint8_t* highByte);
+ bool hasAuthData(void* p, uint16_t* diversity, bool* hasAddrDiv, uint8_t* key);
+ bool hasRebaseTarget32(void* p, uint32_t* vmAddr);
+ bool hasRebaseTarget64(void* p, uint64_t* vmAddr);
private:
uint8_t* _regionStart = nullptr;
- uint8_t* _endStart = nullptr;
+ uint8_t* _regionEnd = nullptr;
bool* _bitmap = nullptr;
unsigned _pageCount = 0;
unsigned _pageSize = 4096;
bool _enabled = true;
+
+ struct AuthData {
+ uint16_t diversity;
+ bool addrDiv;
+ uint8_t key;
+ };
+ std::unordered_map<void*, uint8_t> _high8Map;
+ std::unordered_map<void*, AuthData> _authDataMap;
+ std::unordered_map<void*, uint32_t> _rebaseTarget32;
+ std::unordered_map<void*, uint64_t> _rebaseTarget64;
};
typedef std::map<uint64_t, std::set<void*>> LOH_Tracker;
+ static const uint64_t kRebaseTargetInSideTableArm64e = 0x7FFFFFFFFFFULL;
+ static const uint64_t kRebaseTargetInSideTableArm64 = 0xFFFFFFFFFULL;
+ static const uint64_t kRebaseTargetInSideTableGeneric32 = 0x3FFFFFFULL;
+
+
struct Region
{
uint8_t* buffer = nullptr;
uint64_t cacheFileOffset = 0;
};
-private:
+protected:
template <typename P>
friend class LinkeditOptimizer;
-
- struct ArchLayout
- {
- uint64_t sharedMemoryStart;
- uint64_t sharedMemorySize;
- uint64_t textAndDataMaxSize;
- uint64_t sharedRegionPadding;
- uint64_t pointerDeltaMask;
- const char* archName;
- uint16_t csPageSize;
- uint8_t sharedRegionAlignP2;
- uint8_t slideInfoBytesPerPage;
- bool sharedRegionsAreDiscontiguous;
- bool is64;
- bool useValueAdd;
- };
-
- static const ArchLayout _s_archLayout[];
- static const char* const _s_neverStubEliminateDylibs[];
- static const char* const _s_neverStubEliminateSymbols[];
struct UnmappedRegion
{
DylibTextCoalescer textCoalescer;
};
- void makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder);
- void processSelectorStrings(const std::vector<LoadedMachO>& executables);
- void parseCoalescableSegments();
- void assignSegmentAddresses();
-
- uint64_t cacheOverflowAmount();
- size_t evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs);
-
- void fipsSign();
- void codeSign();
- uint64_t pathHash(const char* path);
- void writeCacheHeader();
void copyRawSegments();
void adjustAllImagesForNewSegmentLocations();
- void writeSlideInfoV1();
- void writeSlideInfoV3(const bool bitmap[], unsigned dataPageCoun);
- uint16_t pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[]);
- void findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
- void addImageArray();
- void buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases);
- void addOtherImageArray(const std::vector<LoadedMachO>&, std::vector<const LoadedMachO*>& overflowDylibs);
- void addClosures(const std::vector<LoadedMachO>&);
- void markPaddingInaccessible();
-
- bool writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset));
-
- template <typename P> void writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount);
- template <typename P> bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info);
- template <typename P> void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info,
- std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
-
- template <typename P> void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount);
- template <typename P> bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info);
- template <typename P> void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info,
- std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
// implemented in AdjustDylibSegemnts.cpp
void adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const;
// implemented in OptimizerLinkedit.cpp
void optimizeLinkedit();
- // implemented in OptimizerObjC.cpp
- void optimizeObjC();
- uint32_t computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount);
- uint32_t computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount);
-
- // implemented in OptimizerBranches.cpp
- void optimizeAwayStubs();
-
- typedef std::unordered_map<std::string, const dyld3::MachOAnalyzer*> InstallNameToMA;
-
- typedef uint64_t CacheOffset;
-
const DyldSharedCache::CreateOptions& _options;
const dyld3::closure::FileSystem& _fileSystem;
Region _readExecuteRegion;
Region _readWriteRegion;
Region _readOnlyRegion;
UnmappedRegion _localSymbolsRegion;
- UnmappedRegion _codeSignatureRegion;
vm_address_t _fullAllocatedBuffer;
uint64_t _nonLinkEditReadOnlySize;
Diagnostics _diagnostics;
- std::set<const dyld3::MachOAnalyzer*> _evictions;
- const ArchLayout* _archLayout;
- uint32_t _aliasCount;
- uint64_t _slideInfoFileOffset;
- uint64_t _slideInfoBufferSizeAllocated;
- uint8_t* _objcReadOnlyBuffer;
- uint64_t _objcReadOnlyBufferSizeUsed;
- uint64_t _objcReadOnlyBufferSizeAllocated;
- uint8_t* _objcReadWriteBuffer;
- uint64_t _objcReadWriteBufferSizeAllocated;
uint64_t _allocatedBufferSize;
- CacheCoalescedText _coalescedText;
- uint64_t _selectorStringsFromExecutables;
std::vector<DylibInfo> _sortedDylibs;
- InstallNameToMA _installNameToCacheDylib;
- std::unordered_map<std::string, uint32_t> _dataDirtySegsOrder;
+ CacheCoalescedText _coalescedText;
+ uint32_t _sharedStringsPoolVmOffset = 0;
+ bool _is64 = false;
// Note this is mutable as the only parallel writes to it are done atomically to the bitmap
mutable ASLR_Tracker _aslrTracker;
- std::map<void*, std::string> _missingWeakImports;
mutable LOH_Tracker _lohTracker;
- const dyld3::closure::ImageArray* _imageArray;
- uint32_t _sharedStringsPoolVmOffset;
- uint8_t _cdHashFirst[20];
- uint8_t _cdHashSecond[20];
- std::unordered_map<const dyld3::MachOLoaded*, std::set<CacheOffset>> _dylibToItsExports;
- std::set<std::pair<const dyld3::MachOLoaded*, CacheOffset>> _dylibWeakExports;
- std::unordered_map<CacheOffset, std::vector<dyld_cache_patchable_location>> _exportsToUses;
- std::unordered_map<CacheOffset, std::string> _exportsToName;
};
#include <vector>
#include <unordered_map>
#include <unordered_set>
-#include "CacheBuilder.h"
+#include "SharedCacheBuilder.h"
#include "FileUtils.h"
#endif
const std::vector<MappedMachO>& otherOsDylibs,
const std::vector<MappedMachO>& osExecutables)
{
- CreateResults results;
- CacheBuilder cache(options, fileSystem);
+ CreateResults results;
+ SharedCacheBuilder cache(options, fileSystem);
if (!cache.errorMessage().empty()) {
results.errorMessage = cache.errorMessage();
return results;
}
}
- if (strcmp(segname, "__DATA") == 0)
- return getSection("__DATA_CONST", sectname);
+ if (strcmp(segname, "__DATA") == 0) {
+ if (const macho_section<P>* dataConst = getSection("__DATA_CONST", sectname))
+ return dataConst;
+ if (const macho_section<P>* dataDirty = getSection("__DATA_DIRTY", sectname))
+ return dataDirty;
+ }
return NULL;
}
+++ /dev/null
-/*
- * Copyright (c) 2017 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-#ifndef Manifest_h
-#define Manifest_h
-
-#include <map>
-#include <set>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <unordered_map>
-#include <unordered_set>
-
-#include <assert.h>
-#include <uuid/uuid.h>
-
-#import <Foundation/Foundation.h>
-
-#include "DyldSharedCache.h"
-#include "Diagnostics.h"
-#include "MachOAnalyzer.h"
-#include "ClosureFileSystemPhysical.h"
-
-extern std::string toolDir();
-
-namespace dyld3 {
-
-struct VIS_HIDDEN UUID {
- UUID() {}
- UUID(const UUID& other) { uuid_copy(_bytes, other._bytes); }
- UUID(const uuid_t other) { uuid_copy(&_bytes[0], other); }
- UUID(const dyld3::MachOAnalyzer* ml) { ml->getUuid(_bytes); }
- bool operator<(const UUID& other) const { return uuid_compare(_bytes, other._bytes) < 0; }
- bool operator==(const UUID& other) const { return uuid_compare(_bytes, other._bytes) == 0; }
- bool operator!=(const UUID& other) const { return !(*this == other); }
-
- size_t hash() const
- {
- size_t retval = 0;
- for (size_t i = 0; i < (16 / sizeof(size_t)); ++i) {
- retval ^= ((size_t*)(&_bytes[0]))[i];
- }
- return retval;
- }
- const unsigned char* get() const { return _bytes; };
-
-private:
- uuid_t _bytes;
-};
-
-struct BuildQueueEntry {
- DyldSharedCache::CreateOptions options;
- dyld3::closure::FileSystemPhysical fileSystem;
- std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
- std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles;
- std::vector<DyldSharedCache::MappedMachO> mainExecutables;
- std::string outputPath;
- std::set<std::string> configNames;
-};
-
-struct Manifest {
- struct UUIDInfo {
- const MachOAnalyzer* mh;
- uint64_t sliceFileOffset;
- std::size_t size;
- std::string runtimePath;
- std::string buildPath;
- std::string installName;
- std::string arch;
- UUID uuid;
- UUIDInfo(const MachOAnalyzer* M, std::size_t S, uint64_t SO, UUID U, std::string A, std::string RP, std::string BP, std::string IN)
- : mh(M), size(S), arch(A), uuid(U), runtimePath(RP), buildPath(BP), installName(IN), sliceFileOffset(SO) {}
- UUIDInfo() : UUIDInfo(nullptr, 0, 0, UUID(), "", "", "", "") {}
- };
-
- struct Project {
- std::vector<std::string> sources;
- };
-
-
- struct SegmentInfo {
- std::string name;
- uint64_t startAddr;
- uint64_t endAddr;
- };
-
- struct CacheInfo {
- std::vector<SegmentInfo> regions;
- std::string cdHash;
- };
-
- struct CacheImageInfo {
- bool included;
- std::string exclusionInfo;
- UUID uuid;
- std::string installname;
- std::vector<SegmentInfo> segments;
- CacheImageInfo(void)
- : included(true)
- {
- }
- };
-
- struct Results {
- std::string failure;
- std::map<UUID, CacheImageInfo> dylibs;
- std::map<UUID, CacheImageInfo> bundles;
- std::map<UUID, CacheImageInfo> executables;
-
- std::set<std::string> warnings;
- CacheInfo developmentCache;
- CacheInfo productionCache;
- CacheImageInfo& dylibForInstallname(const std::string& installname);
- void exclude(const dyld3::MachOAnalyzer* ml, const std::string& reason);
- void exclude(Manifest& manifest, const UUID& uuid, const std::string& reason);
- };
-
- struct Architecture {
- mutable Results results;
-
- bool operator==(const Architecture& O) const;
- bool operator!=(const Architecture& other) const;
- };
-
- struct Configuration {
- std::string platformName;
- std::string device;
- std::string disposition;
- std::string metabomTag;
- std::set<std::string> metabomTags;
- std::set<std::string> metabomExcludeTags;
- std::set<std::string> metabomRestrictTags;
- std::set<std::string> restrictedInstallnames;
- std::map<std::string, Architecture> architectures;
-
- bool operator==(const Configuration& O) const;
- bool operator!=(const Configuration& other) const;
- const Architecture& architecture(const std::string& architecture) const;
- void forEachArchitecture(std::function<void(const std::string& archName)> lambda) const;
- };
-
- const std::map<std::string, Project>& projects();
- const Configuration& configuration(const std::string& configuration) const;
- void forEachConfiguration(std::function<void(const std::string& configName)> lambda) const;
-
- void addProjectSource(const std::string& project, const std::string& source, bool first = false);
-
- const std::string projectPath(const std::string& projectName);
- const bool empty(void);
- const std::string dylibOrderFile() const;
- void setDylibOrderFile(const std::string& dylibOrderFile);
-
- const std::string dirtyDataOrderFile() const;
- void setDirtyDataOrderFile(const std::string& dirtyDataOrderFile);
-
- const std::string metabomFile() const;
- void setMetabomFile(const std::string& metabomFile);
-
- const Platform platform() const;
- void setPlatform(const Platform platform);
-
- const std::string& build() const;
- void setBuild(const std::string& build);
- const uint32_t version() const;
- void setVersion(const uint32_t manifestVersion);
- bool normalized;
-
- Manifest(Diagnostics& D, const std::string& path, bool populateIt = true);
- void populate(const std::set<std::string>& overlays);
-
- BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix,
- bool isLocallyBuiltCache, bool skipWrites, bool verbose);
-
- void write(const std::string& path);
- void writeJSON(const std::string& path);
- void canonicalize(void);
- void calculateClosure();
- const MachOAnalyzer* machOForUUID(const UUID& uuid) const;
- const std::string buildPathForUUID(const UUID& uuid);
- const std::string runtimePathForUUID(const UUID& uuid);
- const std::string& installNameForUUID(const UUID& uuid);
- DyldSharedCache::MappedMachO machoForPathAndArch(const std::string& path, const std::string& arch) const;
- void remove(const std::string& config, const std::string& arch);
- void runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function<void(const std::string configuration, const std::string architecture)> lambda);
- bool filterForConfig(const std::string& configName);
- std::set<std::string> resultsForConfiguration(const std::string& configName);
-
- // These are used by MRM to support having the Manifest give us a list of files/symlinks from the BOM but we use MRM for the actual cache generation
- void forEachMachO(std::string configuration, std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda);
-
- void forEachSymlink(std::string configuration, std::function<void(const std::string &fromPath, const std::string &toPath)> lambda);
-
-private:
- NSDictionary* _manifestDict;
- Diagnostics& _diags;
- std::map<UUID, UUIDInfo> _uuidMap;
- std::map<std::pair<std::string, std::string>, UUID> _installNameMap;
- std::vector<std::pair<std::string, std::string>> _symlinks;
- static dispatch_queue_t _identifierQueue;
- uint32_t _manifestVersion;
- std::string _build;
- std::string _dylibOrderFile;
- std::string _dirtyDataOrderFile;
- std::string _metabomFile;
- Platform _platform;
- std::map<std::string, Project> _projects;
- std::map<std::string, Configuration> _configurations;
- std::map<std::string, std::set<std::string>> _metabomTagMap;
- std::map<std::string, std::set<std::string>> _metabomSymlinkTagMap;
- std::map<std::string, std::set<std::string>> _metabomExcludeTagMap;
- std::map<std::string, std::set<std::string>> _metabomRestrictedTagMap;
-
- std::vector<DyldSharedCache::MappedMachO> dylibsForCache(const std::string& configuration, const std::string& architecture);
- std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles(const std::string& configuration, const std::string& architecture);
- std::vector<DyldSharedCache::MappedMachO> mainExecutables(const std::string& configuration, const std::string& architecture);
-
- const UUIDInfo& infoForUUID(const UUID& uuid) const;
- const UUIDInfo infoForInstallNameAndarch(const std::string& installName, const std::string arch) const;
- void insert(std::vector<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo);
- bool loadParser(const void* p, size_t sliceLength, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set<std::string>& architectures);
- bool loadParsers(const std::string& pathToMachO, const std::string& runtimePath, const std::set<std::string>& architectures);
- void dedupeDispositions();
- void calculateClosure(const std::string& configuration, const std::string& architecture);
- void canonicalizeDylib(const std::string& installname);
- template <typename P>
- void canonicalizeDylib(const std::string& installname, const uint8_t* p);
- void addImplicitAliases(void);
-};
-}
-
-namespace std {
-template <>
-struct hash<dyld3::UUID> {
- size_t operator()(const dyld3::UUID& x) const
- {
- return x.hash();
- }
-};
-}
-
-#endif /* Manifest_h */
+++ /dev/null
-/*
- * Copyright (c) 2017 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-extern "C" {
-#include <Bom/Bom.h>
-#include <Metabom/MBTypes.h>
-#include <Metabom/MBEntry.h>
-#include <Metabom/MBMetabom.h>
-#include <Metabom/MBIterator.h>
-};
-
-#include <algorithm>
-#include <CommonCrypto/CommonDigest.h>
-#include <CommonCrypto/CommonDigestSPI.h>
-#include <Foundation/Foundation.h>
-
-#include "MachOFileAbstraction.hpp"
-#include "FileAbstraction.hpp"
-#include "Trie.hpp"
-#include "FileUtils.h"
-#include "StringUtils.h"
-#include "MachOFile.h"
-#include "MachOAnalyzer.h"
-
-#include <mach-o/loader.h>
-#include <mach-o/fat.h>
-
-#include <array>
-#include <vector>
-
-#include "Manifest.h"
-#include "ClosureFileSystemPhysical.h"
-
-namespace {
-//FIXME this should be in a class
-static inline NSString* cppToObjStr(const std::string& str) { return [NSString stringWithUTF8String:str.c_str()]; }
-
-template <class Set1, class Set2>
-inline bool is_disjoint(const Set1& set1, const Set2& set2)
-{
- if (set1.empty() || set2.empty())
- return true;
-
- typename Set1::const_iterator it1 = set1.begin(), it1End = set1.end();
- typename Set2::const_iterator it2 = set2.begin(), it2End = set2.end();
-
- if (*it1 > *set2.rbegin() || *it2 > *set1.rbegin())
- return true;
-
- while (it1 != it1End && it2 != it2End) {
- if (*it1 == *it2)
- return false;
- if (*it1 < *it2) {
- it1++;
- } else {
- it2++;
- }
- }
-
- return true;
-}
-
-} /* Anonymous namespace */
-
-namespace dyld3 {
-void Manifest::Results::exclude(const dyld3::MachOAnalyzer* mh, const std::string& reason)
-{
- UUID dylibUUID(mh);
- dylibs[dylibUUID].uuid = dylibUUID;
- dylibs[dylibUUID].installname = mh->installName();
- dylibs[dylibUUID].included = false;
- dylibs[dylibUUID].exclusionInfo = reason;
-}
-
-void Manifest::Results::exclude(Manifest& manifest, const UUID& uuid, const std::string& reason)
-{
- const MachOAnalyzer* mh = manifest.machOForUUID(uuid);
- dylibs[uuid].uuid = uuid;
- dylibs[uuid].installname = mh->installName();
- dylibs[uuid].included = false;
- dylibs[uuid].exclusionInfo = reason;
-}
-
-Manifest::CacheImageInfo& Manifest::Results::dylibForInstallname(const std::string& installname)
-{
- auto i = find_if(dylibs.begin(), dylibs.end(), [&installname](std::pair<UUID, CacheImageInfo> d) { return d.second.installname == installname; });
- assert(i != dylibs.end());
- return i->second;
-}
-
-bool Manifest::Architecture::operator==(const Architecture& O) const
-{
- for (auto& dylib : results.dylibs) {
- if (dylib.second.included) {
- auto Odylib = O.results.dylibs.find(dylib.first);
- if (Odylib == O.results.dylibs.end()
- || Odylib->second.included == false
- || Odylib->second.uuid != dylib.second.uuid)
- return false;
- }
- }
-
- for (const auto& Odylib : O.results.dylibs) {
- if (Odylib.second.included) {
- auto dylib = results.dylibs.find(Odylib.first);
- if (dylib == results.dylibs.end()
- || dylib->second.included == false
- || dylib->second.uuid != Odylib.second.uuid)
- return false;
- }
- }
-
- for (auto& bundle : results.bundles) {
- if (bundle.second.included) {
- auto Obundle = O.results.bundles.find(bundle.first);
- if (Obundle == O.results.bundles.end()
- || Obundle->second.included == false
- || Obundle->second.uuid != bundle.second.uuid)
- return false;
- }
- }
-
- for (const auto& Obundle : O.results.bundles) {
- if (Obundle.second.included) {
- auto bundle = results.bundles.find(Obundle.first);
- if (bundle == results.bundles.end()
- || bundle->second.included == false
- || bundle->second.uuid != Obundle.second.uuid)
- return false;
- }
- }
-
- for (auto& executable : results.executables) {
- if (executable.second.included) {
- auto Oexecutable = O.results.executables.find(executable.first);
- if (Oexecutable == O.results.executables.end()
- || Oexecutable->second.included == false
- || Oexecutable->second.uuid != executable.second.uuid)
- return false;
- }
- }
-
- for (const auto& Oexecutable : O.results.executables) {
- if (Oexecutable.second.included) {
- auto executable = results.executables.find(Oexecutable.first);
- if (executable == results.executables.end()
- || executable->second.included == false
- || executable->second.uuid != Oexecutable.second.uuid)
- return false;
- }
- }
-
- return true;
-}
-
-bool Manifest::Configuration::operator==(const Configuration& O) const
-{
- return architectures == O.architectures;
-}
-
-bool Manifest::Configuration::operator!=(const Configuration& other) const { return !(*this == other); }
-
-const Manifest::Architecture& Manifest::Configuration::architecture(const std::string& architecture) const
-{
- assert(architectures.find(architecture) != architectures.end());
- return architectures.find(architecture)->second;
-}
-
-void Manifest::Configuration::forEachArchitecture(std::function<void(const std::string& archName)> lambda) const
-{
- for (const auto& architecutre : architectures) {
- lambda(architecutre.first);
- }
-}
-
-bool Manifest::Architecture::operator!=(const Architecture& other) const { return !(*this == other); }
-
-const std::map<std::string, Manifest::Project>& Manifest::projects()
-{
- return _projects;
-}
-
-const Manifest::Configuration& Manifest::configuration(const std::string& configuration) const
-{
- assert(_configurations.find(configuration) != _configurations.end());
- return _configurations.find(configuration)->second;
-}
-
-void Manifest::forEachConfiguration(std::function<void(const std::string& configName)> lambda) const
-{
- for (const auto& configuration : _configurations) {
- lambda(configuration.first);
- }
-}
-
-void Manifest::addProjectSource(const std::string& project, const std::string& source, bool first)
-{
- auto& sources = _projects[project].sources;
- if (std::find(sources.begin(), sources.end(), source) == sources.end()) {
- if (first) {
- sources.insert(sources.begin(), source);
- } else {
- sources.push_back(source);
- }
- }
-}
-
-const std::string Manifest::projectPath(const std::string& projectName)
-{
- auto project = _projects.find(projectName);
- if (project == _projects.end())
- return "";
- if (project->second.sources.size() == 0)
- return "";
- return project->second.sources[0];
-}
-
-const bool Manifest::empty(void)
-{
- for (const auto& configuration : _configurations) {
- if (configuration.second.architectures.size() != 0)
- return false;
- }
- return true;
-}
-
-const std::string Manifest::dylibOrderFile() const { return _dylibOrderFile; };
-void Manifest::setDylibOrderFile(const std::string& dylibOrderFile) { _dylibOrderFile = dylibOrderFile; };
-
-const std::string Manifest::dirtyDataOrderFile() const { return _dirtyDataOrderFile; };
-void Manifest::setDirtyDataOrderFile(const std::string& dirtyDataOrderFile) { _dirtyDataOrderFile = dirtyDataOrderFile; };
-
-const std::string Manifest::metabomFile() const { return _metabomFile; };
-void Manifest::setMetabomFile(const std::string& metabomFile) { _metabomFile = metabomFile; };
-
-const Platform Manifest::platform() const { return _platform; };
-void Manifest::setPlatform(const Platform platform) { _platform = platform; };
-
-const std::string& Manifest::build() const { return _build; };
-void Manifest::setBuild(const std::string& build) { _build = build; };
-const uint32_t Manifest::version() const { return _manifestVersion; };
-void Manifest::setVersion(const uint32_t manifestVersion) { _manifestVersion = manifestVersion; };
-
-BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs,
- const std::string& prefix, bool isLocallyBuiltCache, bool skipWrites, bool verbose)
-{
- dyld3::BuildQueueEntry retval;
-
- DyldSharedCache::CreateOptions options;
- options.outputFilePath = skipWrites ? "" : outputPath;
- options.outputMapFilePath = skipWrites ? "" : outputPath + ".map";
- options.archs = &dyld3::GradedArchs::forName(arch.c_str());
- options.platform = platform();
- options.excludeLocalSymbols = true;
- options.optimizeStubs = optimizeStubs;
- options.optimizeObjC = true;
- options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ?
- DyldSharedCache::Agile : DyldSharedCache::SHA256only;
- options.dylibsRemovedDuringMastering = true;
- options.inodesAreSameAsRuntime = false;
- options.cacheSupportsASLR = true;
- options.forSimulator = false;
- options.isLocallyBuiltCache = isLocallyBuiltCache;
- options.verbose = verbose;
- options.evictLeafDylibsOnOverflow = true;
- options.loggingPrefix = prefix;
- options.dylibOrdering = parseOrderFile(loadOrderFile(_dylibOrderFile));
- options.dirtyDataSegmentOrdering = parseOrderFile(loadOrderFile(_dirtyDataOrderFile));
-
- char rootsDir[PATH_MAX];
- realpath("./Root/", rootsDir);
-
- dyld3::BuildQueueEntry queueEntry;
- retval.configNames = configs;
- retval.options = options;
- retval.fileSystem = dyld3::closure::FileSystemPhysical(strdup(rootsDir));
- retval.outputPath = outputPath;
- retval.dylibsForCache = dylibsForCache(*configs.begin(), arch);
- retval.otherDylibsAndBundles = otherDylibsAndBundles(*configs.begin(), arch);
- retval.mainExecutables = mainExecutables(*configs.begin(), arch);
-
- return retval;
-}
-
-bool Manifest::loadParser(const void* p, size_t sliceLength, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set<std::string>& architectures)
-{
- assert(!_diags.hasError());
-
- const MachOFile* mf = reinterpret_cast<const MachOFile*>(p);
- const std::string archName = mf->archName();
- if ( archName == "unknown" ) {
- // Clear the error and punt
- _diags.verbose("Dylib located at '%s' has unknown architecture\n", runtimePath.c_str());
- return false;
- }
- if ( architectures.count(archName) == 0 )
- return false;
-
- const MachOAnalyzer* ma = reinterpret_cast<const MachOAnalyzer*>(p);
- if ( !ma->validMachOForArchAndPlatform(_diags, sliceLength, runtimePath.c_str(), dyld3::GradedArchs::forName(archName.c_str()), _platform) ) {
- // Clear the error and punt
- _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
- _diags.clearError();
- return false;
- }
-
- // if this file uses zero-fill expansion, then mapping whole file in one blob will not work
- // remapIfZeroFill() will remap the file
- closure::FileSystemPhysical fileSystem;
- closure::LoadedFileInfo info;
- info.fileContent = p;
- info.fileContentLen = sliceLength;
- info.sliceOffset = 0;
- info.sliceLen = sliceLength;
- info.inode = 0;
- info.mtime = 0;
- info.unload = nullptr;
- ma = ma->remapIfZeroFill(_diags, fileSystem, info);
-
- if (ma == nullptr) {
- _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
- _diags.clearError();
- return false;
- }
-
- uuid_t uuid;
- ma->getUuid(uuid);
-
- if ( ma->isDylib() ) {
- std::string installName = ma->installName();
- auto index = std::make_pair(installName, archName);
- auto i = _installNameMap.find(index);
-
- if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib"
- || installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" ) {
- // HACK to deal with device specific dylibs. These must not be inseted into the installNameMap
- _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
- } else if (i == _installNameMap.end()) {
- _installNameMap.insert(std::make_pair(index, uuid));
- _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
- if (installName[0] != '@' && installName != runtimePath) {
- _diags.warning("Dylib located at '%s' has installname '%s'", runtimePath.c_str(), installName.c_str());
- }
- } else {
- auto info = infoForUUID(i->second);
- _diags.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), runtimePath.c_str(), info.runtimePath.c_str());
-
- // This is the "Good" one, overwrite
- if (runtimePath == installName) {
- _uuidMap.erase(uuid);
- _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
- }
- }
- } else {
- _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, "")));
- }
- return true;
-}
-
-//FIXME: assert we have not errored first
-bool Manifest::loadParsers(const std::string& buildPath, const std::string& runtimePath, const std::set<std::string>& architectures)
-{
- __block bool retval = false;
- const void* p = (uint8_t*)(-1);
- struct stat stat_buf;
-
- std::tie(p, stat_buf) = fileCache.cacheLoad(_diags, buildPath);
-
- if (p == (uint8_t*)(-1)) {
- return false;
- }
-
- if ( const FatFile* fh = FatFile::isFatFile(p) ) {
- fh->forEachSlice(_diags, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
- if (loadParser(sliceStart, sliceSize, (uintptr_t)sliceStart-(uintptr_t)p, runtimePath, buildPath, architectures))
- retval = true;
- });
- } else {
- return loadParser(p, stat_buf.st_size, 0, runtimePath, buildPath, architectures);
- }
- return retval;
-}
-
-
-const Manifest::UUIDInfo& Manifest::infoForUUID(const UUID& uuid) const {
- auto i = _uuidMap.find(uuid);
- assert(i != _uuidMap.end());
- return i->second;
-}
-
-const Manifest::UUIDInfo Manifest::infoForInstallNameAndarch(const std::string& installName, const std::string arch) const {
- UUIDInfo retval;
- auto uuidI = _installNameMap.find(std::make_pair(installName, arch));
- if (uuidI == _installNameMap.end())
- return UUIDInfo();
-
- auto i = _uuidMap.find(uuidI->second);
- if (i == _uuidMap.end())
- return UUIDInfo();
- return i->second;
-}
-
-const MachOAnalyzer* Manifest::machOForUUID(const UUID& uuid) const {
- return infoForUUID(uuid).mh;
-}
-
-const std::string Manifest::buildPathForUUID(const UUID& uuid) {
- return infoForUUID(uuid).buildPath;
-}
-
-const std::string Manifest::runtimePathForUUID(const UUID& uuid) {
- return infoForUUID(uuid).runtimePath;
-}
-
-const std::string& Manifest::installNameForUUID(const UUID& uuid) {
- return infoForUUID(uuid).installName;
-}
-
-Manifest::Manifest(Diagnostics& D, const std::string& path, bool populateIt) :
- _diags(D)
-{
- _manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
- if (!_manifestDict)
- return;
- NSString* platStr = _manifestDict[@"platform"];
-
- if (platStr == nullptr)
- platStr = @"ios";
- std::string platformString = [platStr UTF8String];
- setMetabomFile([_manifestDict[@"metabomFile"] UTF8String]);
-
- if (platformString == "ios") {
- setPlatform(dyld3::Platform::iOS);
- } else if ( (platformString == "tvos") || (platformString == "atv") ) {
- setPlatform(dyld3::Platform::tvOS);
- } else if ( (platformString == "watchos") || (platformString == "watch") ) {
- setPlatform(dyld3::Platform::watchOS);
- } else if ( (platformString == "bridgeos") || (platformString == "bridge") ) {
- setPlatform(dyld3::Platform::bridgeOS);
- } else if ( (platformString == "macos") || (platformString == "osx") ) {
- setPlatform(dyld3::Platform::macOS);
- } else {
- //Fixme should we error?
- setPlatform(dyld3::Platform::iOS);
- }
-
- for (NSString* project in _manifestDict[@"projects"]) {
- for (NSString* source in _manifestDict[@"projects"][project]) {
- addProjectSource([project UTF8String], [source UTF8String]);
- }
- }
-
- for (NSString* configuration in _manifestDict[@"configurations"]) {
- std::string configStr = [configuration UTF8String];
- std::string configTag = [_manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
-
- if (_manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
- for (NSString* excludeTag in _manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
- _metabomExcludeTagMap[configStr].insert([excludeTag UTF8String]);
- _configurations[configStr].metabomExcludeTags.insert([excludeTag UTF8String]);
- }
- }
-
- if (_manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
- for (NSString* restrictTag in _manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
- _metabomRestrictedTagMap[configStr].insert([restrictTag UTF8String]);
- _configurations[configStr].metabomRestrictTags.insert([restrictTag UTF8String]);
- }
- }
-
- _configurations[configStr].metabomTag = configTag;
- _configurations[configStr].metabomTags.insert(configTag);
- _configurations[configStr].platformName =
- [_manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
-
- if (endsWith(configStr, "InternalOS")) {
- _configurations[configStr].disposition = "internal";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("InternalOS"));
- } else if (endsWith(configStr, "VendorOS")) {
- _configurations[configStr].disposition = "internal";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorOS"));
- } else if (endsWith(configStr, "VendorUIOS")) {
- _configurations[configStr].disposition = "internal";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorUIOS"));
- } else if (endsWith(configStr, "CarrierOS")) {
- _configurations[configStr].disposition = "internal";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("CarrierOS"));
- } else if (endsWith(configStr, "FactoryOS")) {
- _configurations[configStr].disposition = "internal";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("FactoryOS"));
- } else if (endsWith(configStr, "DesenseOS")) {
- _configurations[configStr].disposition = "internal";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DesenseOS"));
- } else if (endsWith(configStr, "MinosOS")) {
- _configurations[configStr].disposition = "minos";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
- } else if (endsWith(configStr, "DemoOS")) {
- _configurations[configStr].disposition = "demo";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DemoOS"));
- } else if (endsWith(configStr, "MinosOS")) {
- _configurations[configStr].disposition = "minos";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
- } else if (endsWith(configStr, "DeveloperOS")) {
- _configurations[configStr].disposition = "user";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DeveloperOS"));
- } else if (endsWith(configStr, "OS")) {
- _configurations[configStr].disposition = "user";
- _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("OS"));
- }
-
- for (NSString* architecture in _manifestDict[@"configurations"][configuration][@"architectures"]) {
- //HACK until B&I stops mastering armv7s
- if ([architecture isEqual:@"armv7s"]) break;
- _configurations[configStr].architectures[[architecture UTF8String]] = Architecture();
- }
- }
-
- setVersion([_manifestDict[@"manifest-version"] unsignedIntValue]);
- setBuild([_manifestDict[@"build"] UTF8String]);
- if (_manifestDict[@"dylibOrderFile"]) {
- setDylibOrderFile([_manifestDict[@"dylibOrderFile"] UTF8String]);
- }
- if (_manifestDict[@"dirtyDataOrderFile"]) {
- setDirtyDataOrderFile([_manifestDict[@"dirtyDataOrderFile"] UTF8String]);
- }
-
- if (populateIt)
- populate(std::set<std::string>());
-}
-
-// Perform the initialization that populateIt=false omitted.
-void Manifest::populate(const std::set<std::string>& overlays)
-{
- auto metabom = MBMetabomOpen(metabomFile().c_str(), false);
- auto metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
- MBEntry entry;
-
- // Collect every architecture from the configurations.
- std::set<std::string> architectures;
- for (auto& configuration : _configurations) {
- for (auto& arch : configuration.second.architectures) {
- architectures.insert(arch.first);
- }
- }
-
- auto filterPath = [](std::string& entryPath) {
- // Skip artifacts that happen to be in the build chain
- if ( startsWith(entryPath, "/Applications/Xcode.app") ) {
- return true;
- }
-
- // Skip variants we can't deal with
- if ( endsWith(entryPath, "_profile.dylib") || endsWith(entryPath, "_debug.dylib") || endsWith(entryPath, "_profile") || endsWith(entryPath, "_debug") || endsWith(entryPath, "/CoreADI") ) {
- return true;
- }
-
- // Skip images that are only used in InternalOS variants
- if ( startsWith(entryPath, "/AppleInternal/") || startsWith(entryPath, "/usr/local/") || startsWith(entryPath, "/Developer/")) {
- return true;
- }
-
- // Skip genCache generated dylibs
- if ( endsWith(entryPath, "/System/Library/Caches/com.apple.xpc/sdk.dylib") || endsWith(entryPath, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib")) {
- return true;
- }
-
- return false;
- };
-
- __block std::set<std::string> seenPaths;
-
- // FIXME error handling (NULL metabom)
-
- //First we iterate through the bom and build our objects
- while ((entry = MBIteratorNext(metabomEnumerator))) {
- BOMFSObject fsObject = MBEntryGetFSObject(entry);
- BOMFSObjType entryType = BOMFSObjectType(fsObject);
- std::string entryPath = BOMFSObjectPathName(fsObject);
- if (entryPath[0] == '.') {
- entryPath.erase(0, 1);
- }
-
- if (filterPath(entryPath))
- continue;
-
- MBTag tag;
- auto tagCount = MBEntryGetNumberOfProjectTags(entry);
- bool isObjectFile = (entryType == BOMFileType) && BOMFSObjectIsBinaryObject(fsObject);
- bool isSymlinkFile = entryType == BOMSymlinkType;
- if ( (isObjectFile || isSymlinkFile) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) {
- if (tagCount == 1) {
- MBEntryGetProjectTags(entry, &tag);
- } else {
- MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
- MBEntryGetProjectTags(entry, tags);
-
- //Sigh, we can have duplicate entries for the same tag, so build a set to work with
- std::set<std::string> tagStrs;
- std::map<std::string, MBTag> tagStrMap;
- for (auto i = 0; i < tagCount; ++i) {
- tagStrs.insert(MBMetabomGetProjectForTag(metabom, tags[i]));
- tagStrMap.insert(std::make_pair(MBMetabomGetProjectForTag(metabom, tags[i]), tags[i]));
- }
-
- if (tagStrs.size() > 1) {
- std::string projects;
- for (const auto& tagStr : tagStrs) {
- if (!projects.empty())
- projects += ", ";
-
- projects += "'" + tagStr + "'";
- }
- _diags.warning("Bom entry '%s' is claimed by multiple projects: %s, taking first entry", entryPath.c_str(), projects.c_str());
- }
- tag = tagStrMap[*tagStrs.begin()];
- free(tags);
- }
-
- std::string projectName = MBMetabomGetProjectForTag(metabom, tag);
- tagCount = MBEntryGetNumberOfPackageTags(entry);
- MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
- MBEntryGetPackageTags(entry, tags);
- std::set<std::string> tagStrs;
-
- for (auto i = 0; i < tagCount; ++i) {
- tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i]));
- }
-
- if (isObjectFile) {
- _metabomTagMap.insert(std::make_pair(entryPath, tagStrs));
-
- bool foundParser = false;
- for (const auto& overlay : overlays) {
- if (loadParsers(overlay + "/" + entryPath, entryPath, architectures)) {
- foundParser = true;
- _diags.verbose("Taking '%s' from overlay instead of dylib cache\n", entryPath.c_str());
- break;
- }
- if (_diags.hasError()) {
- _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
- _diags.clearError();
- }
- }
-
- if (!foundParser) {
- foundParser = loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures);
- if (_diags.hasError()) {
- _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
- _diags.clearError();
- }
- }
-
- if (foundParser)
- seenPaths.insert(entryPath);
- } else if (isSymlinkFile) {
- const char* target = BOMFSObjectSymlinkTarget(fsObject);
- _symlinks.push_back({ entryPath, target });
- _metabomSymlinkTagMap.insert(std::make_pair(entryPath, tagStrs));
- }
- }
- }
-
- MBIteratorFree(metabomEnumerator);
- MBMetabomFree(metabom);
-
- // Add files from the overlays
- for (const auto& overlay : overlays) {
- iterateDirectoryTree("", overlay, ^bool(const std::string &dirPath) {
- return false;
- }, ^(const std::string &path, const struct stat &statBuf) {
- std::string relativePath = path;
- if (relativePath.size() < overlay.size())
- return;
- relativePath = relativePath.substr(overlay.size());
- if (relativePath.front() != '/')
- return;
- if (filterPath(relativePath))
- return;
- // Filter out dylibs we've seen already
- if (seenPaths.count(relativePath))
- return;
- if (loadParsers(path, relativePath, architectures)) {
- seenPaths.insert(relativePath);
- // Get the tags from libSystem, assuming that will work.
- std::set<std::string> tagStrs;
- auto it = _metabomTagMap.find("/usr/lib/libSystem.B.dylib");
- if (it != _metabomTagMap.end())
- tagStrs = it->second;
- _metabomTagMap.insert(std::make_pair(relativePath, tagStrs));
- _diags.verbose("Taking '%s' from overlay instead of dylib cache\n", relativePath.c_str());
- return;
- }
- if (_diags.hasError()) {
- _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
- _diags.clearError();
- }
- },
- true, true);
- }
-
-}
-
-void Manifest::insert(std::vector<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo) {
- auto info = infoForUUID(imageInfo.uuid);
- auto runtimePath = info.runtimePath;
- mappedMachOs.emplace_back(runtimePath, info.mh, info.size, false, false, info.sliceFileOffset, 0, 0);
-}
-
-std::vector<DyldSharedCache::MappedMachO> Manifest::dylibsForCache(const std::string& configuration, const std::string& architecture)
-{
- std::vector<DyldSharedCache::MappedMachO> retval;
- const auto& dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
- for (const auto& dylib : dylibs) {
- if (dylib.second.included) {
- insert(retval, dylib.second);
- }
- }
- return retval;
-}
-
-std::vector<DyldSharedCache::MappedMachO> Manifest::otherDylibsAndBundles(const std::string& configuration, const std::string& architecture)
-{
- std::vector<DyldSharedCache::MappedMachO> retval;
- const auto& dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
- for (const auto& dylib : dylibs) {
- if (!dylib.second.included) {
- const UUIDInfo& info = infoForUUID(dylib.second.uuid);
- if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
- insert(retval, dylib.second);
- }
- }
-
- const auto& bundles = _configurations[configuration].architectures[architecture].results.bundles;
- for (const auto& bundle : bundles) {
- const UUIDInfo& info = infoForUUID(bundle.second.uuid);
- if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
- insert(retval, bundle.second);
- }
-
- return retval;
-}
-
-std::vector<DyldSharedCache::MappedMachO> Manifest::mainExecutables(const std::string& configuration, const std::string& architecture)
-{
- std::vector<DyldSharedCache::MappedMachO> retval;
- const auto& executables = _configurations[configuration].architectures[architecture].results.executables;
- for (const auto& executable : executables) {
- insert(retval, executable.second);
- }
-
- return retval;
-}
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wrange-loop-analysis"
-bool Manifest::filterForConfig(const std::string& configName)
-{
- for (const auto configuration : _configurations) {
- if (configName == configuration.first) {
- std::map<std::string, Configuration> filteredConfigs;
- filteredConfigs[configName] = configuration.second;
-
- _configurations = filteredConfigs;
-
- for (auto& arch : configuration.second.architectures) {
- arch.second.results = Manifest::Results();
- }
- return true;
- }
- }
- return false;
-}
-#pragma clang diagnostic pop
-
-std::set<std::string> Manifest::resultsForConfiguration(const std::string& configName) {
- std::set<std::string> results;
- NSDictionary* configurationResults = _manifestDict[@"results"][[NSString stringWithUTF8String:configName.c_str()]];
- for (NSString* arch in configurationResults) {
- NSDictionary* dylibs = configurationResults[arch][@"dylibs"];
- for (NSString* dylib in dylibs) {
- NSDictionary* dylibDict = dylibs[dylib];
- if ([dylibDict[@"included"] boolValue])
- results.insert([dylib UTF8String]);
- }
- }
- return results;
-}
-
-void Manifest::dedupeDispositions(void) {
- // Since this is all hacky and inference based for now only do it for iOS until XBS
- // is reved to give us real info. All the other platforms are way smaller anyway.
- if (_platform != Platform::iOS)
- return;
-
- std::map<std::pair<std::string, std::string>, std::set<std::string>> dispositionSets;
-
- for (const auto& configuration : _configurations) {
- dispositionSets[std::make_pair(configuration.second.device, configuration.second.disposition)].insert(configuration.first);
- }
-
- for (const auto& dSet : dispositionSets) {
- for (const auto &c1 : dSet.second) {
- for (const auto &c2 : dSet.second) {
- _configurations[c1].metabomTags.insert(_configurations[c2].metabomTag);
- }
- }
- }
-}
-
-void Manifest::calculateClosure()
-{
- auto closureSemaphore = dispatch_semaphore_create(32);
- auto closureGroup = dispatch_group_create();
- auto closureQueue = dispatch_queue_create("com.apple.dyld.cache.closure", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
-
- dedupeDispositions();
- for (auto& config : _configurations) {
- for (auto& arch : config.second.architectures) {
- dispatch_semaphore_wait(closureSemaphore, DISPATCH_TIME_FOREVER);
- dispatch_group_async(closureGroup, closureQueue, [&] {
- calculateClosure(config.first, arch.first);
- dispatch_semaphore_signal(closureSemaphore);
- });
- }
- }
-
- dispatch_group_wait(closureGroup, DISPATCH_TIME_FOREVER);
-}
-
-void Manifest::remove(const std::string& config, const std::string& arch)
-{
- if (_configurations.count(config))
- _configurations[config].architectures.erase(arch);
-}
-
-
-void Manifest::calculateClosure(const std::string& configuration, const std::string& architecture)
-{
- __block auto& configManifest = _configurations[configuration];
- __block auto& archManifest = _configurations[configuration].architectures[architecture];
- __block std::set<UUID> newUuids;
- std::set<UUID> processedUuids;
- std::set<UUID> cachedUUIDs;
-
- // Seed anchors
- for (auto& uuidInfo : _uuidMap) {
- auto info = uuidInfo.second;
- if (info.arch != architecture) {
- continue;
- }
-
- auto i = _metabomTagMap.find(info.runtimePath);
- assert(i != _metabomTagMap.end());
- auto tags = i->second;
- if (!is_disjoint(tags, configManifest.metabomTags)) {
- newUuids.insert(info.uuid);
- }
- }
-
- __block std::set<UUID> removedUUIDs;
-
- // Pull in all dependencies
- while (!newUuids.empty()) {
- std::set<UUID> uuidsToProcess = newUuids;
- newUuids.clear();
-
- for (const auto& uuid : uuidsToProcess) {
- if (processedUuids.count(uuid) > 0) {
- continue;
- }
- processedUuids.insert(uuid);
-
- const MachOAnalyzer* mh = machOForUUID(uuid);
- auto runtimePath = runtimePathForUUID(uuid);
- assert(mh != nullptr);
-
- mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
- if (i != _installNameMap.end())
- newUuids.insert(i->second);
- });
-
- if (mh->isDylib()) {
- // Add the dylib to the results
- if (archManifest.results.dylibs.count(uuid) == 0 ) {
- archManifest.results.dylibs[uuid].uuid = uuid;
- archManifest.results.dylibs[uuid].installname = mh->installName();
- }
-
- // HACK to insert device specific dylib closures into all caches
- if ( (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0)
- || (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0) ) {
- archManifest.results.exclude(mh, "Device specific dylib");
- continue;
- }
-
- __block std::set<std::string> reasons;
- if (mh->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* reason) { reasons.insert(reason); })) {
- auto i = _metabomTagMap.find(runtimePath);
- assert(i != _metabomTagMap.end());
- auto restrictions = _metabomRestrictedTagMap.find(configuration);
- if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
- archManifest.results.exclude(mh, "Dylib '" + runtimePath + "' removed due to explict restriction");
- }
-
- // It can be placed in the cache, grab its dependents and queue them for inclusion
- cachedUUIDs.insert(uuid);
- } else {
- // It can't be placed in the cache, print out the reasons why
- std::string reasonString = "Rejected from cached dylibs: " + runtimePath + " " + architecture + " (\"";
- for (auto i = reasons.begin(); i != reasons.end(); ++i) {
- reasonString += *i;
- if (i != --reasons.end()) {
- reasonString += "\", \"";
- }
- }
- reasonString += "\")";
- archManifest.results.exclude(mh, reasonString);
- removedUUIDs.insert(uuid);
- }
- } else if (mh->isBundle()) {
- if (archManifest.results.bundles.count(uuid) == 0) {
- archManifest.results.bundles[uuid].uuid = uuid;
- }
- } else if (mh->isMainExecutable()) {
- //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends
- if (runtimePath == "/sbin/launchd"
- || runtimePath == "/usr/local/sbin/launchd.debug"
- || runtimePath == "/usr/local/sbin/launchd.development"
- || runtimePath == "/usr/libexec/installd") {
- continue;
- }
- if (archManifest.results.executables.count(uuid) == 0) {
- archManifest.results.executables[uuid].uuid = uuid;
- }
- }
- }
- }
-
- __block bool doAgain = true;
-
- //Trim out dylibs that are missing dependencies
- while ( doAgain ) {
- doAgain = false;
- for (const auto& uuid : cachedUUIDs) {
- __block std::set<std::string> badDependencies;
- const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
- mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- if (isWeak)
- return;
-
- auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
- if (i == _installNameMap.end() || removedUUIDs.count(i->second)) {
- removedUUIDs.insert(uuid);
- badDependencies.insert(loadPath);
- doAgain = true;
- }
-
- if (badDependencies.size()) {
- std::string reasonString = "Rejected from cached dylibs: " + std::string(mh->installName()) + " " + architecture + " (\"";
- for (auto i = badDependencies.begin(); i != badDependencies.end(); ++i) {
- reasonString += *i;
- if (i != --badDependencies.end()) {
- reasonString += "\", \"";
- }
- }
- reasonString += "\")";
- archManifest.results.exclude(mh, reasonString);
- }
- });
- }
-
- for (const auto& removedUUID : removedUUIDs) {
- cachedUUIDs.erase(removedUUID);
- }
- }
-
- //Trim out excluded leaf dylibs
- __block std::set<std::string> linkedDylibs;
-
- for(const auto& uuid : cachedUUIDs) {
- const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
- mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
- linkedDylibs.insert(loadPath);
- });
- }
-
- for(const auto& uuid : cachedUUIDs) {
- auto info = infoForUUID(uuid);
- auto i = _metabomTagMap.find(info.runtimePath);
- assert(i != _metabomTagMap.end());
- auto exclusions = _metabomExcludeTagMap.find(configuration);
- if (exclusions == _metabomExcludeTagMap.end() || is_disjoint(exclusions->second, i->second))
- continue;
-
- if (linkedDylibs.count(info.installName) != 0)
- continue;
-
- archManifest.results.exclude(*this, info.uuid, "Dylib '" + info.runtimePath + "' excluded leaf node");
- }
-}
-
-void Manifest::writeJSON(const std::string& path) {
- NSMutableDictionary* jsonDict = [[NSMutableDictionary alloc] init];
- for (auto& configuration : _configurations) {
- jsonDict[cppToObjStr(configuration.first)] = [[NSMutableDictionary alloc] init];
-
- for (auto& arch : configuration.second.architectures) {
- NSMutableOrderedSet* includedDylibsSet = [[NSMutableOrderedSet alloc] init];
- NSMutableOrderedSet* executablesSet = [[NSMutableOrderedSet alloc] init];
- NSMutableOrderedSet* otherSet = [[NSMutableOrderedSet alloc] init];
- for (auto& dylib : arch.second.results.dylibs) {
- NSString *runtimePath = cppToObjStr(runtimePathForUUID(dylib.second.uuid));
- if (dylib.second.included) {
- [includedDylibsSet addObject:runtimePath];
- } else {
- [otherSet addObject:runtimePath];
- }
- }
-
- for (auto& executable : arch.second.results.executables) {
- NSString *runtimePath = cppToObjStr(runtimePathForUUID(executable.second.uuid));
- [executablesSet addObject:runtimePath];
- }
-
- for (auto& bundle : arch.second.results.bundles) {
- NSString *runtimePath = cppToObjStr(runtimePathForUUID(bundle.second.uuid));
- [otherSet addObject:runtimePath];
- }
-
- [includedDylibsSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
- return [obj1 compare:obj2];
- }];
-
- [executablesSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
- return [obj1 compare:obj2];
- }];
-
- [otherSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
- return [obj1 compare:obj2];
- }];
-
- jsonDict[cppToObjStr(configuration.first)][cppToObjStr(arch.first)] = @{ @"cachedDylibs" : [includedDylibsSet array], @"mainExecutables" : [executablesSet array], @"other" : [otherSet array]};;
- }
- }
-
- NSError* error = nil;
- NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:0x0 error:&error];
- (void)[jsonData writeToFile:cppToObjStr(path) atomically:YES];
-}
-
-void Manifest::write(const std::string& path)
-{
- if (path.empty())
- return;
-
- NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
- NSMutableDictionary* projectDict = [[NSMutableDictionary alloc] init];
- NSMutableDictionary* configurationsDict = [[NSMutableDictionary alloc] init];
- NSMutableDictionary* resultsDict = [[NSMutableDictionary alloc] init];
-
- cacheDict[@"manifest-version"] = @(version());
- cacheDict[@"build"] = cppToObjStr(build());
- cacheDict[@"dylibOrderFile"] = cppToObjStr(dylibOrderFile());
- cacheDict[@"dirtyDataOrderFile"] = cppToObjStr(dirtyDataOrderFile());
- cacheDict[@"metabomFile"] = cppToObjStr(metabomFile());
-
- cacheDict[@"projects"] = projectDict;
- cacheDict[@"results"] = resultsDict;
- cacheDict[@"configurations"] = configurationsDict;
-
- for (const auto& project : projects()) {
- NSMutableArray* sources = [[NSMutableArray alloc] init];
-
- for (const auto& source : project.second.sources) {
- [sources addObject:cppToObjStr(source)];
- }
-
- projectDict[cppToObjStr(project.first)] = sources;
- }
-
- for (auto& configuration : _configurations) {
- NSMutableArray* archArray = [[NSMutableArray alloc] init];
- for (auto& arch : configuration.second.architectures) {
- [archArray addObject:cppToObjStr(arch.first)];
- }
-
- NSMutableArray* excludeTags = [[NSMutableArray alloc] init];
- for (const auto& excludeTag : configuration.second.metabomExcludeTags) {
- [excludeTags addObject:cppToObjStr(excludeTag)];
- }
-
- configurationsDict[cppToObjStr(configuration.first)] = @{
- @"platformName" : cppToObjStr(configuration.second.platformName),
- @"metabomTag" : cppToObjStr(configuration.second.metabomTag),
- @"metabomExcludeTags" : excludeTags,
- @"architectures" : archArray
- };
- }
-
- for (auto& configuration : _configurations) {
- NSMutableDictionary* archResultsDict = [[NSMutableDictionary alloc] init];
- for (auto& arch : configuration.second.architectures) {
- NSMutableDictionary* dylibsDict = [[NSMutableDictionary alloc] init];
- NSMutableArray* warningsArray = [[NSMutableArray alloc] init];
- NSMutableDictionary* devRegionsDict = [[NSMutableDictionary alloc] init];
- NSMutableDictionary* prodRegionsDict = [[NSMutableDictionary alloc] init];
- NSString* prodCDHash = cppToObjStr(arch.second.results.productionCache.cdHash);
- NSString* devCDHash = cppToObjStr(arch.second.results.developmentCache.cdHash);
-
- for (auto& dylib : arch.second.results.dylibs) {
- NSMutableDictionary* dylibDict = [[NSMutableDictionary alloc] init];
- if (dylib.second.included) {
- dylibDict[@"included"] = @YES;
- } else {
- dylibDict[@"included"] = @NO;
- dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo);
- }
- dylibsDict[cppToObjStr(dylib.second.installname)] = dylibDict;
- }
-
- for (auto& warning : arch.second.results.warnings) {
- [warningsArray addObject:cppToObjStr(warning)];
- }
-
- BOOL built = arch.second.results.failure.empty();
- archResultsDict[cppToObjStr(arch.first)] = @{
- @"dylibs" : dylibsDict,
- @"built" : @(built),
- @"failure" : cppToObjStr(arch.second.results.failure),
- @"productionCache" : @{ @"cdhash" : prodCDHash, @"regions" : prodRegionsDict },
- @"developmentCache" : @{ @"cdhash" : devCDHash, @"regions" : devRegionsDict },
- @"warnings" : warningsArray
- };
- }
- resultsDict[cppToObjStr(configuration.first)] = archResultsDict;
- }
-
- switch (platform()) {
- case Platform::iOS:
- cacheDict[@"platform"] = @"ios";
- break;
- case Platform::tvOS:
- cacheDict[@"platform"] = @"tvos";
- break;
- case Platform::watchOS:
- cacheDict[@"platform"] = @"watchos";
- break;
- case Platform::bridgeOS:
- cacheDict[@"platform"] = @"bridgeos";
- break;
- case Platform::macOS:
- cacheDict[@"platform"] = @"macos";
- break;
- case Platform::unknown:
- case Platform::iOSMac:
- case Platform::iOS_simulator:
- case Platform::tvOS_simulator:
- case Platform::watchOS_simulator:
- case Platform::driverKit:
- cacheDict[@"platform"] = @"unknown";
- break;
- }
-
- NSError* error = nil;
- NSData* outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
- format:NSPropertyListBinaryFormat_v1_0
- options:0
- error:&error];
- (void)[outData writeToFile:cppToObjStr(path) atomically:YES];
-}
-
-
-void Manifest::forEachMachO(std::string configuration,
- std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda) {
- for (auto& uuidInfo : _uuidMap) {
- auto i = _metabomTagMap.find(uuidInfo.second.runtimePath);
- assert(i != _metabomTagMap.end());
- auto restrictions = _metabomRestrictedTagMap.find(configuration);
- if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
- continue;
- }
- auto& configManifest = _configurations[configuration];
- auto exclusions = _metabomExcludeTagMap.find(configuration);
- bool isExcluded = (exclusions != _metabomExcludeTagMap.end()) && !is_disjoint(exclusions->second, i->second);
- bool isAnchor = !is_disjoint(i->second, configManifest.metabomTags);
- bool shouldBeExcludedIfLeaf = isExcluded || !isAnchor;
- lambda(uuidInfo.second.buildPath, uuidInfo.second.runtimePath, uuidInfo.second.arch, shouldBeExcludedIfLeaf);
- }
-}
-
-
-void Manifest::forEachSymlink(std::string configuration,
- std::function<void(const std::string &fromPath, const std::string &toPath)> lambda) {
- for (const auto& symlink : _symlinks) {
- auto i = _metabomSymlinkTagMap.find(symlink.first);
- assert(i != _metabomSymlinkTagMap.end());
- auto restrictions = _metabomRestrictedTagMap.find(configuration);
- if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
- continue;
- }
- lambda(symlink.first, symlink.second);
- }
-}
-
-} //namespace dyld3
if (instanceProperties) aslrTracker.add(&instanceProperties);
if (extendedMethodTypes) aslrTracker.add(&extendedMethodTypes);
if (demangledName) aslrTracker.add(&demangledName);
+ if (classProperties) aslrTracker.add(&classProperties);
}
};
#include "MachOAnalyzer.h"
#include "Diagnostics.h"
#include "DyldSharedCache.h"
-#include "CacheBuilder.h"
+#include "SharedCacheBuilder.h"
static const bool verbose = false;
macho_header<P>* _mh;
int64_t _cacheSlide = 0;
uint64_t _cacheUnslideAddr = 0;
- bool _chainedFixups = false;
uint32_t _linkeditSize = 0;
uint64_t _linkeditAddr = 0;
const uint8_t* _linkeditBias = nullptr;
{
_cacheSlide = (long)cache - cache->unslidLoadAddress();
_cacheUnslideAddr = cache->unslidLoadAddress();
-#if SUPPORT_ARCH_arm64e
- _chainedFixups = (strcmp(cache->archName(), "arm64e") == 0);
-#else
- _chainedFixups = false;
-#endif
const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
const uint32_t cmd_count = mh->ncmds();
macho_segment_command<P>* segCmd;
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",
delete op;
}
-void CacheBuilder::optimizeAwayStubs()
+void SharedCacheBuilder::optimizeAwayStubs()
{
std::unordered_map<uint64_t, uint64_t> targetAddrToOptStubAddr;
-template <typename P>
-class AcceleratorTables {
-public:
- AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers);
-
- uint32_t totalSize() const;
- void copyTo(uint8_t* buffer);
-
-private:
- typedef typename P::E E;
-
- struct NodeChain;
-
- struct DepNode {
- std::vector<DepNode*> _dependents;
- unsigned _depth;
- const char* _installName;
-
- DepNode() : _depth(0), _installName(nullptr) { }
- void computeDepth();
- static void verifyUnreachable(DepNode* target, NodeChain& chain, Diagnostics& diag, std::unordered_set<DepNode*>& visitedNodes, const std::vector<DepNode*>& from);
- };
-
- struct NodeChain {
- NodeChain* prev;
- DepNode* node;
- };
-
- std::unordered_map<macho_header<P>*, DepNode> _depDAG;
- std::vector<dyld_cache_image_info_extra> _extraInfo;
- std::vector<uint8_t> _trieBytes;
- std::vector<uint16_t> _reExportArray;
- std::vector<uint16_t> _dependencyArray;
- std::vector<uint16_t> _bottomUpArray;
- std::vector<dyld_cache_accelerator_initializer> _initializers;
- std::vector<dyld_cache_accelerator_dof> _dofSections;
- std::vector<dyld_cache_range_entry> _rangeTable;
- std::unordered_map<macho_header<P>*, uint32_t> _machHeaderToImageIndex;
- std::unordered_map<std::string, macho_header<P>*> _dylibPathToMachHeader;
- std::unordered_map<macho_header<P>*, LinkeditOptimizer<P>*> _machHeaderToOptimizer;
- dyld_cache_accelerator_info _acceleratorInfoHeader;
-};
-
-
-template <typename P>
-void AcceleratorTables<P>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables<P>::DepNode* target, struct AcceleratorTables<P>::NodeChain& chain, Diagnostics& diag,
- std::unordered_set<DepNode*>& visitedNodes, const std::vector<AcceleratorTables<P>::DepNode*>& from) {
- for (DepNode* node : from) {
- bool foundCycle = (node == target);
- for (NodeChain* c = &chain; c->prev != nullptr; c = c->prev) {
- if ( c->node == target ) {
- foundCycle = true;
- break;
- }
- }
- if ( foundCycle ) {
- NodeChain* chp = &chain;
- std::string msg = std::string("found cycle for ") + target->_installName;
- while (chp != nullptr) {
- msg = msg + "\n " + chp->node->_installName;
- chp = chp->prev;
- }
- diag.warning("%s", msg.c_str());
- return;
- }
-
- if ( visitedNodes.count(node) )
- continue;
- visitedNodes.insert(node);
- NodeChain nextChain;
- nextChain.prev = &chain;
- nextChain.node = node;
- verifyUnreachable(target, nextChain, diag, visitedNodes, node->_dependents);
- }
-}
-
-const uint16_t kBranchIslandDylibIndex = 0x7FFF;
-
-template <typename P>
-AcceleratorTables<P>::AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers)
-{
- // build table mapping tables to map between mach_header, index, and optimizer
- for ( LinkeditOptimizer<P>* op : optimizers ) {
- _machHeaderToOptimizer[op->machHeader()] = op;
- }
- const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)cache + cache->header.mappingOffset);
- uint64_t cacheStartAddress = mappings[0].address;
- const dyld_cache_image_info* images = (dyld_cache_image_info*)((uint8_t*)cache + cache->header.imagesOffset);
- for (unsigned i=0; i < cache->header.imagesCount; ++i) {
- uint64_t segCacheFileOffset = images[i].address - cacheStartAddress;
- macho_header<P>* mhMapped = (macho_header<P>*)((uint8_t*)cache+segCacheFileOffset);
- const char* path = (char*)cache + images[i].pathFileOffset;
- _dylibPathToMachHeader[path] = mhMapped;
- // don't add alias entries (path offset in pool near start of cache) to header->index map
- if ( images[i].pathFileOffset > segCacheFileOffset )
- _machHeaderToImageIndex[mhMapped] = i;
- }
-
-
- // build DAG of image dependencies
- for (LinkeditOptimizer<P>* op : optimizers) {
- _depDAG[op->machHeader()]._installName = op->installName();
- }
- for (LinkeditOptimizer<P>* op : optimizers) {
- DepNode& node = _depDAG[op->machHeader()];
- for (const char* depPath : op->getDownwardDependents()) {
- macho_header<P>* depMH = _dylibPathToMachHeader[depPath];
- if ( depMH != nullptr ) {
- DepNode* depNode = &_depDAG[depMH];
- node._dependents.push_back(depNode);
- }
- }
- }
-
- // check for cycles in DAG
- for (auto& entry : _depDAG) {
- DepNode* node = &entry.second;
- NodeChain chain;
- chain.prev = nullptr;
- chain.node = node;
- std::unordered_set<DepNode*> visitedNodes;
- DepNode::verifyUnreachable(node, chain, diag, visitedNodes, node->_dependents);
- }
-
- // compute depth for each DAG node
- for (auto& entry : _depDAG) {
- entry.second.computeDepth();
- }
-
- // build sorted (bottom up) list of images
- std::vector<macho_header<P>*> sortedMachHeaders;
- sortedMachHeaders.reserve(optimizers.size());
- for (LinkeditOptimizer<P>* op : optimizers) {
- if ( strcmp(op->installName(), "dyld_shared_cache_branch_islands") != 0 )
- sortedMachHeaders.push_back(op->machHeader());
- else
- _machHeaderToImageIndex[op->machHeader()] = kBranchIslandDylibIndex;
- }
- std::sort(sortedMachHeaders.begin(), sortedMachHeaders.end(),
- [&](macho_header<P>* lmh, macho_header<P>* rmh) -> bool {
- if ( _depDAG[lmh]._depth != _depDAG[rmh]._depth )
- return (_depDAG[lmh]._depth < _depDAG[rmh]._depth);
- else
- return (lmh < rmh);
- });
-
- // build zeroed array of extra infos
- dyld_cache_image_info_extra emptyExtra;
- emptyExtra.exportsTrieAddr = 0;
- emptyExtra.weakBindingsAddr = 0;
- emptyExtra.exportsTrieSize = 0;
- emptyExtra.weakBindingsSize = 0;
- emptyExtra.dependentsStartArrayIndex = 0;
- emptyExtra.reExportsStartArrayIndex = 0;
- _extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra);
-
- //for ( macho_header<P>* mh : sortedMachHeaders ) {
- // fprintf(stderr, "depth: %3d mh: %p path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName());
- //}
-
- // build dependency table
- _dependencyArray.push_back(0xFFFF); // reserve 0 slot to be "no-dependencies"
- for (macho_header<P>* mh : sortedMachHeaders) {
- LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
- unsigned index = _machHeaderToImageIndex[mh];
- auto depPaths = op->getAllDependents();
- if ( depPaths.empty() ) {
- _extraInfo[index].dependentsStartArrayIndex = 0;
- }
- else {
- _extraInfo[index].dependentsStartArrayIndex = (uint32_t)_dependencyArray.size();
- auto downPaths = op->getDownwardDependents();
- for (const char* depPath : depPaths) {
- macho_header<P>* depMH = _dylibPathToMachHeader[depPath];
- uint16_t depIndex = _machHeaderToImageIndex[depMH];
- if ( std::find(downPaths.begin(), downPaths.end(), depPath) == downPaths.end())
- depIndex |= 0x8000;
- _dependencyArray.push_back(depIndex);
- }
- _dependencyArray.push_back(0xFFFF); // mark end of list
- }
- }
-
- // build re-exports table
- _reExportArray.push_back(0xFFFF); // reserve 0 slot to be "no-re-exports"
- for (macho_header<P>* mh : sortedMachHeaders) {
- LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
- unsigned index = _machHeaderToImageIndex[mh];
- auto reExPaths = op->getReExportPaths();
- if ( reExPaths.empty() ) {
- _extraInfo[index].reExportsStartArrayIndex = 0;
- }
- else {
- _extraInfo[index].reExportsStartArrayIndex = (uint32_t)_reExportArray.size();
- for (const char* reExPath : reExPaths) {
- macho_header<P>* reExMH = _dylibPathToMachHeader[reExPath];
- uint32_t reExIndex = _machHeaderToImageIndex[reExMH];
- _reExportArray.push_back(reExIndex);
- }
- _reExportArray.push_back(0xFFFF); // mark end of list
- }
- }
-
- // build ordered list of initializers
- for (macho_header<P>* mh : sortedMachHeaders) {
- LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
- unsigned index = _machHeaderToImageIndex[mh];
- _bottomUpArray.push_back(index);
- for (uint64_t initializer : op->initializerAddresses()) {
- //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
- dyld_cache_accelerator_initializer entry;
- entry.functionOffset = (uint32_t)(initializer-cacheStartAddress);
- entry.imageIndex = _machHeaderToImageIndex[mh];
- _initializers.push_back(entry);
- }
- }
-
- // build ordered list of DOF sections
- for (macho_header<P>* mh : sortedMachHeaders) {
- LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
- assert(op != NULL);
- unsigned imageIndex = _machHeaderToImageIndex[mh];
- for (auto& sect : op->dofSections()) {
- //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
- dyld_cache_accelerator_dof entry;
- entry.sectionAddress = sect->addr();
- entry.sectionSize = (uint32_t)sect->size();
- entry.imageIndex = imageIndex;
- _dofSections.push_back(entry);
- }
- }
-
-
- // register exports trie and weak binding info in each dylib with image extra info
- for (macho_header<P>* mh : sortedMachHeaders) {
- LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
- unsigned index = _machHeaderToImageIndex[mh];
- _extraInfo[index].exportsTrieAddr = op->exportsTrieLinkEditOffset() + linkeditStartAddr;
- _extraInfo[index].exportsTrieSize = op->exportsTrieLinkEditSize();
- _extraInfo[index].weakBindingsAddr = op->weakBindingLinkEditOffset() + linkeditStartAddr;
- _extraInfo[index].weakBindingsSize = op->weakBindingLinkEditSize();
- }
-
- // record location of __DATA/__dyld section in libdyld.dylib
- macho_header<P>* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"];
- LinkeditOptimizer<P>* libdyldOp = _machHeaderToOptimizer[libdyldMH];
- uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress();
-
- // build range table for fast address->image lookups
- for (macho_header<P>* mh : sortedMachHeaders) {
- LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
- unsigned imageIndex = _machHeaderToImageIndex[mh];
- for (const macho_segment_command<P>* segCmd : op->segCmds()) {
- if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
- continue;
- dyld_cache_range_entry entry;
- entry.startAddress = segCmd->vmaddr();
- entry.size = (uint32_t)segCmd->vmsize();
- entry.imageIndex = imageIndex;
- _rangeTable.push_back(entry);
- }
- }
- std::sort(_rangeTable.begin(), _rangeTable.end(),
- [&](const dyld_cache_range_entry& lRange, const dyld_cache_range_entry& rRange) -> bool {
- return (lRange.startAddress < rRange.startAddress);
- });
-
- // build trie that maps install names to image index
- std::vector<DylibIndexTrie::Entry> dylibEntrys;
- for (auto &x : _dylibPathToMachHeader) {
- const std::string& path = x.first;
- unsigned index = _machHeaderToImageIndex[x.second];
- dylibEntrys.push_back(DylibIndexTrie::Entry(path, DylibIndex(index)));
- }
- DylibIndexTrie dylibsTrie(dylibEntrys);
- dylibsTrie.emit(_trieBytes);
- while ( (_trieBytes.size() % 4) != 0 )
- _trieBytes.push_back(0);
-
- // fill out header
- _acceleratorInfoHeader.version = 1;
- _acceleratorInfoHeader.imageExtrasCount = (uint32_t)_extraInfo.size();
- _acceleratorInfoHeader.imagesExtrasOffset = ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra);
- _acceleratorInfoHeader.bottomUpListOffset = _acceleratorInfoHeader.imagesExtrasOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(dyld_cache_image_info_extra);
- _acceleratorInfoHeader.dylibTrieOffset = _acceleratorInfoHeader.bottomUpListOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(uint16_t);
- _acceleratorInfoHeader.dylibTrieSize = (uint32_t)_trieBytes.size();
- _acceleratorInfoHeader.initializersOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.dylibTrieOffset + _acceleratorInfoHeader.dylibTrieSize, dyld_cache_accelerator_initializer);
- _acceleratorInfoHeader.initializersCount = (uint32_t)_initializers.size();
- _acceleratorInfoHeader.dofSectionsOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.initializersOffset + _acceleratorInfoHeader.initializersCount*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer);
- _acceleratorInfoHeader.dofSectionsCount = (uint32_t)_dofSections.size();
- _acceleratorInfoHeader.reExportListOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.dofSectionsOffset + _acceleratorInfoHeader.dofSectionsCount*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof);
- _acceleratorInfoHeader.reExportCount = (uint32_t)_reExportArray.size();
- _acceleratorInfoHeader.depListOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.reExportListOffset + _acceleratorInfoHeader.reExportCount*sizeof(uint16_t), uint16_t);
- _acceleratorInfoHeader.depListCount = (uint32_t)_dependencyArray.size();
- _acceleratorInfoHeader.rangeTableOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.depListOffset + _acceleratorInfoHeader.depListCount*sizeof(uint16_t), dyld_cache_range_entry);
- _acceleratorInfoHeader.rangeTableCount = (uint32_t)_rangeTable.size();
- _acceleratorInfoHeader.dyldSectionAddr = dyldSectionAddr;
-}
-
-
-template <typename P>
-void AcceleratorTables<P>::DepNode::computeDepth()
-{
- if ( _depth != 0 )
- return;
- _depth = 1;
- for (DepNode* node : _dependents) {
- node->computeDepth();
- if ( node->_depth >= _depth )
- _depth = node->_depth + 1;
- }
-}
-
-template <typename P>
-uint32_t AcceleratorTables<P>::totalSize() const
-{
- return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset + _acceleratorInfoHeader.rangeTableCount*sizeof(dyld_cache_range_entry), 14);
-}
-
-template <typename P>
-void AcceleratorTables<P>::copyTo(uint8_t* buffer)
-{
- memcpy(buffer, &_acceleratorInfoHeader, sizeof(dyld_cache_accelerator_info));
- memcpy(&buffer[_acceleratorInfoHeader.imagesExtrasOffset], &_extraInfo[0], _extraInfo.size()*sizeof(dyld_cache_image_info_extra));
- memcpy(&buffer[_acceleratorInfoHeader.bottomUpListOffset], &_bottomUpArray[0], _bottomUpArray.size()*sizeof(uint16_t));
- memcpy(&buffer[_acceleratorInfoHeader.initializersOffset], &_initializers[0], _initializers.size()*sizeof(dyld_cache_accelerator_initializer));
- memcpy(&buffer[_acceleratorInfoHeader.reExportListOffset], &_reExportArray[0], _reExportArray.size()*sizeof(uint16_t));
- memcpy(&buffer[_acceleratorInfoHeader.dofSectionsOffset], &_dofSections[0], _dofSections.size()*sizeof(dyld_cache_accelerator_dof));
- memcpy(&buffer[_acceleratorInfoHeader.depListOffset], &_dependencyArray[0], _dependencyArray.size()*sizeof(uint16_t));
- memcpy(&buffer[_acceleratorInfoHeader.rangeTableOffset], &_rangeTable[0], _rangeTable.size()*sizeof(dyld_cache_range_entry));
- memcpy(&buffer[_acceleratorInfoHeader.dylibTrieOffset], &_trieBytes[0], _trieBytes.size());
-}
-
-
-
template <typename P>
LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diag)
: _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diag)
}
}
break;
+ case LC_DYLD_CHAINED_FIXUPS:
case LC_SEGMENT_SPLIT_INFO:
remove = true;
break;
::free(newLinkEdit);
builder._readOnlyRegion.sizeInUse = builder._nonLinkEditReadOnlySize + newLinkeditAlignedSize;
- // If making cache for customers, add extra accelerator tables for dyld
- DyldSharedCache* cacheHeader = (DyldSharedCache*)builder._readExecuteRegion.buffer;
- if ( builder._options.optimizeStubs ) {
- uint64_t addrWhereAccTablesWillBe = builder._readOnlyRegion.unslidLoadAddress+builder._readOnlyRegion.sizeInUse;
- uint64_t addrWhereMergedLinkWillStart = builder._readOnlyRegion.unslidLoadAddress+builder._nonLinkEditReadOnlySize;
- AcceleratorTables<P> tables(cacheHeader, addrWhereMergedLinkWillStart, builder._diagnostics, optimizers);
- uint32_t tablesSize = tables.totalSize();
- if ( tablesSize < (builder._readOnlyRegion.bufferSize - builder._readOnlyRegion.sizeInUse) ) {
- tables.copyTo(builder._readOnlyRegion.buffer+builder._readOnlyRegion.sizeInUse);
- cacheHeader->header.accelerateInfoAddr = addrWhereAccTablesWillBe;
- cacheHeader->header.accelerateInfoSize = tablesSize;
- builder._readOnlyRegion.sizeInUse += align(tablesSize, 14);
- builder._diagnostics.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize/(1024*1024));
- }
- else {
- builder._diagnostics.warning("not enough room to add dyld accelerator tables");
- }
- }
-
// overwrite end of un-opt linkedits to create a new unmapped region for local symbols
if ( builder._options.excludeLocalSymbols ) {
const uint32_t entriesOffset = sizeof(dyld_cache_local_symbols_info);
// copy string pool
localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
// update cache header
+ DyldSharedCache* cacheHeader = (DyldSharedCache*)builder._readExecuteRegion.buffer;
cacheHeader->header.localSymbolsSize = localsBufferSize;
// return buffer of local symbols, caller to free() it
builder._localSymbolsRegion.buffer = (uint8_t*)localsBuffer;
void CacheBuilder::optimizeLinkedit()
{
- if ( _archLayout->is64 ) {
+ if ( _is64 ) {
return LinkeditOptimizer<Pointer64<LittleEndian>>::optimizeLinkedit(*this);
}
else {
#include "DyldSharedCache.h"
#include "Diagnostics.h"
-#include "CacheBuilder.h"
+#include "SharedCacheBuilder.h"
#include "FileAbstraction.hpp"
#include "MachOFileAbstraction.hpp"
#include "MachOLoaded.h"
_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;
}
uint64_t _slide;
uint64_t _cacheUnslideAddr;
uint8_t* _cacheStart;
- bool _chainedFixups;
};
} // anon namespace
-void CacheBuilder::optimizeObjC()
+void SharedCacheBuilder::optimizeObjC()
{
uint32_t objcRwFileOffset = (uint32_t)((_objcReadWriteBuffer - _readWriteRegion.buffer) + _readWriteRegion.cacheFileOffset);
if ( _archLayout->is64 )
// 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);
}
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * Copyright (c) 2014 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <mach/mach_time.h>
+#include <mach/shared_region.h>
+#include <apfs/apfs_fsctl.h>
+
+#include <CommonCrypto/CommonHMAC.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+
+#include "ClosureBuilder.h"
+#include "Closure.h"
+#include "ClosureFileSystemNull.h"
+#include "CodeSigningTypes.h"
+#include "MachOFileAbstraction.hpp"
+#include "SharedCacheBuilder.h"
+
+#include "FileUtils.h"
+#include "StringUtils.h"
+#include "Trie.hpp"
+
+#if __has_include("dyld_cache_config.h")
+ #include "dyld_cache_config.h"
+#else
+ #define ARM_SHARED_REGION_START 0x1A000000ULL
+ #define ARM_SHARED_REGION_SIZE 0x26000000ULL
+ #define ARM64_SHARED_REGION_START 0x180000000ULL
+ #define ARM64_SHARED_REGION_SIZE 0x100000000ULL
+#endif
+
+#ifndef ARM64_32_SHARED_REGION_START
+ #define ARM64_32_SHARED_REGION_START 0x1A000000ULL
+ #define ARM64_32_SHARED_REGION_SIZE 0x26000000ULL
+#endif
+
+#if ARM_SHARED_REGION_SIZE > 0x26000000ULL
+ #define ARMV7K_CHAIN_BITS 0xC0000000
+ #define ARMV7K_MAX 0x0
+#else
+ #define ARMV7K_CHAIN_BITS 0xE0000000
+ #define ARMV7K_MAX 0x20000000
+#endif
+
+const SharedCacheBuilder::ArchLayout SharedCacheBuilder::_s_archLayout[] = {
+ { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x0, 0x40000000, 0x00FFFF0000000000, "x86_64", CS_PAGE_SIZE_4K, 12, 2, true, true, true },
+ { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x0, 0x40000000, 0x00FFFF0000000000, "x86_64h", CS_PAGE_SIZE_4K, 12, 2, true, true, true },
+ { SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386, 0x0, 0x00200000, 0x0, "i386", CS_PAGE_SIZE_4K, 12, 0, false, false, true },
+ { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x0, 0x02000000, 0x00FFFF0000000000, "arm64", CS_PAGE_SIZE_4K, 14, 2, false, true, false },
+#if SUPPORT_ARCH_arm64e
+ { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x0, 0x02000000, 0x00FFFF0000000000, "arm64e", CS_PAGE_SIZE_16K, 14, 2, false, true, false },
+#endif
+#if SUPPORT_ARCH_arm64_32
+ { ARM64_32_SHARED_REGION_START, ARM64_32_SHARED_REGION_SIZE,0x0, 0x02000000, 0xC0000000, "arm64_32", CS_PAGE_SIZE_16K, 14, 6, false, false, true },
+#endif
+ { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x0, 0x02000000, 0xE0000000, "armv7s", CS_PAGE_SIZE_4K, 14, 4, false, false, true },
+ { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, ARMV7K_MAX, 0x00400000, ARMV7K_CHAIN_BITS, "armv7k", CS_PAGE_SIZE_4K, 14, 4, false, false, true },
+ { 0x40000000, 0x40000000, 0x0, 0x02000000, 0x0, "sim-x86", CS_PAGE_SIZE_4K, 14, 0, false, false, true }
+};
+
+
+// These are dylibs that may be interposed, so stubs calling into them should never be bypassed
+const char* const SharedCacheBuilder::_s_neverStubEliminateDylibs[] = {
+ "/usr/lib/system/libdispatch.dylib",
+ nullptr
+};
+
+// These are functions that are interposed by Instruments.app or ASan
+const char* const SharedCacheBuilder::_s_neverStubEliminateSymbols[] = {
+ "___bzero",
+ "___cxa_atexit",
+ "___cxa_throw",
+ "__longjmp",
+ "__objc_autoreleasePoolPop",
+ "_accept",
+ "_access",
+ "_asctime",
+ "_asctime_r",
+ "_asprintf",
+ "_atoi",
+ "_atol",
+ "_atoll",
+ "_calloc",
+ "_chmod",
+ "_chown",
+ "_close",
+ "_confstr",
+ "_ctime",
+ "_ctime_r",
+ "_dispatch_after",
+ "_dispatch_after_f",
+ "_dispatch_async",
+ "_dispatch_async_f",
+ "_dispatch_barrier_async_f",
+ "_dispatch_group_async",
+ "_dispatch_group_async_f",
+ "_dispatch_source_set_cancel_handler",
+ "_dispatch_source_set_event_handler",
+ "_dispatch_sync_f",
+ "_dlclose",
+ "_dlopen",
+ "_dup",
+ "_dup2",
+ "_endgrent",
+ "_endpwent",
+ "_ether_aton",
+ "_ether_hostton",
+ "_ether_line",
+ "_ether_ntoa",
+ "_ether_ntohost",
+ "_fchmod",
+ "_fchown",
+ "_fclose",
+ "_fdopen",
+ "_fflush",
+ "_fopen",
+ "_fork",
+ "_fprintf",
+ "_free",
+ "_freopen",
+ "_frexp",
+ "_frexpf",
+ "_frexpl",
+ "_fscanf",
+ "_fstat",
+ "_fstatfs",
+ "_fstatfs64",
+ "_fsync",
+ "_ftime",
+ "_getaddrinfo",
+ "_getattrlist",
+ "_getcwd",
+ "_getgrent",
+ "_getgrgid",
+ "_getgrgid_r",
+ "_getgrnam",
+ "_getgrnam_r",
+ "_getgroups",
+ "_gethostbyaddr",
+ "_gethostbyname",
+ "_gethostbyname2",
+ "_gethostent",
+ "_getifaddrs",
+ "_getitimer",
+ "_getnameinfo",
+ "_getpass",
+ "_getpeername",
+ "_getpwent",
+ "_getpwnam",
+ "_getpwnam_r",
+ "_getpwuid",
+ "_getpwuid_r",
+ "_getsockname",
+ "_getsockopt",
+ "_gmtime",
+ "_gmtime_r",
+ "_if_indextoname",
+ "_if_nametoindex",
+ "_index",
+ "_inet_aton",
+ "_inet_ntop",
+ "_inet_pton",
+ "_initgroups",
+ "_ioctl",
+ "_lchown",
+ "_lgamma",
+ "_lgammaf",
+ "_lgammal",
+ "_link",
+ "_listxattr",
+ "_localtime",
+ "_localtime_r",
+ "_longjmp",
+ "_lseek",
+ "_lstat",
+ "_malloc",
+ "_malloc_create_zone",
+ "_malloc_default_purgeable_zone",
+ "_malloc_default_zone",
+ "_malloc_good_size",
+ "_malloc_make_nonpurgeable",
+ "_malloc_make_purgeable",
+ "_malloc_set_zone_name",
+ "_mbsnrtowcs",
+ "_mbsrtowcs",
+ "_mbstowcs",
+ "_memchr",
+ "_memcmp",
+ "_memcpy",
+ "_memmove",
+ "_memset",
+ "_mktime",
+ "_mlock",
+ "_mlockall",
+ "_modf",
+ "_modff",
+ "_modfl",
+ "_munlock",
+ "_munlockall",
+ "_objc_autoreleasePoolPop",
+ "_objc_setProperty",
+ "_objc_setProperty_atomic",
+ "_objc_setProperty_atomic_copy",
+ "_objc_setProperty_nonatomic",
+ "_objc_setProperty_nonatomic_copy",
+ "_objc_storeStrong",
+ "_open",
+ "_opendir",
+ "_poll",
+ "_posix_memalign",
+ "_pread",
+ "_printf",
+ "_pthread_attr_getdetachstate",
+ "_pthread_attr_getguardsize",
+ "_pthread_attr_getinheritsched",
+ "_pthread_attr_getschedparam",
+ "_pthread_attr_getschedpolicy",
+ "_pthread_attr_getscope",
+ "_pthread_attr_getstack",
+ "_pthread_attr_getstacksize",
+ "_pthread_condattr_getpshared",
+ "_pthread_create",
+ "_pthread_getschedparam",
+ "_pthread_join",
+ "_pthread_mutex_lock",
+ "_pthread_mutex_unlock",
+ "_pthread_mutexattr_getprioceiling",
+ "_pthread_mutexattr_getprotocol",
+ "_pthread_mutexattr_getpshared",
+ "_pthread_mutexattr_gettype",
+ "_pthread_rwlockattr_getpshared",
+ "_pwrite",
+ "_rand_r",
+ "_read",
+ "_readdir",
+ "_readdir_r",
+ "_readv",
+ "_readv$UNIX2003",
+ "_realloc",
+ "_realpath",
+ "_recv",
+ "_recvfrom",
+ "_recvmsg",
+ "_remquo",
+ "_remquof",
+ "_remquol",
+ "_scanf",
+ "_send",
+ "_sendmsg",
+ "_sendto",
+ "_setattrlist",
+ "_setgrent",
+ "_setitimer",
+ "_setlocale",
+ "_setpwent",
+ "_shm_open",
+ "_shm_unlink",
+ "_sigaction",
+ "_sigemptyset",
+ "_sigfillset",
+ "_siglongjmp",
+ "_signal",
+ "_sigpending",
+ "_sigprocmask",
+ "_sigwait",
+ "_snprintf",
+ "_sprintf",
+ "_sscanf",
+ "_stat",
+ "_statfs",
+ "_statfs64",
+ "_strcasecmp",
+ "_strcat",
+ "_strchr",
+ "_strcmp",
+ "_strcpy",
+ "_strdup",
+ "_strerror",
+ "_strerror_r",
+ "_strlen",
+ "_strncasecmp",
+ "_strncat",
+ "_strncmp",
+ "_strncpy",
+ "_strptime",
+ "_strtoimax",
+ "_strtol",
+ "_strtoll",
+ "_strtoumax",
+ "_tempnam",
+ "_time",
+ "_times",
+ "_tmpnam",
+ "_tsearch",
+ "_unlink",
+ "_valloc",
+ "_vasprintf",
+ "_vfprintf",
+ "_vfscanf",
+ "_vprintf",
+ "_vscanf",
+ "_vsnprintf",
+ "_vsprintf",
+ "_vsscanf",
+ "_wait",
+ "_wait$UNIX2003",
+ "_wait3",
+ "_wait4",
+ "_waitid",
+ "_waitid$UNIX2003",
+ "_waitpid",
+ "_waitpid$UNIX2003",
+ "_wcslen",
+ "_wcsnrtombs",
+ "_wcsrtombs",
+ "_wcstombs",
+ "_wordexp",
+ "_write",
+ "_writev",
+ "_writev$UNIX2003",
+ // <rdar://problem/22050956> always use stubs for C++ symbols that can be overridden
+ "__ZdaPv",
+ "__ZdlPv",
+ "__Znam",
+ "__Znwm",
+
+ nullptr
+};
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+ return (uint32_t)(abstime/1000/1000);
+}
+
+// Handles building a list of input files to the SharedCacheBuilder itself.
+class CacheInputBuilder {
+public:
+ CacheInputBuilder(const dyld3::closure::FileSystem& fileSystem,
+ const dyld3::GradedArchs& archs, dyld3::Platform reqPlatform)
+ : fileSystem(fileSystem), reqArchs(archs), reqPlatform(reqPlatform) { }
+
+ // Loads and maps any MachOs in the given list of files.
+ void loadMachOs(std::vector<CacheBuilder::InputFile>& inputFiles,
+ std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
+ std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
+ std::vector<CacheBuilder::LoadedMachO>& executables,
+ std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles) {
+
+ std::map<std::string, uint64_t> dylibInstallNameMap;
+ for (CacheBuilder::InputFile& inputFile : inputFiles) {
+ char realerPath[MAXPATHLEN];
+ dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchs, reqPlatform, realerPath);
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+ if (ma == nullptr) {
+ couldNotLoadFiles.emplace_back((CacheBuilder::LoadedMachO){ DyldSharedCache::MappedMachO(), loadedFileInfo, &inputFile });
+ continue;
+ }
+
+ DyldSharedCache::MappedMachO mappedFile(inputFile.path, ma, loadedFileInfo.sliceLen, false, false,
+ loadedFileInfo.sliceOffset, loadedFileInfo.mtime, loadedFileInfo.inode);
+
+ // The file can be loaded with the given slice, but we may still want to exlude it from the cache.
+ if (ma->isDylib()) {
+ std::string installName = ma->installName();
+
+ // Let the platform exclude the file before we do anything else.
+ if (platformExcludesInstallName(installName)) {
+ inputFile.diag.verbose("Platform excluded file\n");
+ fileSystem.unloadFile(loadedFileInfo);
+ continue;
+ }
+
+ if (!ma->canBePlacedInDyldCache(inputFile.path, ^(const char* msg) {
+ inputFile.diag.warning("Dylib located at '%s' cannot be placed in cache because: %s", inputFile.path, msg);
+ })) {
+ // TODO: Add exclusion lists here?
+ // Probably not as we already applied the dylib exclusion list.
+ if (!ma->canHavePrecomputedDlopenClosure(inputFile.path, ^(const char* msg) {
+ inputFile.diag.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile.path, msg);
+ }) ) {
+ fileSystem.unloadFile(loadedFileInfo);
+ continue;
+ }
+ otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+ continue;
+ }
+
+ // Otherwise see if we have another file with this install name
+ auto iteratorAndInserted = dylibInstallNameMap.insert(std::make_pair(installName, dylibsToCache.size()));
+ if (iteratorAndInserted.second) {
+ // We inserted the dylib so we haven't seen another with this name.
+ if (installName[0] != '@' && installName != inputFile.path) {
+ inputFile.diag.warning("Dylib located at '%s' has installname '%s'", inputFile.path, installName.c_str());
+ }
+
+ dylibsToCache.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+ } else {
+ // We didn't insert this one so we've seen it before.
+ CacheBuilder::LoadedMachO& previousLoadedMachO = dylibsToCache[iteratorAndInserted.first->second];
+ inputFile.diag.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), inputFile.path, previousLoadedMachO.mappedFile.runtimePath.c_str());
+
+ // This is the "Good" one, overwrite
+ if (inputFile.path == installName) {
+ // Unload the old one
+ fileSystem.unloadFile(previousLoadedMachO.loadedFileInfo);
+
+ // And replace with this one.
+ previousLoadedMachO.mappedFile = mappedFile;
+ previousLoadedMachO.loadedFileInfo = loadedFileInfo;
+ }
+ }
+ } else if (ma->isBundle()) {
+ // TODO: Add exclusion lists here?
+ if (!ma->canHavePrecomputedDlopenClosure(inputFile.path, ^(const char* msg) {
+ inputFile.diag.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile.path, msg);
+ }) ) {
+ fileSystem.unloadFile(loadedFileInfo);
+ continue;
+ }
+ otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+ } else if (ma->isDynamicExecutable()) {
+ if (platformExcludesExecutablePath_macOS(inputFile.path)) {
+ inputFile.diag.verbose("Platform excluded file\n");
+ fileSystem.unloadFile(loadedFileInfo);
+ continue;
+ }
+ executables.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+ } else {
+ inputFile.diag.verbose("Unsupported mach file type\n");
+ fileSystem.unloadFile(loadedFileInfo);
+ }
+ }
+ }
+
+private:
+
+
+
+ static bool platformExcludesInstallName_macOS(const std::string& installName) {
+ return false;
+ }
+
+ static bool platformExcludesInstallName_iOS(const std::string& installName) {
+ if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib" )
+ return true;
+ if ( installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" )
+ return true;
+ return false;
+ }
+
+ static bool platformExcludesInstallName_tvOS(const std::string& installName) {
+ return platformExcludesInstallName_iOS(installName);
+ }
+
+ static bool platformExcludesInstallName_watchOS(const std::string& installName) {
+ return platformExcludesInstallName_iOS(installName);
+ }
+
+ static bool platformExcludesInstallName_bridgeOS(const std::string& installName) {
+ return platformExcludesInstallName_iOS(installName);
+ }
+
+ // Returns true if the current platform requires that this install name be excluded from the shared cache
+ // Note that this overrides any exclusion from anywhere else.
+ bool platformExcludesInstallName(const std::string& installName) {
+ switch (reqPlatform) {
+ case dyld3::Platform::unknown:
+ return false;
+ case dyld3::Platform::macOS:
+ return platformExcludesInstallName_macOS(installName);
+ case dyld3::Platform::iOS:
+ return platformExcludesInstallName_iOS(installName);
+ case dyld3::Platform::tvOS:
+ return platformExcludesInstallName_tvOS(installName);
+ case dyld3::Platform::watchOS:
+ return platformExcludesInstallName_watchOS(installName);
+ case dyld3::Platform::bridgeOS:
+ return platformExcludesInstallName_bridgeOS(installName);
+ case dyld3::Platform::iOSMac:
+ return false;
+ case dyld3::Platform::iOS_simulator:
+ return false;
+ case dyld3::Platform::tvOS_simulator:
+ return false;
+ case dyld3::Platform::watchOS_simulator:
+ return false;
+ case dyld3::Platform::driverKit:
+ return false;
+ }
+ }
+
+
+
+
+ static bool platformExcludesExecutablePath_macOS(const std::string& path) {
+ return false;
+ }
+
+ static bool platformExcludesExecutablePath_iOS(const std::string& path) {
+ //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends
+ if (path == "/sbin/launchd"
+ || path == "/usr/local/sbin/launchd.debug"
+ || path == "/usr/local/sbin/launchd.development"
+ || path == "/usr/libexec/installd") {
+ return true;
+ }
+ return false;
+ }
+
+ static bool platformExcludesExecutablePath_tvOS(const std::string& path) {
+ return platformExcludesExecutablePath_iOS(path);
+ }
+
+ static bool platformExcludesExecutablePath_watchOS(const std::string& path) {
+ return platformExcludesExecutablePath_iOS(path);
+ }
+
+ static bool platformExcludesExecutablePath_bridgeOS(const std::string& path) {
+ return platformExcludesExecutablePath_iOS(path);
+ }
+
+ // Returns true if the current platform requires that this path be excluded from the shared cache
+ // Note that this overrides any exclusion from anywhere else.
+ bool platformExcludesExecutablePath(const std::string& path) {
+ switch (reqPlatform) {
+ case dyld3::Platform::unknown:
+ return false;
+ case dyld3::Platform::macOS:
+ return platformExcludesExecutablePath_macOS(path);
+ case dyld3::Platform::iOS:
+ return platformExcludesExecutablePath_iOS(path);
+ case dyld3::Platform::tvOS:
+ return platformExcludesExecutablePath_tvOS(path);
+ case dyld3::Platform::watchOS:
+ return platformExcludesExecutablePath_watchOS(path);
+ case dyld3::Platform::bridgeOS:
+ return platformExcludesExecutablePath_bridgeOS(path);
+ case dyld3::Platform::iOSMac:
+ return false;
+ case dyld3::Platform::iOS_simulator:
+ return false;
+ case dyld3::Platform::tvOS_simulator:
+ return false;
+ case dyld3::Platform::watchOS_simulator:
+ return false;
+ case dyld3::Platform::driverKit:
+ return false;
+ }
+ }
+
+ const dyld3::closure::FileSystem& fileSystem;
+ const dyld3::GradedArchs& reqArchs;
+ dyld3::Platform reqPlatform;
+};
+
+SharedCacheBuilder::SharedCacheBuilder(const DyldSharedCache::CreateOptions& options,
+ const dyld3::closure::FileSystem& fileSystem)
+ : CacheBuilder(options, fileSystem) {
+
+ std::string targetArch = options.archs->name();
+ if ( options.forSimulator && (options.archs == &dyld3::GradedArchs::i386) )
+ targetArch = "sim-x86";
+
+ for (const ArchLayout& layout : _s_archLayout) {
+ if ( layout.archName == targetArch ) {
+ _archLayout = &layout;
+ _is64 = _archLayout->is64;
+ break;
+ }
+ }
+
+ if (!_archLayout) {
+ _diagnostics.error("Tool was built without support for: '%s'", targetArch.c_str());
+ }
+}
+
+static void verifySelfContained(std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
+ std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
+ std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles)
+{
+ // build map of dylibs
+ __block std::map<std::string, const CacheBuilder::LoadedMachO*> knownDylibs;
+ __block std::map<std::string, const CacheBuilder::LoadedMachO*> allDylibs;
+ for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+ knownDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+ allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+ if (const char* installName = dylib.mappedFile.mh->installName()) {
+ knownDylibs.insert({ installName, &dylib });
+ allDylibs.insert({ installName, &dylib });
+ }
+ }
+
+ for (const CacheBuilder::LoadedMachO& dylib : otherDylibs) {
+ allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+ if (const char* installName = dylib.mappedFile.mh->installName())
+ allDylibs.insert({ installName, &dylib });
+ }
+
+ for (const CacheBuilder::LoadedMachO& dylib : couldNotLoadFiles) {
+ allDylibs.insert({ dylib.inputFile->path, &dylib });
+ }
+
+ // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
+ __block std::map<std::string, std::set<std::string>> badDylibs;
+ __block bool doAgain = true;
+ while ( doAgain ) {
+ doAgain = false;
+ // scan dylib list making sure all dependents are in dylib list
+ for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+ if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+ continue;
+ dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if (isWeak)
+ return;
+ if ( knownDylibs.count(loadPath) == 0 ) {
+ badDylibs[dylib.mappedFile.runtimePath].insert(std::string("Could not find dependency '") + loadPath + "'");
+ knownDylibs.erase(dylib.mappedFile.runtimePath);
+ knownDylibs.erase(dylib.mappedFile.mh->installName());
+ doAgain = true;
+ }
+ });
+ }
+ }
+
+ // Now walk the dylibs which depend on missing dylibs and see if any of them are required binaries.
+ for (auto badDylibsIterator : badDylibs) {
+ const std::string& dylibRuntimePath = badDylibsIterator.first;
+ auto requiredDylibIterator = allDylibs.find(dylibRuntimePath);
+ if (requiredDylibIterator == allDylibs.end())
+ continue;
+ if (!requiredDylibIterator->second->inputFile->mustBeIncluded())
+ continue;
+ // This dylib is required so mark all dependencies as requried too
+ __block std::vector<const CacheBuilder::LoadedMachO*> worklist;
+ worklist.push_back(requiredDylibIterator->second);
+ while (!worklist.empty()) {
+ const CacheBuilder::LoadedMachO* dylib = worklist.back();
+ worklist.pop_back();
+ if (!dylib->mappedFile.mh)
+ continue;
+ dylib->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+ if (isWeak)
+ return;
+ auto dylibIterator = allDylibs.find(loadPath);
+ if (dylibIterator != allDylibs.end()) {
+ if (dylibIterator->second->inputFile->state == CacheBuilder::InputFile::Unset) {
+ dylibIterator->second->inputFile->state = CacheBuilder::InputFile::MustBeIncludedForDependent;
+ worklist.push_back(dylibIterator->second);
+ }
+ }
+ });
+ }
+ }
+
+ // FIXME: Make this an option we can pass in
+ const bool evictLeafDylibs = true;
+ if (evictLeafDylibs) {
+ doAgain = true;
+ while ( doAgain ) {
+ doAgain = false;
+
+ // build count of how many references there are to each dylib
+ __block std::set<std::string> referencedDylibs;
+ for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+ if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+ continue;
+ dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+ referencedDylibs.insert(loadPath);
+ });
+ }
+
+ // find all dylibs not referenced
+ for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+ if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+ continue;
+ const char* installName = dylib.mappedFile.mh->installName();
+ if ( (referencedDylibs.count(installName) == 0) && (dylib.inputFile->state == CacheBuilder::InputFile::MustBeExcludedIfUnused) ) {
+ badDylibs[dylib.mappedFile.runtimePath].insert(std::string("It has been explicitly excluded as it is unused"));
+ doAgain = true;
+ }
+ }
+ }
+ }
+
+ // Move bad dylibs from dylibs to cache to other dylibs.
+ for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+ auto i = badDylibs.find(dylib.mappedFile.runtimePath);
+ if ( i != badDylibs.end()) {
+ otherDylibs.push_back(dylib);
+ for (const std::string& reason : i->second )
+ otherDylibs.back().inputFile->diag.warning("Dylib located at '%s' not placed in shared cache because: %s", dylib.mappedFile.runtimePath.c_str(), reason.c_str());
+ }
+ }
+
+ const auto& badDylibsLambdaRef = badDylibs;
+ dylibsToCache.erase(std::remove_if(dylibsToCache.begin(), dylibsToCache.end(), [&](const CacheBuilder::LoadedMachO& dylib) {
+ if (badDylibsLambdaRef.find(dylib.mappedFile.runtimePath) != badDylibsLambdaRef.end())
+ return true;
+ return false;
+ }), dylibsToCache.end());
+}
+
+// This is the new build API which takes the raw files (which could be FAT) and tries to build a cache from them.
+// We should remove the other build() method, or make it private so that this can wrap it.
+void SharedCacheBuilder::build(std::vector<CacheBuilder::InputFile>& inputFiles,
+ std::vector<DyldSharedCache::FileAlias>& aliases) {
+ // First filter down to files which are actually MachO's
+ CacheInputBuilder cacheInputBuilder(_fileSystem, *_options.archs, _options.platform);
+
+ std::vector<LoadedMachO> dylibsToCache;
+ std::vector<LoadedMachO> otherDylibs;
+ std::vector<LoadedMachO> executables;
+ std::vector<LoadedMachO> couldNotLoadFiles;
+ cacheInputBuilder.loadMachOs(inputFiles, dylibsToCache, otherDylibs, executables, couldNotLoadFiles);
+
+ verifySelfContained(dylibsToCache, otherDylibs, couldNotLoadFiles);
+
+ // Check for required binaries before we try to build the cache
+ if (!_diagnostics.hasError()) {
+ // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
+ std::string errorString;
+ for (const LoadedMachO& dylib : otherDylibs) {
+ if (dylib.inputFile->mustBeIncluded()) {
+ // An error loading a required file must be propagated up to the top level diagnostic handler.
+ bool gotWarning = false;
+ for (const std::string& warning : dylib.inputFile->diag.warnings()) {
+ gotWarning = true;
+ std::string message = warning;
+ if (message.back() == '\n')
+ message.pop_back();
+ if (!errorString.empty())
+ errorString += "ERROR: ";
+ errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + message + "\n";
+ }
+ if (!gotWarning) {
+ if (!errorString.empty())
+ errorString += "ERROR: ";
+ errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error. Please report to dyld'\n";
+ }
+ }
+ }
+ for (const LoadedMachO& dylib : couldNotLoadFiles) {
+ if (dylib.inputFile->mustBeIncluded()) {
+ if (dylib.inputFile->diag.hasError()) {
+ if (!errorString.empty())
+ errorString += "ERROR: ";
+ errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + dylib.inputFile->diag.errorMessage() + "\n";
+ } else {
+ if (!errorString.empty())
+ errorString += "ERROR: ";
+ errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error. Please report to dyld'\n";
+
+ }
+ }
+ }
+ if (!errorString.empty()) {
+ _diagnostics.error("%s", errorString.c_str());
+ }
+ }
+
+ if (!_diagnostics.hasError())
+ build(dylibsToCache, otherDylibs, executables, aliases);
+
+ if (!_diagnostics.hasError()) {
+ // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
+ std::string errorString;
+ for (CacheBuilder::InputFile& inputFile : inputFiles) {
+ if (inputFile.mustBeIncluded() && inputFile.diag.hasError()) {
+ // An error loading a required file must be propagated up to the top level diagnostic handler.
+ std::string message = inputFile.diag.errorMessage();
+ if (message.back() == '\n')
+ message.pop_back();
+ errorString += "Required binary was not included in the shared cache '" + std::string(inputFile.path) + "' because: " + message + "\n";
+ }
+ }
+ if (!errorString.empty()) {
+ _diagnostics.error("%s", errorString.c_str());
+ }
+ }
+
+ // Add all the warnings from the input files to the top level warnings on the main diagnostics object.
+ for (CacheBuilder::InputFile& inputFile : inputFiles) {
+ for (const std::string& warning : inputFile.diag.warnings())
+ _diagnostics.warning("%s", warning.c_str());
+ }
+
+ // Clean up the loaded files
+ for (LoadedMachO& loadedMachO : dylibsToCache)
+ _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+ for (LoadedMachO& loadedMachO : otherDylibs)
+ _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+ for (LoadedMachO& loadedMachO : executables)
+ _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+}
+
+void SharedCacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs,
+ const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibsInput,
+ const std::vector<DyldSharedCache::MappedMachO>& osExecutables,
+ std::vector<DyldSharedCache::FileAlias>& aliases) {
+
+ std::vector<LoadedMachO> dylibsToCache;
+ std::vector<LoadedMachO> otherDylibs;
+ std::vector<LoadedMachO> executables;
+
+ for (const DyldSharedCache::MappedMachO& mappedMachO : dylibs) {
+ dyld3::closure::LoadedFileInfo loadedFileInfo;
+ loadedFileInfo.fileContent = mappedMachO.mh;
+ loadedFileInfo.fileContentLen = mappedMachO.length;
+ loadedFileInfo.sliceOffset = mappedMachO.sliceFileOffset;
+ loadedFileInfo.sliceLen = mappedMachO.length;
+ loadedFileInfo.inode = mappedMachO.inode;
+ loadedFileInfo.mtime = mappedMachO.modTime;
+ loadedFileInfo.path = mappedMachO.runtimePath.c_str();
+ dylibsToCache.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+ }
+
+ for (const DyldSharedCache::MappedMachO& mappedMachO : otherOsDylibsInput) {
+ dyld3::closure::LoadedFileInfo loadedFileInfo;
+ loadedFileInfo.fileContent = mappedMachO.mh;
+ loadedFileInfo.fileContentLen = mappedMachO.length;
+ loadedFileInfo.sliceOffset = mappedMachO.sliceFileOffset;
+ loadedFileInfo.sliceLen = mappedMachO.length;
+ loadedFileInfo.inode = mappedMachO.inode;
+ loadedFileInfo.mtime = mappedMachO.modTime;
+ loadedFileInfo.path = mappedMachO.runtimePath.c_str();
+ otherDylibs.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+ }
+
+ for (const DyldSharedCache::MappedMachO& mappedMachO : osExecutables) {
+ dyld3::closure::LoadedFileInfo loadedFileInfo;
+ loadedFileInfo.fileContent = mappedMachO.mh;
+ loadedFileInfo.fileContentLen = mappedMachO.length;
+ loadedFileInfo.sliceOffset = mappedMachO.sliceFileOffset;
+ loadedFileInfo.sliceLen = mappedMachO.length;
+ loadedFileInfo.inode = mappedMachO.inode;
+ loadedFileInfo.mtime = mappedMachO.modTime;
+ loadedFileInfo.path = mappedMachO.runtimePath.c_str();
+ executables.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+ }
+
+ build(dylibsToCache, otherDylibs, executables, aliases);
+}
+
+void SharedCacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
+ const std::vector<LoadedMachO>& otherOsDylibsInput,
+ const std::vector<LoadedMachO>& osExecutables,
+ std::vector<DyldSharedCache::FileAlias>& aliases)
+{
+ // <rdar://problem/21317611> error out instead of crash if cache has no dylibs
+ // FIXME: plist should specify required vs optional dylibs
+ if ( dylibs.size() < 30 ) {
+ _diagnostics.error("missing required minimum set of dylibs");
+ return;
+ }
+ uint64_t t1 = mach_absolute_time();
+
+ // make copy of dylib list and sort
+ makeSortedDylibs(dylibs, _options.dylibOrdering);
+
+ // allocate space used by largest possible cache plus room for LINKEDITS before optimization
+ _allocatedBufferSize = _archLayout->sharedMemorySize * 1.50;
+ if ( vm_allocate(mach_task_self(), &_fullAllocatedBuffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) {
+ _diagnostics.error("could not allocate buffer");
+ return;
+ }
+
+ // assign addresses for each segment of each dylib in new cache
+ parseCoalescableSegments();
+ processSelectorStrings(osExecutables);
+ assignSegmentAddresses();
+ std::vector<const LoadedMachO*> overflowDylibs;
+ while ( cacheOverflowAmount() != 0 ) {
+ if ( !_options.evictLeafDylibsOnOverflow ) {
+ _diagnostics.error("cache overflow by %lluMB", cacheOverflowAmount() / 1024 / 1024);
+ return;
+ }
+ size_t evictionCount = evictLeafDylibs(cacheOverflowAmount(), overflowDylibs);
+ // re-layout cache
+ for (DylibInfo& dylib : _sortedDylibs)
+ dylib.cacheLocation.clear();
+ _coalescedText.clear();
+ parseCoalescableSegments();
+ processSelectorStrings(osExecutables);
+ assignSegmentAddresses();
+
+ _diagnostics.verbose("cache overflow, evicted %lu leaf dylibs\n", evictionCount);
+ }
+ markPaddingInaccessible();
+
+ // copy all segments into cache
+ uint64_t t2 = mach_absolute_time();
+ writeCacheHeader();
+ copyRawSegments();
+
+ // rebase all dylibs for new location in cache
+ uint64_t t3 = mach_absolute_time();
+ _aslrTracker.setDataRegion(_readWriteRegion.buffer, _readWriteRegion.sizeInUse);
+ if ( !_options.cacheSupportsASLR )
+ _aslrTracker.disable();
+ adjustAllImagesForNewSegmentLocations();
+ if ( _diagnostics.hasError() )
+ return;
+
+ // build ImageArray for dyld3, which has side effect of binding all cached dylibs
+ uint64_t t4 = mach_absolute_time();
+ buildImageArray(aliases);
+ if ( _diagnostics.hasError() )
+ return;
+
+ // optimize ObjC
+ uint64_t t5 = mach_absolute_time();
+ DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ if ( _options.optimizeObjC )
+ optimizeObjC();
+ if ( _diagnostics.hasError() )
+ return;
+
+
+ // optimize away stubs
+ uint64_t t6 = mach_absolute_time();
+ if ( _options.optimizeStubs )
+ optimizeAwayStubs();
+
+
+ // FIPS seal corecrypto, This must be done after stub elimination (so that __TEXT,__text is not changed after sealing)
+ fipsSign();
+
+ // merge and compact LINKEDIT segments
+ uint64_t t7 = mach_absolute_time();
+ optimizeLinkedit();
+
+ // copy ImageArray to end of read-only region
+ addImageArray();
+ if ( _diagnostics.hasError() )
+ return;
+ uint64_t t8 = mach_absolute_time();
+
+ // don't add dyld3 closures to simulator cache
+ if ( !dyld3::MachOFile::isSimulatorPlatform(_options.platform) ) {
+ // compute and add dlopen closures for all other dylibs
+ addOtherImageArray(otherOsDylibsInput, overflowDylibs);
+ if ( _diagnostics.hasError() )
+ return;
+
+ // compute and add launch closures to end of read-only region
+ addClosures(osExecutables);
+ if ( _diagnostics.hasError() )
+ return;
+ }
+
+ // update final readOnly region size
+ dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCache->header.mappingOffset);
+ mappings[2].size = _readOnlyRegion.sizeInUse;
+ if ( _options.excludeLocalSymbols )
+ dyldCache->header.localSymbolsOffset = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse;
+
+ // record max slide now that final size is established
+ if ( _archLayout->sharedRegionsAreDiscontiguous ) {
+ // special case x86_64 which has three non-contiguous chunks each in their own 1GB regions
+ uint64_t maxSlide0 = 0x60000000 - _readExecuteRegion.sizeInUse; // TEXT region has 1.5GB region
+ uint64_t maxSlide1 = 0x40000000 - _readWriteRegion.sizeInUse;
+ uint64_t maxSlide2 = 0x3FE00000 - _readOnlyRegion.sizeInUse;
+ dyldCache->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2);
+ }
+ else {
+ // <rdar://problem/49852839> branch predictor on arm64 currently only looks at low 32-bits, so don't slide cache more than 2GB
+ if ( (_archLayout->sharedMemorySize == 0x100000000) && (_readExecuteRegion.sizeInUse < 0x80000000) )
+ dyldCache->header.maxSlide = 0x80000000 - _readExecuteRegion.sizeInUse;
+ else
+ dyldCache->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (_readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
+ }
+
+ // mark if any input dylibs were built with chained fixups
+ dyldCache->header.builtFromChainedFixups = _someDylibsUsedChainedFixups;
+
+ uint64_t t9 = mach_absolute_time();
+
+ // fill in slide info at start of region[2]
+ // do this last because it modifies pointers in DATA segments
+ if ( _options.cacheSupportsASLR ) {
+#if SUPPORT_ARCH_arm64e
+ if ( strcmp(_archLayout->archName, "arm64e") == 0 )
+ writeSlideInfoV3(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+ else
+#endif
+ if ( _archLayout->is64 )
+ writeSlideInfoV2<Pointer64<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
+ else if ( _archLayout->pointerDeltaMask == 0xC0000000 )
+ writeSlideInfoV4<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+#endif
+ else
+ writeSlideInfoV2<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+ }
+
+ uint64_t t10 = mach_absolute_time();
+
+ // last sanity check on size
+ if ( cacheOverflowAmount() != 0 ) {
+ _diagnostics.error("cache overflow after optimizations 0x%llX -> 0x%llX", _readExecuteRegion.unslidLoadAddress, _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
+ return;
+ }
+
+ // codesignature is part of file, but is not mapped
+ codeSign();
+ if ( _diagnostics.hasError() )
+ return;
+
+ uint64_t t11 = mach_absolute_time();
+
+ if ( _options.verbose ) {
+ fprintf(stderr, "time to layout cache: %ums\n", absolutetime_to_milliseconds(t2-t1));
+ fprintf(stderr, "time to copy cached dylibs into buffer: %ums\n", absolutetime_to_milliseconds(t3-t2));
+ fprintf(stderr, "time to adjust segments for new split locations: %ums\n", absolutetime_to_milliseconds(t4-t3));
+ fprintf(stderr, "time to bind all images: %ums\n", absolutetime_to_milliseconds(t5-t4));
+ fprintf(stderr, "time to optimize Objective-C: %ums\n", absolutetime_to_milliseconds(t6-t5));
+ fprintf(stderr, "time to do stub elimination: %ums\n", absolutetime_to_milliseconds(t7-t6));
+ fprintf(stderr, "time to optimize LINKEDITs: %ums\n", absolutetime_to_milliseconds(t8-t7));
+ fprintf(stderr, "time to build %lu closures: %ums\n", osExecutables.size(), absolutetime_to_milliseconds(t9-t8));
+ fprintf(stderr, "time to compute slide info: %ums\n", absolutetime_to_milliseconds(t10-t9));
+ fprintf(stderr, "time to compute UUID and codesign cache file: %ums\n", absolutetime_to_milliseconds(t11-t10));
+ }
+
+ return;
+}
+
+const std::set<std::string> SharedCacheBuilder::warnings()
+{
+ return _diagnostics.warnings();
+}
+
+const std::set<const dyld3::MachOAnalyzer*> SharedCacheBuilder::evictions()
+{
+ return _evictions;
+}
+
+void SharedCacheBuilder::deleteBuffer()
+{
+ // Cache buffer
+ vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _archLayout->sharedMemorySize);
+ _fullAllocatedBuffer = 0;
+ _allocatedBufferSize = 0;
+ // Local symbols buffer
+ if ( _localSymbolsRegion.bufferSize != 0 ) {
+ vm_deallocate(mach_task_self(), (vm_address_t)_localSymbolsRegion.buffer, _localSymbolsRegion.bufferSize);
+ _localSymbolsRegion.buffer = 0;
+ _localSymbolsRegion.bufferSize = 0;
+ }
+ // Code singatures
+ vm_deallocate(mach_task_self(), (vm_address_t)_codeSignatureRegion.buffer, _codeSignatureRegion.bufferSize);
+ _codeSignatureRegion.buffer = 0;
+ _codeSignatureRegion.bufferSize = 0;
+}
+
+
+void SharedCacheBuilder::makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder)
+{
+ for (const LoadedMachO& dylib : dylibs) {
+ _sortedDylibs.push_back({ &dylib, dylib.mappedFile.runtimePath, {} });
+ }
+
+ std::sort(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& a, const DylibInfo& b) {
+ const auto& orderA = sortOrder.find(a.input->mappedFile.runtimePath);
+ const auto& orderB = sortOrder.find(b.input->mappedFile.runtimePath);
+ bool foundA = (orderA != sortOrder.end());
+ bool foundB = (orderB != sortOrder.end());
+
+ // Order all __DATA_DIRTY segments specified in the order file first, in
+ // the order specified in the file, followed by any other __DATA_DIRTY
+ // segments in lexicographic order.
+ if ( foundA && foundB )
+ return orderA->second < orderB->second;
+ else if ( foundA )
+ return true;
+ else if ( foundB )
+ return false;
+
+ // Sort mac before iOSMac
+ bool isIOSMacA = strncmp(a.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0;
+ bool isIOSMacB = strncmp(b.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0;
+ if (isIOSMacA != isIOSMacB)
+ return !isIOSMacA;
+
+ // Finally sort by path
+ return a.input->mappedFile.runtimePath < b.input->mappedFile.runtimePath;
+ });
+}
+
+struct DylibAndSize
+{
+ const CacheBuilder::LoadedMachO* input;
+ const char* installName;
+ uint64_t size;
+};
+
+uint64_t SharedCacheBuilder::cacheOverflowAmount()
+{
+ if ( _archLayout->sharedRegionsAreDiscontiguous ) {
+ // for macOS x86_64 cache, need to check each region for overflow
+ if ( _readExecuteRegion.sizeInUse > 0x60000000 )
+ return (_readExecuteRegion.sizeInUse - 0x60000000);
+
+ if ( _readWriteRegion.sizeInUse > 0x40000000 )
+ return (_readWriteRegion.sizeInUse - 0x40000000);
+
+ if ( _readOnlyRegion.sizeInUse > 0x3FE00000 )
+ return (_readOnlyRegion.sizeInUse - 0x3FE00000);
+ }
+ else if ( _archLayout->textAndDataMaxSize != 0 ) {
+ // for armv7k, limit is 512MB of TEX+DATA
+ uint64_t totalTextAndData = _readWriteRegion.unslidLoadAddress + _readWriteRegion.sizeInUse - _readExecuteRegion.unslidLoadAddress;
+ if ( totalTextAndData < _archLayout->textAndDataMaxSize )
+ return 0;
+ else
+ return totalTextAndData - _archLayout->textAndDataMaxSize;
+ }
+ else {
+ bool alreadyOptimized = (_readOnlyRegion.sizeInUse != _readOnlyRegion.bufferSize);
+ uint64_t vmSize = _readOnlyRegion.unslidLoadAddress - _readExecuteRegion.unslidLoadAddress;
+ if ( alreadyOptimized )
+ vmSize += _readOnlyRegion.sizeInUse;
+ else if ( _options.excludeLocalSymbols )
+ vmSize += (_readOnlyRegion.sizeInUse * 37/100); // assume locals removal and LINKEDIT optimzation reduces LINKEDITs %37 of original size
+ else
+ vmSize += (_readOnlyRegion.sizeInUse * 80/100); // assume LINKEDIT optimzation reduces LINKEDITs to %80 of original size
+ if ( vmSize > _archLayout->sharedMemorySize )
+ return vmSize - _archLayout->sharedMemorySize;
+ }
+ // fits in shared region
+ return 0;
+}
+
+size_t SharedCacheBuilder::evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs)
+{
+ // build a reverse map of all dylib dependencies
+ __block std::map<std::string, std::set<std::string>> references;
+ std::map<std::string, std::set<std::string>>* referencesPtr = &references;
+ for (const DylibInfo& dylib : _sortedDylibs) {
+ // Esnure we have an entry (even if it is empty)
+ if (references.count(dylib.input->mappedFile.mh->installName()) == 0) {
+ references[dylib.input->mappedFile.mh->installName()] = std::set<std::string>();
+ };
+ dylib.input->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+ references[loadPath].insert(dylib.input->mappedFile.mh->installName());
+ });
+ }
+
+ // Find the sizes of all the dylibs
+ std::vector<DylibAndSize> dylibsToSort;
+ std::vector<DylibAndSize> sortedDylibs;
+ for (const DylibInfo& dylib : _sortedDylibs) {
+ const char* installName = dylib.input->mappedFile.mh->installName();
+ __block uint64_t segsSize = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
+ if ( strcmp(info.segName, "__LINKEDIT") != 0 )
+ segsSize += info.vmSize;
+ });
+ dylibsToSort.push_back({ dylib.input, installName, segsSize });
+ }
+
+ // Build an ordered list of what to remove. At each step we do following
+ // 1) Find all dylibs that nothing else depends on
+ // 2a) If any of those dylibs are not in the order select the largest one of them
+ // 2b) If all the leaf dylibs are in the order file select the last dylib that appears last in the order file
+ // 3) Remove all entries to the removed file from the reverse dependency map
+ // 4) Go back to one and repeat until there are no more evictable dylibs
+ // This results in us always choosing the locally optimal selection, and then taking into account how that impacts
+ // the dependency graph for subsequent selections
+
+ bool candidateFound = true;
+ while (candidateFound) {
+ candidateFound = false;
+ DylibAndSize candidate;
+ uint64_t candidateOrder = 0;
+ for(const auto& dylib : dylibsToSort) {
+ const auto &i = referencesPtr->find(dylib.installName);
+ assert(i != referencesPtr->end());
+ if (!i->second.empty()) {
+ continue;
+ }
+ const auto& j = _options.dylibOrdering.find(dylib.input->mappedFile.runtimePath);
+ uint64_t order = 0;
+ if (j != _options.dylibOrdering.end()) {
+ order = j->second;
+ } else {
+ // Not in the order file, set order sot it goes to the front of the list
+ order = UINT64_MAX;
+ }
+ if (order > candidateOrder ||
+ (order == UINT64_MAX && candidate.size < dylib.size)) {
+ // The new file is either a lower priority in the order file
+ // or the same priority as the candidate but larger
+ candidate = dylib;
+ candidateOrder = order;
+ candidateFound = true;
+ }
+ }
+ if (candidateFound) {
+ sortedDylibs.push_back(candidate);
+ referencesPtr->erase(candidate.installName);
+ for (auto& dependent : references) {
+ (void)dependent.second.erase(candidate.installName);
+ }
+ auto j = std::find_if(dylibsToSort.begin(), dylibsToSort.end(), [&candidate](const DylibAndSize& dylib) {
+ return (strcmp(candidate.installName, dylib.installName) == 0);
+ });
+ if (j != dylibsToSort.end()) {
+ dylibsToSort.erase(j);
+ }
+ }
+ }
+
+ // build set of dylibs that if removed will allow cache to build
+ for (DylibAndSize& dylib : sortedDylibs) {
+ if ( _options.verbose )
+ _diagnostics.warning("to prevent cache overflow, not caching %s", dylib.installName);
+ _evictions.insert(dylib.input->mappedFile.mh);
+ // Track the evicted dylibs so we can try build "other" dlopen closures for them.
+ overflowDylibs.push_back(dylib.input);
+ if ( dylib.size > reductionTarget )
+ break;
+ reductionTarget -= dylib.size;
+ }
+
+ // prune _sortedDylibs
+ _sortedDylibs.erase(std::remove_if(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& dylib) {
+ return (_evictions.count(dylib.input->mappedFile.mh) != 0);
+ }),_sortedDylibs.end());
+
+ return _evictions.size();
+}
+
+
+void SharedCacheBuilder::writeCacheHeader()
+{
+ // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
+ std::string magic = "dyld_v1";
+ magic.append(15 - magic.length() - strlen(_options.archs->name()), ' ');
+ magic.append(_options.archs->name());
+ assert(magic.length() == 15);
+
+ // fill in header
+ dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+ memcpy(dyldCacheHeader->magic, magic.c_str(), 16);
+ dyldCacheHeader->mappingOffset = sizeof(dyld_cache_header);
+ dyldCacheHeader->mappingCount = 3;
+ dyldCacheHeader->imagesOffset = (uint32_t)(dyldCacheHeader->mappingOffset + 3*sizeof(dyld_cache_mapping_info));
+ dyldCacheHeader->imagesCount = (uint32_t)_sortedDylibs.size() + _aliasCount;
+ dyldCacheHeader->dyldBaseAddress = 0;
+ dyldCacheHeader->codeSignatureOffset = 0;
+ dyldCacheHeader->codeSignatureSize = 0;
+ dyldCacheHeader->slideInfoOffset = _slideInfoFileOffset;
+ dyldCacheHeader->slideInfoSize = _slideInfoBufferSizeAllocated;
+ dyldCacheHeader->localSymbolsOffset = 0;
+ dyldCacheHeader->localSymbolsSize = 0;
+ dyldCacheHeader->cacheType = _options.optimizeStubs ? kDyldSharedCacheTypeProduction : kDyldSharedCacheTypeDevelopment;
+ dyldCacheHeader->accelerateInfoAddr = 0;
+ dyldCacheHeader->accelerateInfoSize = 0;
+ bzero(dyldCacheHeader->uuid, 16);// overwritten later by recomputeCacheUUID()
+ dyldCacheHeader->branchPoolsOffset = 0;
+ dyldCacheHeader->branchPoolsCount = 0;
+ dyldCacheHeader->imagesTextOffset = dyldCacheHeader->imagesOffset + sizeof(dyld_cache_image_info)*dyldCacheHeader->imagesCount;
+ dyldCacheHeader->imagesTextCount = _sortedDylibs.size();
+ dyldCacheHeader->patchInfoAddr = 0;
+ dyldCacheHeader->patchInfoSize = 0;
+ dyldCacheHeader->otherImageGroupAddrUnused = 0;
+ dyldCacheHeader->otherImageGroupSizeUnused = 0;
+ dyldCacheHeader->progClosuresAddr = 0;
+ dyldCacheHeader->progClosuresSize = 0;
+ dyldCacheHeader->progClosuresTrieAddr = 0;
+ dyldCacheHeader->progClosuresTrieSize = 0;
+ dyldCacheHeader->platform = (uint8_t)_options.platform;
+ dyldCacheHeader->formatVersion = dyld3::closure::kFormatVersion;
+ dyldCacheHeader->dylibsExpectedOnDisk = !_options.dylibsRemovedDuringMastering;
+ dyldCacheHeader->simulator = _options.forSimulator;
+ dyldCacheHeader->locallyBuiltCache = _options.isLocallyBuiltCache;
+ dyldCacheHeader->builtFromChainedFixups= false;
+ dyldCacheHeader->formatVersion = dyld3::closure::kFormatVersion;
+ dyldCacheHeader->sharedRegionStart = _archLayout->sharedMemoryStart;
+ dyldCacheHeader->sharedRegionSize = _archLayout->sharedMemorySize;
+
+ // fill in mappings
+ dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCacheHeader->mappingOffset);
+ mappings[0].address = _readExecuteRegion.unslidLoadAddress;
+ mappings[0].fileOffset = 0;
+ mappings[0].size = _readExecuteRegion.sizeInUse;
+ mappings[0].maxProt = VM_PROT_READ | VM_PROT_EXECUTE;
+ mappings[0].initProt = VM_PROT_READ | VM_PROT_EXECUTE;
+ mappings[1].address = _readWriteRegion.unslidLoadAddress;
+ mappings[1].fileOffset = _readExecuteRegion.sizeInUse;
+ mappings[1].size = _readWriteRegion.sizeInUse;
+ mappings[1].maxProt = VM_PROT_READ | VM_PROT_WRITE;
+ mappings[1].initProt = VM_PROT_READ | VM_PROT_WRITE;
+ mappings[2].address = _readOnlyRegion.unslidLoadAddress;
+ mappings[2].fileOffset = _readExecuteRegion.sizeInUse + _readWriteRegion.sizeInUse;
+ mappings[2].size = _readOnlyRegion.sizeInUse;
+ mappings[2].maxProt = VM_PROT_READ;
+ mappings[2].initProt = VM_PROT_READ;
+
+ // fill in image table
+ dyld_cache_image_info* images = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
+ for (const DylibInfo& dylib : _sortedDylibs) {
+ const char* installName = dylib.input->mappedFile.mh->installName();
+ images->address = dylib.cacheLocation[0].dstCacheUnslidAddress;
+ if ( _options.dylibsRemovedDuringMastering ) {
+ images->modTime = 0;
+ images->inode = pathHash(installName);
+ }
+ else {
+ images->modTime = dylib.input->mappedFile.modTime;
+ images->inode = dylib.input->mappedFile.inode;
+ }
+ uint32_t installNameOffsetInTEXT = (uint32_t)(installName - (char*)dylib.input->mappedFile.mh);
+ images->pathFileOffset = (uint32_t)dylib.cacheLocation[0].dstCacheFileOffset + installNameOffsetInTEXT;
+ ++images;
+ }
+ // append aliases image records and strings
+/*
+ for (auto &dylib : _dylibs) {
+ if (!dylib->installNameAliases.empty()) {
+ for (const std::string& alias : dylib->installNameAliases) {
+ images->set_address(_segmentMap[dylib][0].address);
+ if (_manifest.platform() == "osx") {
+ images->modTime = dylib->lastModTime;
+ images->inode = dylib->inode;
+ }
+ else {
+ images->modTime = 0;
+ images->inode = pathHash(alias.c_str());
+ }
+ images->pathFileOffset = offset;
+ //fprintf(stderr, "adding alias %s for %s\n", alias.c_str(), dylib->installName.c_str());
+ ::strcpy((char*)&_buffer[offset], alias.c_str());
+ offset += alias.size() + 1;
+ ++images;
+ }
+ }
+ }
+*/
+ // calculate start of text image array and trailing string pool
+ dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesTextOffset);
+ uint32_t stringOffset = (uint32_t)(dyldCacheHeader->imagesTextOffset + sizeof(dyld_cache_image_text_info) * _sortedDylibs.size());
+
+ // write text image array and image names pool at same time
+ for (const DylibInfo& dylib : _sortedDylibs) {
+ dylib.input->mappedFile.mh->getUuid(textImages->uuid);
+ textImages->loadAddress = dylib.cacheLocation[0].dstCacheUnslidAddress;
+ textImages->textSegmentSize = (uint32_t)dylib.cacheLocation[0].dstCacheSegmentSize;
+ textImages->pathOffset = stringOffset;
+ const char* installName = dylib.input->mappedFile.mh->installName();
+ ::strcpy((char*)_readExecuteRegion.buffer + stringOffset, installName);
+ stringOffset += (uint32_t)strlen(installName)+1;
+ ++textImages;
+ }
+
+ // make sure header did not overflow into first mapped image
+ const dyld_cache_image_info* firstImage = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
+ assert(stringOffset <= (firstImage->address - mappings[0].address));
+}
+
+void SharedCacheBuilder::processSelectorStrings(const std::vector<LoadedMachO>& executables) {
+ const bool log = false;
+
+ _selectorStringsFromExecutables = 0;
+
+ // Don't do this optimisation on watchOS where the shared cache is too small
+ if (_options.platform == dyld3::Platform::watchOS)
+ return;
+
+ // Get the method name coalesced section as that is where we need to put these strings
+ CacheBuilder::CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData("__objc_methname");
+ for (const LoadedMachO& executable : executables) {
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)executable.loadedFileInfo.fileContent;
+
+ uint64_t sizeBeforeProcessing = cacheStringSection.bufferSize;
+
+ ma->forEachObjCMethodName(^(const char *methodName) {
+ std::string_view str = methodName;
+ auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize });
+ if (itAndInserted.second) {
+ // If we inserted the string then we need to include it in the total
+ cacheStringSection.bufferSize += str.size() + 1;
+ // if (log) printf("Selector: %s -> %s\n", ma->installName(), methodName);
+ ++_selectorStringsFromExecutables;
+ }
+ });
+
+ uint64_t sizeAfterProcessing = cacheStringSection.bufferSize;
+ if ( log && (sizeBeforeProcessing != sizeAfterProcessing) ) {
+ printf("Pulled in % 6lld bytes of selectors from %s\n",
+ sizeAfterProcessing - sizeBeforeProcessing, executable.loadedFileInfo.path);
+ }
+ }
+
+ _diagnostics.verbose("Pulled in %lld selector strings from executables\n", _selectorStringsFromExecutables);
+}
+
+void SharedCacheBuilder::parseCoalescableSegments() {
+ const bool log = false;
+
+ for (DylibInfo& dylib : _sortedDylibs)
+ _coalescedText.parseCoalescableText(dylib.input->mappedFile.mh, dylib.textCoalescer);
+
+ if (log) {
+ for (const char* section : CacheCoalescedText::SupportedSections) {
+ CacheCoalescedText::StringSection& sectionData = _coalescedText.getSectionData(section);
+ printf("Coalesced %s from % 10lld -> % 10d, saving % 10lld bytes\n", section,
+ sectionData.bufferSize + sectionData.savedSpace, sectionData.bufferSize, sectionData.savedSpace);
+ }
+ }
+}
+
+void SharedCacheBuilder::assignSegmentAddresses()
+{
+ // calculate size of header info and where first dylib's mach_header should start
+ size_t startOffset = sizeof(dyld_cache_header) + 3*sizeof(dyld_cache_mapping_info);
+ startOffset += sizeof(dyld_cache_image_info) * _sortedDylibs.size();
+ startOffset += sizeof(dyld_cache_image_text_info) * _sortedDylibs.size();
+ for (const DylibInfo& dylib : _sortedDylibs) {
+ startOffset += (strlen(dylib.input->mappedFile.mh->installName()) + 1);
+ }
+ //fprintf(stderr, "%s total header size = 0x%08lX\n", _options.archName.c_str(), startOffset);
+ startOffset = align(startOffset, 12);
+
+ // HACK!: Rebase v4 assumes that values below 0x8000 are not pointers (encoding as offsets from the cache header).
+ // If using a minimal cache, we need to pad out the cache header to make sure a pointer doesn't fall within that range
+#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
+ if ( _options.cacheSupportsASLR && !_archLayout->is64 ) {
+ if ( _archLayout->pointerDeltaMask == 0xC0000000 )
+ startOffset = std::max(startOffset, (size_t)0x8000);
+ }
+#endif
+
+ // assign TEXT segment addresses
+ _readExecuteRegion.buffer = (uint8_t*)_fullAllocatedBuffer;
+ _readExecuteRegion.bufferSize = 0;
+ _readExecuteRegion.sizeInUse = 0;
+ _readExecuteRegion.unslidLoadAddress = _archLayout->sharedMemoryStart;
+ _readExecuteRegion.cacheFileOffset = 0;
+ __block uint64_t addr = _readExecuteRegion.unslidLoadAddress + startOffset; // header
+ for (DylibInfo& dylib : _sortedDylibs) {
+ __block uint64_t textSegVmAddr = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+ if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+ textSegVmAddr = segInfo.vmAddr;
+ if ( segInfo.protections != (VM_PROT_READ | VM_PROT_EXECUTE) )
+ return;
+ // We may have coalesced the sections at the end of this segment. In that case, shrink the segment to remove them.
+ __block size_t sizeOfSections = 0;
+ __block bool foundCoalescedSection = false;
+ dylib.input->mappedFile.mh->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §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 + 0x4000, _archLayout->sharedRegionAlignP2);
+ _slideInfoFileOffset = _readOnlyRegion.cacheFileOffset;
+ addr += _slideInfoBufferSizeAllocated;
+ }
+
+ // layout all read-only (but not LINKEDIT) segments
+ if ( _archLayout->textAndDataMaxSize == 0 ) {
+ for (DylibInfo& dylib : _sortedDylibs) {
+ __block uint64_t textSegVmAddr = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+ if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+ textSegVmAddr = segInfo.vmAddr;
+ if ( segInfo.protections != VM_PROT_READ )
+ return;
+ if ( strcmp(segInfo.segName, "__LINKEDIT") == 0 )
+ return;
+
+ // Keep segments segments 4K or more aligned
+ addr = align(addr, std::max((int)segInfo.p2align, (int)12));
+ uint64_t offsetInRegion = addr - _readOnlyRegion.unslidLoadAddress;
+ SegmentMappingInfo loc;
+ loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+ loc.segName = segInfo.segName;
+ loc.dstSegment = _readOnlyRegion.buffer + offsetInRegion;
+ loc.dstCacheUnslidAddress = addr;
+ loc.dstCacheFileOffset = (uint32_t)(_readOnlyRegion.cacheFileOffset + offsetInRegion);
+ loc.dstCacheSegmentSize = (uint32_t)align(segInfo.sizeOfSections, 12);
+ loc.dstCacheFileSize = (uint32_t)segInfo.sizeOfSections;
+ loc.copySegmentSize = (uint32_t)segInfo.sizeOfSections;
+ loc.srcSegmentIndex = segInfo.segIndex;
+ dylib.cacheLocation.push_back(loc);
+ addr += loc.dstCacheSegmentSize;
+ });
+ }
+ }
+ // layout all LINKEDIT segments (after other read-only segments), aligned to 16KB
+ addr = align(addr, 14);
+ _nonLinkEditReadOnlySize = addr - _readOnlyRegion.unslidLoadAddress;
+ for (DylibInfo& dylib : _sortedDylibs) {
+ __block uint64_t textSegVmAddr = 0;
+ dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+ if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+ textSegVmAddr = segInfo.vmAddr;
+ if ( segInfo.protections != VM_PROT_READ )
+ return;
+ if ( strcmp(segInfo.segName, "__LINKEDIT") != 0 )
+ return;
+ // Keep segments segments 4K or more aligned
+ addr = align(addr, std::max((int)segInfo.p2align, (int)12));
+ size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+ uint64_t offsetInRegion = addr - _readOnlyRegion.unslidLoadAddress;
+ SegmentMappingInfo loc;
+ loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+ loc.segName = segInfo.segName;
+ loc.dstSegment = _readOnlyRegion.buffer + offsetInRegion;
+ loc.dstCacheUnslidAddress = addr;
+ loc.dstCacheFileOffset = (uint32_t)(_readOnlyRegion.cacheFileOffset + offsetInRegion);
+ loc.dstCacheSegmentSize = (uint32_t)align(segInfo.sizeOfSections, 12);
+ loc.dstCacheFileSize = (uint32_t)copySize;
+ loc.copySegmentSize = (uint32_t)copySize;
+ loc.srcSegmentIndex = segInfo.segIndex;
+ dylib.cacheLocation.push_back(loc);
+ addr += loc.dstCacheSegmentSize;
+ });
+ }
+
+ // align r/o region end
+ uint64_t endReadOnlyAddress = align(addr, _archLayout->sharedRegionAlignP2);
+ _readOnlyRegion.bufferSize = endReadOnlyAddress - _readOnlyRegion.unslidLoadAddress;
+ _readOnlyRegion.sizeInUse = _readOnlyRegion.bufferSize;
+
+ //fprintf(stderr, "RX region=%p -> %p, logical addr=0x%llX\n", _readExecuteRegion.buffer, _readExecuteRegion.buffer+_readExecuteRegion.bufferSize, _readExecuteRegion.unslidLoadAddress);
+ //fprintf(stderr, "RW region=%p -> %p, logical addr=0x%llX\n", _readWriteRegion.buffer, _readWriteRegion.buffer+_readWriteRegion.bufferSize, _readWriteRegion.unslidLoadAddress);
+ //fprintf(stderr, "RO region=%p -> %p, logical addr=0x%llX\n", _readOnlyRegion.buffer, _readOnlyRegion.buffer+_readOnlyRegion.bufferSize, _readOnlyRegion.unslidLoadAddress);
+
+ // sort SegmentMappingInfo for each image to be in the same order as original segments
+ for (DylibInfo& dylib : _sortedDylibs) {
+ std::sort(dylib.cacheLocation.begin(), dylib.cacheLocation.end(), [&](const SegmentMappingInfo& a, const SegmentMappingInfo& b) {
+ return a.srcSegmentIndex < b.srcSegmentIndex;
+ });
+ }
+}
+
+static dyld_cache_patchable_location makePatchLocation(size_t cacheOff, uint64_t ad) {
+ int64_t signedAddend = (int64_t)ad;
+ assert(((signedAddend << 52) >> 52) == signedAddend);
+ dyld_cache_patchable_location patch;
+ patch.cacheOffset = cacheOff;
+ patch.addend = ad;
+ patch.authenticated = 0;
+ patch.usesAddressDiversity = 0;
+ patch.key = 0;
+ patch.discriminator = 0;
+ return patch;
+}
+
+static dyld_cache_patchable_location makePatchLocation(size_t cacheOff, uint64_t ad,
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk authInfo) {
+ int64_t signedAddend = (int64_t)ad;
+ assert(((signedAddend << 52) >> 52) == signedAddend);
+ dyld_cache_patchable_location patch;
+ patch.cacheOffset = cacheOff;
+ patch.addend = ad;
+ patch.authenticated = authInfo.arm64e.authBind.auth;
+ patch.usesAddressDiversity = authInfo.arm64e.authBind.addrDiv;
+ patch.key = authInfo.arm64e.authBind.key;
+ patch.discriminator = authInfo.arm64e.authBind.diversity;
+ return patch;
+}
+
+void SharedCacheBuilder::buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases)
+{
+ typedef dyld3::closure::ClosureBuilder::CachedDylibInfo CachedDylibInfo;
+
+ // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray()
+ __block std::vector<CachedDylibInfo> dylibInfos;
+ __block std::unordered_map<dyld3::closure::ImageNum, const dyld3::MachOLoaded*> imageNumToML;
+ DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ cache->forEachImage(^(const mach_header* mh, const char* installName) {
+ uint64_t mtime;
+ uint64_t inode;
+ cache->getIndexedImageEntry((uint32_t)dylibInfos.size(), mtime, inode);
+ CachedDylibInfo entry;
+ entry.fileInfo.fileContent = mh;
+ entry.fileInfo.path = installName;
+ entry.fileInfo.sliceOffset = 0;
+ entry.fileInfo.inode = inode;
+ entry.fileInfo.mtime = mtime;
+ dylibInfos.push_back(entry);
+ imageNumToML[(dyld3::closure::ImageNum)(dylibInfos.size())] = (dyld3::MachOLoaded*)mh;
+ });
+
+ // Convert symlinks from STL to simple char pointers.
+ std::vector<dyld3::closure::ClosureBuilder::CachedDylibAlias> dylibAliases;
+ dylibAliases.reserve(aliases.size());
+ for (const auto& alias : aliases)
+ dylibAliases.push_back({ alias.realPath.c_str(), alias.aliasPath.c_str() });
+
+ dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers handlers;
+
+ handlers.chainedBind = ^(dyld3::closure::ImageNum, const dyld3::MachOLoaded* imageLoadAddress,
+ const dyld_chained_starts_in_image* starts,
+ const dyld3::Array<dyld3::closure::Image::ResolvedSymbolTarget>& targets,
+ const dyld3::Array<dyld3::closure::ClosureBuilder::ResolvedTargetInfo>& targetInfos) {
+ _someDylibsUsedChainedFixups = true;
+ imageLoadAddress->forEachFixupInAllChains(_diagnostics, starts, true, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+ uint64_t offsetInCache;
+ dyld3::closure::Image::ResolvedSymbolTarget target;
+ const dyld3::closure::ClosureBuilder::ResolvedTargetInfo* targetInfo;
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgValue;
+ uint64_t targetVMAddr;
+ uint64_t addend;
+ bool pointerIntoCache = true;
+ switch (segInfo->pointer_format) {
+ case DYLD_CHAINED_PTR_ARM64E:
+ orgValue = *fixupLoc;
+ if ( fixupLoc->arm64e.bind.bind ) {
+ target = targets[fixupLoc->arm64e.bind.ordinal];
+ targetInfo = &targetInfos[fixupLoc->arm64e.bind.ordinal];
+ switch ( target.sharedCache.kind ) {
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
+ offsetInCache = target.sharedCache.offset - targetInfo->addend;
+ _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache);
+ _exportsToName[offsetInCache] = targetInfo->foundSymbolName;
+ addend = targetInfo->addend;
+ {
+ uint16_t top16 = addend >> 48;
+ uint8_t top8 = addend >> 56;
+ // if top byte is non-zero and this is not a negative addend, pull off high8
+ if ( (top16 != 0xFFFF) && (top8 != 0) ) {
+ _aslrTracker.setHigh8(fixupLoc, top8);
+ addend &= 0x00FFFFFFFFFFFFFFULL;
+ }
+ }
+ targetVMAddr = _archLayout->sharedMemoryStart + target.sharedCache.offset;
+ if ( fixupLoc->arm64e.authBind.auth ) {
+ // store auth data in side table
+ _aslrTracker.setAuthData(fixupLoc, fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.key);
+ _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, addend, *fixupLoc));
+ }
+ else {
+ _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, addend));
+ // plain binds can have addend in chain
+ targetVMAddr += fixupLoc->arm64e.bind.addend;
+ }
+ // change location from a chain ptr into a raw pointer to the target vmaddr
+ fixupLoc->raw64 = targetVMAddr;
+ _aslrTracker.add(fixupLoc);
+ break;
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
+ fixupLoc->raw64 = target.absolute.value;
+ pointerIntoCache = false;
+ // don't record absolute targets for ASLR
+ _aslrTracker.remove(fixupLoc);
+ if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) {
+ _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1);
+ }
+ break;
+ default:
+ assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
+ }
+ }
+ else {
+ // convert rebase chain entry to raw pointer to target vmaddr
+ if ( fixupLoc->arm64e.rebase.auth ) {
+ // store auth data in side table
+ _aslrTracker.setAuthData(fixupLoc, fixupLoc->arm64e.authRebase.diversity, fixupLoc->arm64e.authRebase.addrDiv, fixupLoc->arm64e.authRebase.key);
+ targetVMAddr = fixupLoc->arm64e.authRebase.target;
+ fixupLoc->raw64 = targetVMAddr;
+ }
+ else {
+ targetVMAddr = fixupLoc->arm64e.rebase.target;
+ if ( targetVMAddr == CacheBuilder::kRebaseTargetInSideTableArm64e ) {
+ // target was stored in side table
+ _aslrTracker.hasRebaseTarget64(fixupLoc, &targetVMAddr);
+ }
+ // store high8 in side table
+ if ( fixupLoc->arm64e.rebase.high8 )
+ _aslrTracker.setHigh8(fixupLoc, fixupLoc->arm64e.rebase.high8);
+ fixupLoc->raw64 = targetVMAddr;
+ }
+ }
+ if ( pointerIntoCache ) {
+ assert(fixupLoc->raw64 > _readExecuteRegion.unslidLoadAddress);
+ assert(fixupLoc->raw64 < _readOnlyRegion.unslidLoadAddress+_readOnlyRegion.sizeInUse);
+ }
+ break;
+ case DYLD_CHAINED_PTR_64:
+ if ( fixupLoc->generic64.bind.bind ) {
+ target = targets[fixupLoc->generic64.bind.ordinal];
+ targetInfo = &targetInfos[fixupLoc->generic64.bind.ordinal];
+ switch ( target.sharedCache.kind ) {
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
+ offsetInCache = target.sharedCache.offset - targetInfo->addend;
+ _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache);
+ _exportsToName[offsetInCache] = targetInfo->foundSymbolName;
+ addend = targetInfo->addend + fixupLoc->generic64.bind.addend;
+ {
+ uint16_t top16 = addend >> 48;
+ uint8_t top8 = addend >> 56;
+ // if top byte is non-zero and this is not a negative addend, pull off high8
+ if ( (top16 != 0xFFFF) && (top8 != 0) ) {
+ _aslrTracker.setHigh8(fixupLoc, top8);
+ addend &= 0x00FFFFFFFFFFFFFFULL;
+ }
+ }
+ // turn this bind into a flat vmaddr
+ fixupLoc->raw64 = _archLayout->sharedMemoryStart + target.sharedCache.offset + fixupLoc->generic64.bind.addend;
+ _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, addend));
+ _aslrTracker.add(fixupLoc);
+ break;
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
+ fixupLoc->raw64 = target.absolute.value;
+ pointerIntoCache = false;
+ // don't record absolute targets for ASLR
+ _aslrTracker.remove(fixupLoc);
+ if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) {
+ _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1);
+ }
+ break;
+ default:
+ assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
+ }
+ }
+ else {
+ // convert rebase chain entry to raw pointer to target vmaddr
+ targetVMAddr = fixupLoc->generic64.rebase.target;
+ if ( targetVMAddr == CacheBuilder::kRebaseTargetInSideTableArm64 ) {
+ // target was stored in side table
+ _aslrTracker.hasRebaseTarget64(fixupLoc, &targetVMAddr);
+ }
+ // store high8 in side table
+ if ( fixupLoc->generic64.rebase.high8 )
+ _aslrTracker.setHigh8(fixupLoc, fixupLoc->generic64.rebase.high8);
+ fixupLoc->raw64 = targetVMAddr;
+ }
+ if ( pointerIntoCache ) {
+ assert(fixupLoc->raw64 > _readExecuteRegion.unslidLoadAddress);
+ assert(fixupLoc->raw64 < _readOnlyRegion.unslidLoadAddress+_readOnlyRegion.sizeInUse);
+ }
+ break;
+ case DYLD_CHAINED_PTR_32:
+ if ( fixupLoc->generic32.bind.bind ) {
+ // turn this bind into a flat vmaddr pointer
+ target = targets[fixupLoc->generic32.bind.ordinal];
+ targetInfo = &targetInfos[fixupLoc->generic32.bind.ordinal];
+ switch ( target.sharedCache.kind ) {
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
+ offsetInCache = target.sharedCache.offset - targetInfo->addend;
+ _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache);
+ _exportsToName[offsetInCache] = targetInfo->foundSymbolName;
+ fixupLoc->raw32 = (uint32_t)(_archLayout->sharedMemoryStart + target.sharedCache.offset + fixupLoc->generic32.bind.addend);
+ _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend+fixupLoc->generic32.bind.addend));
+ _aslrTracker.add(fixupLoc);
+ break;
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
+ fixupLoc->raw32 = (uint32_t)target.absolute.value;
+ pointerIntoCache = false;
+ // don't record absolute targets for ASLR
+ _aslrTracker.remove(fixupLoc);
+ if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) {
+ _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1);
+ }
+ break;
+ default:
+ assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
+ }
+ }
+ else if ( fixupLoc->generic32.rebase.target == CacheBuilder::kRebaseTargetInSideTableGeneric32 ) {
+ uint32_t targetVmAddr;
+ if ( _aslrTracker.hasRebaseTarget32(fixupLoc, &targetVmAddr) )
+ fixupLoc->raw32 = targetVmAddr;
+ else
+ assert(0 && "missing target for rebase");
+ }
+ else if ( fixupLoc->generic32.rebase.target > segInfo->max_valid_pointer ) {
+ __block const char* badImagePath = nullptr;
+ cache->forEachImage(^(const mach_header* mh, const char* installName) {
+ if ( mh == imageLoadAddress )
+ badImagePath = installName;
+ });
+ _diagnostics.error("unexpected non-pointer in chain for image at %s", badImagePath);
+ stop = true;
+ pointerIntoCache = false;
+ }
+ if ( pointerIntoCache ) {
+ assert(fixupLoc->raw32 > _readExecuteRegion.unslidLoadAddress);
+ assert(fixupLoc->raw32 < _readOnlyRegion.unslidLoadAddress+_readOnlyRegion.sizeInUse);
+ }
+ break;
+ case DYLD_CHAINED_PTR_64_OFFSET:
+ case DYLD_CHAINED_PTR_ARM64E_OFFSET:
+ assert(0 && "unsupported chained bind type");
+ break;
+ default:
+ assert(0 && "unsupported chained bind type");
+ }
+
+ });
+ };
+
+ handlers.rebase = ^(dyld3::closure::ImageNum imageNum, const dyld3::MachOLoaded* imageToFix, uint32_t runtimeOffset) {
+ // record location in aslr tracker so kernel can slide this on page-in
+ uint8_t* fixupLoc = (uint8_t*)imageToFix+runtimeOffset;
+ _aslrTracker.add(fixupLoc);
+ };
+
+ handlers.bind = ^(dyld3::closure::ImageNum imageNum, const dyld3::MachOLoaded* mh,
+ uint32_t runtimeOffset, dyld3::closure::Image::ResolvedSymbolTarget target,
+ const dyld3::closure::ClosureBuilder::ResolvedTargetInfo& targetInfo) {
+ uint8_t* fixupLoc = (uint8_t*)mh+runtimeOffset;
+
+ // binder is called a second time for weak_bind info, which we ignore when building cache
+ const bool weakDefUseAlreadySet = targetInfo.weakBindCoalese && _aslrTracker.has(fixupLoc);
+
+ // do actual bind that sets pointer in image to unslid target address
+ uint64_t offsetInCache;
+ switch ( target.sharedCache.kind ) {
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
+ offsetInCache = target.sharedCache.offset - targetInfo.addend;
+ _dylibToItsExports[targetInfo.foundInDylib].insert(offsetInCache);
+ if (targetInfo.isWeakDef)
+ _dylibWeakExports.insert({ targetInfo.foundInDylib, offsetInCache });
+ _exportsToUses[offsetInCache].push_back(makePatchLocation(fixupLoc - _readExecuteRegion.buffer, targetInfo.addend));
+ _exportsToName[offsetInCache] = targetInfo.foundSymbolName;
+ if ( !weakDefUseAlreadySet ) {
+ if ( _archLayout->is64 )
+ *((uint64_t*)fixupLoc) = _archLayout->sharedMemoryStart + target.sharedCache.offset;
+ else
+ *((uint32_t*)fixupLoc) = (uint32_t)(_archLayout->sharedMemoryStart + target.sharedCache.offset);
+ // record location in aslr tracker so kernel can slide this on page-in
+ _aslrTracker.add(fixupLoc);
+ }
+ break;
+ case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
+ if ( _archLayout->is64 )
+ *((uint64_t*)fixupLoc) = target.absolute.value;
+ else
+ *((uint32_t*)fixupLoc) = (uint32_t)(target.absolute.value);
+ // don't record absolute targets for ASLR
+ // HACK: Split seg may have added a target. Remove it
+ _aslrTracker.remove(fixupLoc);
+ if ( (targetInfo.libOrdinal > 0) && (targetInfo.libOrdinal <= (int)(mh->dependentDylibCount())) ) {
+ _missingWeakImports[fixupLoc] = mh->dependentDylibLoadPath(targetInfo.libOrdinal - 1);
+ }
+ break;
+ default:
+ assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
+ }
+ };
+
+ // build ImageArray for all dylibs in dyld cache
+ dyld3::closure::PathOverrides pathOverrides;
+ dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstDyldCacheImageNum, _fileSystem, cache, false, *_options.archs, pathOverrides,
+ dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform, &handlers);
+ dyld3::Array<CachedDylibInfo> dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size());
+ const dyld3::Array<dyld3::closure::ClosureBuilder::CachedDylibAlias> aliasesArray(dylibAliases.data(), dylibAliases.size(), dylibAliases.size());
+ _imageArray = cb.makeDyldCacheImageArray(_options.optimizeStubs, dylibs, aliasesArray);
+ if ( cb.diagnostics().hasError() ) {
+ _diagnostics.error("%s", cb.diagnostics().errorMessage().c_str());
+ return;
+ }
+
+}
+
+static bool operator==(const dyld_cache_patchable_location& a, const dyld_cache_patchable_location& b) {
+ return a.cacheOffset == b.cacheOffset;
+}
+
+void SharedCacheBuilder::addImageArray()
+{
+ // build trie of dylib paths
+ __block std::vector<DylibIndexTrie::Entry> dylibEntrys;
+ _imageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+ dylibEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum()-1)));
+ image->forEachAlias(^(const char *aliasPath, bool &innerStop) {
+ dylibEntrys.push_back(DylibIndexTrie::Entry(aliasPath, DylibIndex(image->imageNum()-1)));
+ });
+ });
+ DylibIndexTrie dylibsTrie(dylibEntrys);
+ std::vector<uint8_t> trieBytes;
+ dylibsTrie.emit(trieBytes);
+ while ( (trieBytes.size() % 4) != 0 )
+ trieBytes.push_back(0);
+
+ // build set of functions to never stub-eliminate because tools may need to override them
+ std::unordered_set<std::string> alwaysGeneratePatch;
+ for (const char* const* p=_s_neverStubEliminateSymbols; *p != nullptr; ++p)
+ alwaysGeneratePatch.insert(*p);
+
+ // Add the patches for the image array.
+ __block uint64_t numPatchImages = _imageArray->size();
+ __block uint64_t numPatchExports = 0;
+ __block uint64_t numPatchLocations = 0;
+ __block uint64_t numPatchExportNameBytes = 0;
+
+ auto needsPatch = [&](bool dylibNeedsPatching, const dyld3::MachOLoaded* mh,
+ CacheOffset offset) -> bool {
+ if (dylibNeedsPatching)
+ return true;
+ if (_dylibWeakExports.find({ mh, offset }) != _dylibWeakExports.end())
+ return true;
+ const std::string& exportName = _exportsToName[offset];
+ return alwaysGeneratePatch.find(exportName) != alwaysGeneratePatch.end();
+ };
+
+ std::set<std::string> alwaysPatchDylibs;
+ for (const char* const* d= _s_neverStubEliminateDylibs; *d != nullptr; ++d)
+ alwaysPatchDylibs.insert(*d);
+
+ // First calculate how much space we need
+ const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ cache->forEachImage(^(const mach_header* mh, const char* installName) {
+ const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh;
+ const std::set<CacheOffset>& dylibExports = _dylibToItsExports[ml];
+
+ // On a customer cache, only store patch locations for interposable dylibs and weak binding
+ bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName);
+
+ uint64_t numDylibExports = 0;
+ for (CacheOffset exportCacheOffset : dylibExports) {
+ if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset))
+ continue;
+ std::vector<dyld_cache_patchable_location>& uses = _exportsToUses[exportCacheOffset];
+ uses.erase(std::unique(uses.begin(), uses.end()), uses.end());
+ numPatchLocations += uses.size();
+
+ std::string exportName = _exportsToName[exportCacheOffset];
+ numPatchExportNameBytes += exportName.size() + 1;
+ }
+ numPatchExports += numDylibExports;
+ });
+
+ // Now reserve the space
+ __block std::vector<dyld_cache_image_patches> patchImages;
+ __block std::vector<dyld_cache_patchable_export> patchExports;
+ __block std::vector<dyld_cache_patchable_location> patchLocations;
+ __block std::vector<char> patchExportNames;
+
+ patchImages.reserve(numPatchImages);
+ patchExports.reserve(numPatchExports);
+ patchLocations.reserve(numPatchLocations);
+ patchExportNames.reserve(numPatchExportNameBytes);
+
+ // And now fill it with the patch data
+ cache->forEachImage(^(const mach_header* mh, const char* installName) {
+ const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh;
+ const std::set<CacheOffset>& dylibExports = _dylibToItsExports[ml];
+
+ // On a customer cache, only store patch locations for interposable dylibs and weak binding
+ bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName);
+
+ // Add the patch image which points in to the exports
+ dyld_cache_image_patches patchImage;
+ patchImage.patchExportsStartIndex = (uint32_t)patchExports.size();
+ patchImage.patchExportsCount = 0;
+
+ // Then add each export which points to a list of locations and a name
+ for (CacheOffset exportCacheOffset : dylibExports) {
+ if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset))
+ continue;
+ ++patchImage.patchExportsCount;
+ std::vector<dyld_cache_patchable_location>& uses = _exportsToUses[exportCacheOffset];
+
+ dyld_cache_patchable_export cacheExport;
+ cacheExport.cacheOffsetOfImpl = (uint32_t)exportCacheOffset;
+ cacheExport.patchLocationsStartIndex = (uint32_t)patchLocations.size();
+ cacheExport.patchLocationsCount = (uint32_t)uses.size();
+ cacheExport.exportNameOffset = (uint32_t)patchExportNames.size();
+ patchExports.push_back(cacheExport);
+
+ // Now add the list of locations.
+ patchLocations.insert(patchLocations.end(), uses.begin(), uses.end());
+
+ // And add the export name
+ const std::string& exportName = _exportsToName[exportCacheOffset];
+ patchExportNames.insert(patchExportNames.end(), &exportName[0], &exportName[0] + exportName.size() + 1);
+ }
+ patchImages.push_back(patchImage);
+ });
+
+ while ( (patchExportNames.size() % 4) != 0 )
+ patchExportNames.push_back('\0');
+
+ uint64_t patchInfoSize = sizeof(dyld_cache_patch_info);
+ patchInfoSize += sizeof(dyld_cache_image_patches) * patchImages.size();
+ patchInfoSize += sizeof(dyld_cache_patchable_export) * patchExports.size();
+ patchInfoSize += sizeof(dyld_cache_patchable_location) * patchLocations.size();
+ patchInfoSize += patchExportNames.size();
+
+ // check for fit
+ uint64_t imageArraySize = _imageArray->size();
+ size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+ if ( (imageArraySize+trieBytes.size()+patchInfoSize) > freeSpace ) {
+ _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, patch size=%lluKB, free space=%ldMB)",
+ _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, patchInfoSize/1024, freeSpace/1024/1024);
+ return;
+ }
+
+ // copy into cache and update header
+ DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ dyldCache->header.dylibsImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+ dyldCache->header.dylibsImageArraySize = imageArraySize;
+ dyldCache->header.dylibsTrieAddr = dyldCache->header.dylibsImageArrayAddr + imageArraySize;
+ dyldCache->header.dylibsTrieSize = trieBytes.size();
+ ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, _imageArray, imageArraySize);
+ ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
+
+ // Also write out the patch info
+ dyldCache->header.patchInfoAddr = dyldCache->header.dylibsTrieAddr + dyldCache->header.dylibsTrieSize;
+ dyldCache->header.patchInfoSize = patchInfoSize;
+ dyld_cache_patch_info patchInfo;
+ patchInfo.patchTableArrayAddr = dyldCache->header.patchInfoAddr + sizeof(dyld_cache_patch_info);
+ patchInfo.patchTableArrayCount = patchImages.size();
+ patchInfo.patchExportArrayAddr = patchInfo.patchTableArrayAddr + (patchInfo.patchTableArrayCount * sizeof(dyld_cache_image_patches));
+ patchInfo.patchExportArrayCount = patchExports.size();
+ patchInfo.patchLocationArrayAddr = patchInfo.patchExportArrayAddr + (patchInfo.patchExportArrayCount * sizeof(dyld_cache_patchable_export));
+ patchInfo.patchLocationArrayCount = patchLocations.size();
+ patchInfo.patchExportNamesAddr = patchInfo.patchLocationArrayAddr + (patchInfo.patchLocationArrayCount * sizeof(dyld_cache_patchable_location));
+ patchInfo.patchExportNamesSize = patchExportNames.size();
+ ::memcpy(_readOnlyRegion.buffer + dyldCache->header.patchInfoAddr - _readOnlyRegion.unslidLoadAddress,
+ &patchInfo, sizeof(dyld_cache_patch_info));
+ ::memcpy(_readOnlyRegion.buffer + patchInfo.patchTableArrayAddr - _readOnlyRegion.unslidLoadAddress,
+ &patchImages[0], sizeof(patchImages[0]) * patchImages.size());
+ ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportArrayAddr - _readOnlyRegion.unslidLoadAddress,
+ &patchExports[0], sizeof(patchExports[0]) * patchExports.size());
+ ::memcpy(_readOnlyRegion.buffer + patchInfo.patchLocationArrayAddr - _readOnlyRegion.unslidLoadAddress,
+ &patchLocations[0], sizeof(patchLocations[0]) * patchLocations.size());
+ ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportNamesAddr - _readOnlyRegion.unslidLoadAddress,
+ &patchExportNames[0], patchExportNames.size());
+
+ _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size()+patchInfoSize,14);
+
+ // Free the underlying image array buffer
+ _imageArray->deallocate();
+}
+
+void SharedCacheBuilder::addOtherImageArray(const std::vector<LoadedMachO>& otherDylibsAndBundles, std::vector<const LoadedMachO*>& overflowDylibs)
+{
+ DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ dyld3::closure::PathOverrides pathOverrides;
+ dyld3::closure::FileSystemNull nullFileSystem;
+ dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstOtherOSImageNum, nullFileSystem, cache, false, *_options.archs, pathOverrides,
+ dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform);
+
+ // make ImageArray for other dylibs and bundles
+ STACK_ALLOC_ARRAY(dyld3::closure::LoadedFileInfo, others, otherDylibsAndBundles.size() + overflowDylibs.size());
+ for (const LoadedMachO& other : otherDylibsAndBundles) {
+ if ( !contains(other.loadedFileInfo.path, "staged_system_apps/") )
+ others.push_back(other.loadedFileInfo);
+ }
+
+ for (const LoadedMachO* dylib : overflowDylibs) {
+ if (dylib->mappedFile.mh->canHavePrecomputedDlopenClosure(dylib->mappedFile.runtimePath.c_str(), ^(const char*) {}) )
+ others.push_back(dylib->loadedFileInfo);
+ }
+
+ // Sort the others array by name so that it is deterministic
+ std::sort(others.begin(), others.end(),
+ [](const dyld3::closure::LoadedFileInfo& a, const dyld3::closure::LoadedFileInfo& b) {
+ // Sort mac before iOSMac
+ bool isIOSMacA = strncmp(a.path, "/System/iOSSupport/", 19) == 0;
+ bool isIOSMacB = strncmp(b.path, "/System/iOSSupport/", 19) == 0;
+ if (isIOSMacA != isIOSMacB)
+ return !isIOSMacA;
+ return strcmp(a.path, b.path) < 0;
+ });
+
+ const dyld3::closure::ImageArray* otherImageArray = cb.makeOtherDylibsImageArray(others, (uint32_t)_sortedDylibs.size());
+
+ // build trie of paths
+ __block std::vector<DylibIndexTrie::Entry> otherEntrys;
+ otherImageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+ if ( !image->isInvalid() )
+ otherEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum())));
+ });
+ DylibIndexTrie dylibsTrie(otherEntrys);
+ std::vector<uint8_t> trieBytes;
+ dylibsTrie.emit(trieBytes);
+ while ( (trieBytes.size() % 4) != 0 )
+ trieBytes.push_back(0);
+
+ // check for fit
+ uint64_t imageArraySize = otherImageArray->size();
+ size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+ if ( imageArraySize+trieBytes.size() > freeSpace ) {
+ _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, free space=%ldMB)",
+ _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, freeSpace/1024/1024);
+ return;
+ }
+
+ // copy into cache and update header
+ DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ dyldCache->header.otherImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+ dyldCache->header.otherImageArraySize = imageArraySize;
+ dyldCache->header.otherTrieAddr = dyldCache->header.otherImageArrayAddr + imageArraySize;
+ dyldCache->header.otherTrieSize = trieBytes.size();
+ ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, otherImageArray, imageArraySize);
+ ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
+ _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size(),14);
+
+ // Free the underlying buffer
+ otherImageArray->deallocate();
+}
+
+
+void SharedCacheBuilder::addClosures(const std::vector<LoadedMachO>& osExecutables)
+{
+ const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+
+ __block std::vector<Diagnostics> osExecutablesDiags;
+ __block std::vector<const dyld3::closure::LaunchClosure*> osExecutablesClosures;
+ osExecutablesDiags.resize(osExecutables.size());
+ osExecutablesClosures.resize(osExecutables.size());
+
+ dispatch_apply(osExecutables.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+ const LoadedMachO& loadedMachO = osExecutables[index];
+ // don't pre-build closures for staged apps into dyld cache, since they won't run from that location
+ if ( startsWith(loadedMachO.mappedFile.runtimePath, "/private/var/staged_system_apps/") ) {
+ return;
+ }
+ dyld3::closure::PathOverrides pathOverrides;
+ dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, _fileSystem, dyldCache, false, *_options.archs, pathOverrides,
+ dyld3::closure::ClosureBuilder::AtPath::all, false, nullptr, _options.platform, nullptr);
+ bool issetuid = false;
+ if ( this->_options.platform == dyld3::Platform::macOS || dyld3::MachOFile::isSimulatorPlatform(this->_options.platform) )
+ _fileSystem.fileExists(loadedMachO.loadedFileInfo.path, nullptr, nullptr, &issetuid, nullptr);
+ const dyld3::closure::LaunchClosure* mainClosure = builder.makeLaunchClosure(loadedMachO.loadedFileInfo, issetuid);
+ if ( builder.diagnostics().hasError() ) {
+ osExecutablesDiags[index].error("%s", builder.diagnostics().errorMessage().c_str());
+ }
+ else {
+ assert(mainClosure != nullptr);
+ osExecutablesClosures[index] = mainClosure;
+ }
+ });
+
+ std::map<std::string, const dyld3::closure::LaunchClosure*> closures;
+ for (uint64_t i = 0, e = osExecutables.size(); i != e; ++i) {
+ const LoadedMachO& loadedMachO = osExecutables[i];
+ const Diagnostics& diag = osExecutablesDiags[i];
+ if (diag.hasError()) {
+ if ( _options.verbose ) {
+ _diagnostics.warning("building closure for '%s': %s", loadedMachO.mappedFile.runtimePath.c_str(), diag.errorMessage().c_str());
+ for (const std::string& warn : diag.warnings() )
+ _diagnostics.warning("%s", warn.c_str());
+ }
+ if ( loadedMachO.inputFile && (loadedMachO.inputFile->mustBeIncluded()) ) {
+ loadedMachO.inputFile->diag.error("%s", diag.errorMessage().c_str());
+ }
+ } else {
+ // Note, a closure could be null here if it has a path we skip.
+ if (osExecutablesClosures[i] != nullptr)
+ closures[loadedMachO.mappedFile.runtimePath] = osExecutablesClosures[i];
+ }
+ }
+
+ osExecutablesDiags.clear();
+ osExecutablesClosures.clear();
+
+ // preflight space needed
+ size_t closuresSpace = 0;
+ for (const auto& entry : closures) {
+ closuresSpace += entry.second->size();
+ }
+ size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+ if ( closuresSpace > freeSpace ) {
+ _diagnostics.error("cache buffer too small to hold all closures (buffer size=%lldMB, closures size=%ldMB, free space=%ldMB)",
+ _allocatedBufferSize/1024/1024, closuresSpace/1024/1024, freeSpace/1024/1024);
+ return;
+ }
+ DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ cache->header.progClosuresAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+ uint8_t* closuresBase = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse;
+ std::vector<DylibIndexTrie::Entry> closureEntrys;
+ uint32_t currentClosureOffset = 0;
+ for (const auto& entry : closures) {
+ const dyld3::closure::LaunchClosure* closure = entry.second;
+ closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset)));
+ size_t size = closure->size();
+ assert((size % 4) == 0);
+ memcpy(closuresBase+currentClosureOffset, closure, size);
+ currentClosureOffset += size;
+ freeSpace -= size;
+ closure->deallocate();
+ }
+ cache->header.progClosuresSize = currentClosureOffset;
+ _readOnlyRegion.sizeInUse += currentClosureOffset;
+ freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+ // build trie of indexes into closures list
+ DylibIndexTrie closureTrie(closureEntrys);
+ std::vector<uint8_t> trieBytes;
+ closureTrie.emit(trieBytes);
+ while ( (trieBytes.size() % 8) != 0 )
+ trieBytes.push_back(0);
+ if ( trieBytes.size() > freeSpace ) {
+ _diagnostics.error("cache buffer too small to hold all closures trie (buffer size=%lldMB, trie size=%ldMB, free space=%ldMB)",
+ _allocatedBufferSize/1024/1024, trieBytes.size()/1024/1024, freeSpace/1024/1024);
+ return;
+ }
+ memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, &trieBytes[0], trieBytes.size());
+ cache->header.progClosuresTrieAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+ cache->header.progClosuresTrieSize = trieBytes.size();
+ _readOnlyRegion.sizeInUse += trieBytes.size();
+ _readOnlyRegion.sizeInUse = align(_readOnlyRegion.sizeInUse, 14);
+}
+
+
+bool SharedCacheBuilder::writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset))
+{
+ const dyld_cache_header* cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + cacheHeader->mappingOffset);
+ assert(_readExecuteRegion.sizeInUse == mappings[0].size);
+ assert(_readWriteRegion.sizeInUse == mappings[1].size);
+ assert(_readOnlyRegion.sizeInUse == mappings[2].size);
+ assert(_readExecuteRegion.cacheFileOffset == mappings[0].fileOffset);
+ assert(_readWriteRegion.cacheFileOffset == mappings[1].fileOffset);
+ assert(_readOnlyRegion.cacheFileOffset == mappings[2].fileOffset);
+ assert(_codeSignatureRegion.sizeInUse == cacheHeader->codeSignatureSize);
+ assert(cacheHeader->codeSignatureOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse);
+ cacheSizeCallback(_readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse+_codeSignatureRegion.sizeInUse);
+ bool fullyWritten = copyCallback(_readExecuteRegion.buffer, _readExecuteRegion.sizeInUse, mappings[0].fileOffset);
+ fullyWritten &= copyCallback(_readWriteRegion.buffer, _readWriteRegion.sizeInUse, mappings[1].fileOffset);
+ fullyWritten &= copyCallback(_readOnlyRegion.buffer, _readOnlyRegion.sizeInUse, mappings[2].fileOffset);
+ if ( _localSymbolsRegion.sizeInUse != 0 ) {
+ assert(cacheHeader->localSymbolsOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse);
+ fullyWritten &= copyCallback(_localSymbolsRegion.buffer, _localSymbolsRegion.sizeInUse, cacheHeader->localSymbolsOffset);
+ }
+ fullyWritten &= copyCallback(_codeSignatureRegion.buffer, _codeSignatureRegion.sizeInUse, cacheHeader->codeSignatureOffset);
+ return fullyWritten;
+}
+
+
+void SharedCacheBuilder::writeFile(const std::string& path)
+{
+ std::string pathTemplate = path + "-XXXXXX";
+ size_t templateLen = strlen(pathTemplate.c_str())+2;
+ BLOCK_ACCCESSIBLE_ARRAY(char, pathTemplateSpace, templateLen);
+ strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen);
+ int fd = mkstemp(pathTemplateSpace);
+ if ( fd != -1 ) {
+ auto cacheSizeCallback = ^(uint64_t size) {
+ // if making macOS dyld cache for current OS into standard location
+ if ( (_options.platform == dyld3::Platform::macOS) && startsWith(path, MACOSX_DYLD_SHARED_CACHE_DIR) ) {
+ // <rdar://48687550> pin cache file to SSD on fusion drives
+ apfs_data_pin_location_t where = APFS_PIN_DATA_TO_MAIN;
+ ::fsctl(pathTemplateSpace, APFSIOC_PIN_DATA, &where, 0);
+ }
+ // set final cache file size (may help defragment file)
+ ::ftruncate(fd, size);
+ };
+ auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
+ uint64_t writtenSize = pwrite(fd, src, size, dstOffset);
+ return writtenSize == size;
+ };
+ // <rdar://problem/55370916> TOCTOU: verify path is still a realpath (not changed)
+ char tempPath[MAXPATHLEN];
+ if ( ::fcntl(fd, F_GETPATH, tempPath) == 0 ) {
+ size_t tempPathLen = strlen(tempPath);
+ if ( tempPathLen > 7 )
+ tempPath[tempPathLen-7] = '\0'; // remove trailing -xxxxxx
+ if ( path != tempPath ) {
+ _diagnostics.error("output file path changed from: '%s' to: '%s'", path.c_str(), tempPath);
+ ::close(fd);
+ return;
+ }
+ }
+ else {
+ _diagnostics.error("unable to fcntl(fd, F_GETPATH) on output file");
+ ::close(fd);
+ return;
+ }
+ bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
+ if ( fullyWritten ) {
+ ::fchmod(fd, S_IRUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "r--r--r--"
+ // <rdar://problem/55370916> TOCTOU: verify path is still a realpath (not changed)
+ char resolvedPath[PATH_MAX];
+ ::realpath(path.c_str(), resolvedPath);
+ // Note: if the target cache file does not already exist, realpath() will return NULL, but still fill in the path buffer
+ if ( path != resolvedPath ) {
+ _diagnostics.error("output file path changed from: '%s' to: '%s'", path.c_str(), resolvedPath);
+ return;
+ }
+ if ( ::rename(pathTemplateSpace, path.c_str()) == 0) {
+ ::close(fd);
+ return; // success
+ }
+ }
+ else {
+ _diagnostics.error("could not write file %s", pathTemplateSpace);
+ }
+ ::close(fd);
+ ::unlink(pathTemplateSpace);
+ }
+ else {
+ _diagnostics.error("could not open file %s", pathTemplateSpace);
+ }
+}
+
+void SharedCacheBuilder::writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) {
+ auto cacheSizeCallback = ^(uint64_t size) {
+ buffer = (uint8_t*)malloc(size);
+ bufferSize = size;
+ };
+ auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
+ memcpy(buffer + dstOffset, src, size);
+ return true;
+ };
+ bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
+ assert(fullyWritten);
+}
+
+void SharedCacheBuilder::writeMapFile(const std::string& path)
+{
+ const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ std::string mapContent = cache->mapFile();
+ safeSave(mapContent.c_str(), mapContent.size(), path);
+}
+
+std::string SharedCacheBuilder::getMapFileBuffer(const std::string& cacheDisposition) const
+{
+ const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ return cache->generateJSONMap(cacheDisposition.c_str());
+}
+
+void SharedCacheBuilder::markPaddingInaccessible()
+{
+ // region between RX and RW
+ uint8_t* startPad1 = _readExecuteRegion.buffer+_readExecuteRegion.sizeInUse;
+ uint8_t* endPad1 = _readWriteRegion.buffer;
+ ::vm_protect(mach_task_self(), (vm_address_t)startPad1, endPad1-startPad1, false, 0);
+
+ // region between RW and RO
+ uint8_t* startPad2 = _readWriteRegion.buffer+_readWriteRegion.sizeInUse;
+ uint8_t* endPad2 = _readOnlyRegion.buffer;
+ ::vm_protect(mach_task_self(), (vm_address_t)startPad2, endPad2-startPad2, false, 0);
+}
+
+
+void SharedCacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) {
+ for (const DylibInfo& dylibInfo : _sortedDylibs)
+ callback(dylibInfo.runtimePath);
+}
+
+
+uint64_t SharedCacheBuilder::pathHash(const char* path)
+{
+ uint64_t sum = 0;
+ for (const char* s=path; *s != '\0'; ++s)
+ sum += sum*4 + *s;
+ return sum;
+}
+
+
+void SharedCacheBuilder::findDylibAndSegment(const void* contentPtr, std::string& foundDylibName, std::string& foundSegName)
+{
+ foundDylibName = "???";
+ foundSegName = "???";
+ uint64_t unslidVmAddr = ((uint8_t*)contentPtr - _readExecuteRegion.buffer) + _readExecuteRegion.unslidLoadAddress;
+ const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ cache->forEachImage(^(const mach_header* mh, const char* installName) {
+ ((dyld3::MachOLoaded*)mh)->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool &stop) {
+ if ( (unslidVmAddr >= info.vmAddr) && (unslidVmAddr < (info.vmAddr+info.vmSize)) ) {
+ foundDylibName = installName;
+ foundSegName = info.segName;
+ stop = true;
+ }
+ });
+ });
+}
+
+
+void SharedCacheBuilder::fipsSign()
+{
+ // find libcorecrypto.dylib in cache being built
+ DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+ __block const dyld3::MachOLoaded* ml = nullptr;
+ dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+ if ( strcmp(installName, "/usr/lib/system/libcorecrypto.dylib") == 0 )
+ ml = (dyld3::MachOLoaded*)mh;
+ });
+ if ( ml == nullptr ) {
+ _diagnostics.warning("Could not find libcorecrypto.dylib, skipping FIPS sealing");
+ return;
+ }
+
+ // find location in libcorecrypto.dylib to store hash of __text section
+ uint64_t hashStoreSize;
+ const void* hashStoreLocation = ml->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize);
+ if ( hashStoreLocation == nullptr ) {
+ _diagnostics.warning("Could not find __TEXT/__fips_hmacs section in libcorecrypto.dylib, skipping FIPS sealing");
+ return;
+ }
+ if ( hashStoreSize != 32 ) {
+ _diagnostics.warning("__TEXT/__fips_hmacs section in libcorecrypto.dylib is not 32 bytes in size, skipping FIPS sealing");
+ return;
+ }
+
+ // compute hmac hash of __text section
+ uint64_t textSize;
+ const void* textLocation = ml->findSectionContent("__TEXT", "__text", textSize);
+ if ( textLocation == nullptr ) {
+ _diagnostics.warning("Could not find __TEXT/__text section in libcorecrypto.dylib, skipping FIPS sealing");
+ return;
+ }
+ unsigned char hmac_key = 0;
+ CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, textLocation, textSize, (void*)hashStoreLocation); // store hash directly into hashStoreLocation
+}
+
+void SharedCacheBuilder::codeSign()
+{
+ uint8_t dscHashType;
+ uint8_t dscHashSize;
+ uint32_t dscDigestFormat;
+ bool agile = false;
+
+ // select which codesigning hash
+ switch (_options.codeSigningDigestMode) {
+ case DyldSharedCache::Agile:
+ agile = true;
+ // Fall through to SHA1, because the main code directory remains SHA1 for compatibility.
+ [[clang::fallthrough]];
+ case DyldSharedCache::SHA1only:
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ dscHashType = CS_HASHTYPE_SHA1;
+ dscHashSize = CS_HASH_SIZE_SHA1;
+ dscDigestFormat = kCCDigestSHA1;
+#pragma clang diagnostic pop
+ break;
+ case DyldSharedCache::SHA256only:
+ dscHashType = CS_HASHTYPE_SHA256;
+ dscHashSize = CS_HASH_SIZE_SHA256;
+ dscDigestFormat = kCCDigestSHA256;
+ break;
+ default:
+ _diagnostics.error("codeSigningDigestMode has unknown, unexpected value %d, bailing out.",
+ _options.codeSigningDigestMode);
+ return;
+ }
+
+ std::string cacheIdentifier = "com.apple.dyld.cache.";
+ cacheIdentifier += _options.archs->name();
+ if ( _options.dylibsRemovedDuringMastering ) {
+ if ( _options.optimizeStubs )
+ cacheIdentifier += ".release";
+ else
+ cacheIdentifier += ".development";
+ }
+ // get pointers into shared cache buffer
+ size_t inBbufferSize = _readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse;
+
+ const uint16_t pageSize = _archLayout->csPageSize;
+
+ // layout code signature contents
+ uint32_t blobCount = agile ? 4 : 3;
+ size_t idSize = cacheIdentifier.size()+1; // +1 for terminating 0
+ uint32_t slotCount = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize);
+ uint32_t xSlotCount = CSSLOT_REQUIREMENTS;
+ size_t idOffset = offsetof(CS_CodeDirectory, end_withExecSeg);
+ size_t hashOffset = idOffset+idSize + dscHashSize*xSlotCount;
+ size_t hash256Offset = idOffset+idSize + CS_HASH_SIZE_SHA256*xSlotCount;
+ size_t cdSize = hashOffset + (slotCount * dscHashSize);
+ size_t cd256Size = agile ? hash256Offset + (slotCount * CS_HASH_SIZE_SHA256) : 0;
+ size_t reqsSize = 12;
+ size_t cmsSize = sizeof(CS_Blob);
+ size_t cdOffset = sizeof(CS_SuperBlob) + blobCount*sizeof(CS_BlobIndex);
+ size_t cd256Offset = cdOffset + cdSize;
+ size_t reqsOffset = cd256Offset + cd256Size; // equals cdOffset + cdSize if not agile
+ size_t cmsOffset = reqsOffset + reqsSize;
+ size_t sbSize = cmsOffset + cmsSize;
+ size_t sigSize = align(sbSize, 14); // keep whole cache 16KB aligned
+
+ // allocate space for blob
+ vm_address_t codeSigAlloc;
+ if ( vm_allocate(mach_task_self(), &codeSigAlloc, sigSize, VM_FLAGS_ANYWHERE) != 0 ) {
+ _diagnostics.error("could not allocate code signature buffer");
+ return;
+ }
+ _codeSignatureRegion.buffer = (uint8_t*)codeSigAlloc;
+ _codeSignatureRegion.bufferSize = sigSize;
+ _codeSignatureRegion.sizeInUse = sigSize;
+
+ // create overall code signature which is a superblob
+ CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(_codeSignatureRegion.buffer);
+ sb->magic = htonl(CSMAGIC_EMBEDDED_SIGNATURE);
+ sb->length = htonl(sbSize);
+ sb->count = htonl(blobCount);
+ sb->index[0].type = htonl(CSSLOT_CODEDIRECTORY);
+ sb->index[0].offset = htonl(cdOffset);
+ sb->index[1].type = htonl(CSSLOT_REQUIREMENTS);
+ sb->index[1].offset = htonl(reqsOffset);
+ sb->index[2].type = htonl(CSSLOT_CMS_SIGNATURE);
+ sb->index[2].offset = htonl(cmsOffset);
+ if ( agile ) {
+ sb->index[3].type = htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES + 0);
+ sb->index[3].offset = htonl(cd256Offset);
+ }
+
+ // fill in empty requirements
+ CS_RequirementsBlob* reqs = (CS_RequirementsBlob*)(((char*)sb)+reqsOffset);
+ reqs->magic = htonl(CSMAGIC_REQUIREMENTS);
+ reqs->length = htonl(sizeof(CS_RequirementsBlob));
+ reqs->data = 0;
+
+ // initialize fixed fields of Code Directory
+ CS_CodeDirectory* cd = (CS_CodeDirectory*)(((char*)sb)+cdOffset);
+ cd->magic = htonl(CSMAGIC_CODEDIRECTORY);
+ cd->length = htonl(cdSize);
+ cd->version = htonl(0x20400); // supports exec segment
+ cd->flags = htonl(kSecCodeSignatureAdhoc);
+ cd->hashOffset = htonl(hashOffset);
+ cd->identOffset = htonl(idOffset);
+ cd->nSpecialSlots = htonl(xSlotCount);
+ cd->nCodeSlots = htonl(slotCount);
+ cd->codeLimit = htonl(inBbufferSize);
+ cd->hashSize = dscHashSize;
+ cd->hashType = dscHashType;
+ cd->platform = 0; // not platform binary
+ cd->pageSize = __builtin_ctz(pageSize); // log2(CS_PAGE_SIZE);
+ cd->spare2 = 0; // unused (must be zero)
+ cd->scatterOffset = 0; // not supported anymore
+ cd->teamOffset = 0; // no team ID
+ cd->spare3 = 0; // unused (must be zero)
+ cd->codeLimit64 = 0; // falls back to codeLimit
+
+ // executable segment info
+ cd->execSegBase = htonll(_readExecuteRegion.cacheFileOffset); // base of TEXT segment
+ cd->execSegLimit = htonll(_readExecuteRegion.sizeInUse); // size of TEXT segment
+ cd->execSegFlags = 0; // not a main binary
+
+ // initialize dynamic fields of Code Directory
+ strcpy((char*)cd + idOffset, cacheIdentifier.c_str());
+
+ // add special slot hashes
+ uint8_t* hashSlot = (uint8_t*)cd + hashOffset;
+ uint8_t* reqsHashSlot = &hashSlot[-CSSLOT_REQUIREMENTS*dscHashSize];
+ CCDigest(dscDigestFormat, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHashSlot);
+
+ CS_CodeDirectory* cd256;
+ uint8_t* hash256Slot;
+ uint8_t* reqsHash256Slot;
+ if ( agile ) {
+ // Note that the assumption here is that the size up to the hashes is the same as for
+ // sha1 code directory, and that they come last, after everything else.
+
+ cd256 = (CS_CodeDirectory*)(((char*)sb)+cd256Offset);
+ cd256->magic = htonl(CSMAGIC_CODEDIRECTORY);
+ cd256->length = htonl(cd256Size);
+ cd256->version = htonl(0x20400); // supports exec segment
+ cd256->flags = htonl(kSecCodeSignatureAdhoc);
+ cd256->hashOffset = htonl(hash256Offset);
+ cd256->identOffset = htonl(idOffset);
+ cd256->nSpecialSlots = htonl(xSlotCount);
+ cd256->nCodeSlots = htonl(slotCount);
+ cd256->codeLimit = htonl(inBbufferSize);
+ cd256->hashSize = CS_HASH_SIZE_SHA256;
+ cd256->hashType = CS_HASHTYPE_SHA256;
+ cd256->platform = 0; // not platform binary
+ cd256->pageSize = __builtin_ctz(pageSize); // log2(CS_PAGE_SIZE);
+ cd256->spare2 = 0; // unused (must be zero)
+ cd256->scatterOffset = 0; // not supported anymore
+ cd256->teamOffset = 0; // no team ID
+ cd256->spare3 = 0; // unused (must be zero)
+ cd256->codeLimit64 = 0; // falls back to codeLimit
+
+ // executable segment info
+ cd256->execSegBase = cd->execSegBase;
+ cd256->execSegLimit = cd->execSegLimit;
+ cd256->execSegFlags = cd->execSegFlags;
+
+ // initialize dynamic fields of Code Directory
+ strcpy((char*)cd256 + idOffset, cacheIdentifier.c_str());
+
+ // add special slot hashes
+ hash256Slot = (uint8_t*)cd256 + hash256Offset;
+ reqsHash256Slot = &hash256Slot[-CSSLOT_REQUIREMENTS*CS_HASH_SIZE_SHA256];
+ CCDigest(kCCDigestSHA256, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHash256Slot);
+ }
+ else {
+ cd256 = NULL;
+ hash256Slot = NULL;
+ reqsHash256Slot = NULL;
+ }
+
+ // fill in empty CMS blob for ad-hoc signing
+ CS_Blob* cms = (CS_Blob*)(((char*)sb)+cmsOffset);
+ cms->magic = htonl(CSMAGIC_BLOBWRAPPER);
+ cms->length = htonl(sizeof(CS_Blob));
+
+
+ // alter header of cache to record size and location of code signature
+ // do this *before* hashing each page
+ dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer;
+ cache->codeSignatureOffset = inBbufferSize;
+ cache->codeSignatureSize = sigSize;
+
+ const uint32_t rwSlotStart = (uint32_t)(_readExecuteRegion.sizeInUse / pageSize);
+ const uint32_t roSlotStart = (uint32_t)(rwSlotStart + _readWriteRegion.sizeInUse / pageSize);
+ const uint32_t localsSlotStart = (uint32_t)(roSlotStart + _readOnlyRegion.sizeInUse / pageSize);
+ auto codeSignPage = ^(size_t i) {
+ const uint8_t* code = nullptr;
+ // move to correct region
+ if ( i < rwSlotStart )
+ code = _readExecuteRegion.buffer + (i * pageSize);
+ else if ( i >= rwSlotStart && i < roSlotStart )
+ code = _readWriteRegion.buffer + ((i - rwSlotStart) * pageSize);
+ else if ( i >= roSlotStart && i < localsSlotStart )
+ code = _readOnlyRegion.buffer + ((i - roSlotStart) * pageSize);
+ else
+ code = _localSymbolsRegion.buffer + ((i - localsSlotStart) * pageSize);
+
+ CCDigest(dscDigestFormat, code, pageSize, hashSlot + (i * dscHashSize));
+
+ if ( agile ) {
+ CCDigest(kCCDigestSHA256, code, pageSize, hash256Slot + (i * CS_HASH_SIZE_SHA256));
+ }
+ };
+
+ // compute hashes
+ dispatch_apply(slotCount, DISPATCH_APPLY_AUTO, ^(size_t i) {
+ codeSignPage(i);
+ });
+
+ // Now that we have a code signature, compute a cache UUID by hashing the code signature blob
+ {
+ uint8_t* uuidLoc = cache->uuid;
+ assert(uuid_is_null(uuidLoc));
+ static_assert(offsetof(dyld_cache_header, uuid) / CS_PAGE_SIZE_4K == 0, "uuid is expected in the first page of the cache");
+ uint8_t fullDigest[CC_SHA256_DIGEST_LENGTH];
+ CC_SHA256((const void*)cd, (unsigned)cdSize, fullDigest);
+ memcpy(uuidLoc, fullDigest, 16);
+ // <rdar://problem/6723729> uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
+ uuidLoc[6] = ( uuidLoc[6] & 0x0F ) | ( 3 << 4 );
+ uuidLoc[8] = ( uuidLoc[8] & 0x3F ) | 0x80;
+
+ // Now codesign page 0 again, because we modified it by setting uuid in header
+ codeSignPage(0);
+ }
+
+ // hash of entire code directory (cdHash) uses same hash as each page
+ uint8_t fullCdHash[dscHashSize];
+ CCDigest(dscDigestFormat, (const uint8_t*)cd, cdSize, fullCdHash);
+ // Note: cdHash is defined as first 20 bytes of hash
+ memcpy(_cdHashFirst, fullCdHash, 20);
+ if ( agile ) {
+ uint8_t fullCdHash256[CS_HASH_SIZE_SHA256];
+ CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, cd256Size, fullCdHash256);
+ // Note: cdHash is defined as first 20 bytes of hash, even for sha256
+ memcpy(_cdHashSecond, fullCdHash256, 20);
+ }
+ else {
+ memset(_cdHashSecond, 0, 20);
+ }
+}
+
+const bool SharedCacheBuilder::agileSignature()
+{
+ return _options.codeSigningDigestMode == DyldSharedCache::Agile;
+}
+
+static const std::string cdHash(uint8_t hash[20])
+{
+ char buff[48];
+ for (int i = 0; i < 20; ++i)
+ sprintf(&buff[2*i], "%2.2x", hash[i]);
+ return buff;
+}
+
+const std::string SharedCacheBuilder::cdHashFirst()
+{
+ return cdHash(_cdHashFirst);
+}
+
+const std::string SharedCacheBuilder::cdHashSecond()
+{
+ return cdHash(_cdHashSecond);
+}
+
+const std::string SharedCacheBuilder::uuid() const
+{
+ dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer;
+ uuid_string_t uuidStr;
+ uuid_unparse(cache->uuid, uuidStr);
+ return uuidStr;
+}
+
+
+
+template <typename P>
+bool SharedCacheBuilder::makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info2* info)
+{
+ typedef typename P::uint_t pint_t;
+
+ const pint_t deltaMask = (pint_t)(info->delta_mask);
+ const pint_t valueMask = ~deltaMask;
+ const pint_t valueAdd = (pint_t)(info->value_add);
+ const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
+ const uint32_t maxDelta = (uint32_t)(deltaMask >> deltaShift);
+
+ pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0];
+ pint_t lastValue = (pint_t)P::getP(*lastLoc);
+ if ( (lastValue - valueAdd) & deltaMask ) {
+ std::string dylibName;
+ std::string segName;
+ findDylibAndSegment((void*)pageContent, dylibName, segName);
+ _diagnostics.error("rebase pointer (0x%0lX) does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
+ (long)lastValue, lastLocationOffset, segName.c_str(), dylibName.c_str());
+ return false;
+ }
+ if ( offset <= (lastLocationOffset+maxDelta) ) {
+ // previous location in range, make link from it
+ // encode this location into last value
+ pint_t delta = offset - lastLocationOffset;
+ pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift);
+ //warning(" add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
+ // offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
+ uint8_t highByte;
+ if ( _aslrTracker.hasHigh8(lastLoc, &highByte) ) {
+ uint64_t tbi = (uint64_t)highByte << 56;
+ newLastValue |= tbi;
+ }
+ P::setP(*lastLoc, newLastValue);
+ return true;
+ }
+ //fprintf(stderr, " too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
+
+ // distance between rebase locations is too far
+ // see if we can make a chain from non-rebase locations
+ uint16_t nonRebaseLocationOffsets[1024];
+ unsigned nrIndex = 0;
+ for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) {
+ nonRebaseLocationOffsets[nrIndex] = 0;
+ for (int j=maxDelta; j > 0; j -= 4) {
+ pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]);
+ if ( value == 0 ) {
+ // Steal values of 0 to be used in the rebase chain
+ nonRebaseLocationOffsets[nrIndex] = i+j;
+ break;
+ }
+ }
+ if ( nonRebaseLocationOffsets[nrIndex] == 0 ) {
+ lastValue = (pint_t)P::getP(*lastLoc);
+ pint_t newValue = ((lastValue - valueAdd) & valueMask);
+ //warning(" no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX", lastLocationOffset, (long)value, (long)newValue);
+ P::setP(*lastLoc, newValue);
+ return false;
+ }
+ i = nonRebaseLocationOffsets[nrIndex];
+ ++nrIndex;
+ }
+
+ // we can make chain. go back and add each non-rebase location to chain
+ uint16_t prevOffset = lastLocationOffset;
+ pint_t* prevLoc = (pint_t*)&pageContent[prevOffset];
+ for (unsigned n=0; n < nrIndex; ++n) {
+ uint16_t nOffset = nonRebaseLocationOffsets[n];
+ assert(nOffset != 0);
+ pint_t* nLoc = (pint_t*)&pageContent[nOffset];
+ uint32_t delta2 = nOffset - prevOffset;
+ pint_t value = (pint_t)P::getP(*prevLoc);
+ pint_t newValue;
+ if ( value == 0 )
+ newValue = (delta2 << deltaShift);
+ else
+ newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift);
+ //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
+ P::setP(*prevLoc, newValue);
+ prevOffset = nOffset;
+ prevLoc = nLoc;
+ }
+ uint32_t delta3 = offset - prevOffset;
+ pint_t value = (pint_t)P::getP(*prevLoc);
+ pint_t newValue;
+ if ( value == 0 )
+ newValue = (delta3 << deltaShift);
+ else
+ newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift);
+ //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
+ P::setP(*prevLoc, newValue);
+
+ return true;
+}
+
+
+template <typename P>
+void SharedCacheBuilder::addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info,
+ std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
+{
+ typedef typename P::uint_t pint_t;
+
+ const pint_t deltaMask = (pint_t)(info->delta_mask);
+ const pint_t valueMask = ~deltaMask;
+ const uint32_t pageSize = info->page_size;
+ const pint_t valueAdd = (pint_t)(info->value_add);
+
+ uint16_t startValue = DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE;
+ uint16_t lastLocationOffset = 0xFFFF;
+ for(uint32_t i=0; i < pageSize/4; ++i) {
+ unsigned offset = i*4;
+ if ( bitmap[i] ) {
+ if ( startValue == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
+ // found first rebase location in page
+ startValue = i;
+ }
+ else if ( !makeRebaseChainV2<P>(pageContent, lastLocationOffset, offset, info) ) {
+ // can't record all rebasings in one chain
+ if ( (startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 ) {
+ // switch page_start to "extras" which is a list of chain starts
+ unsigned indexInExtras = (unsigned)pageExtras.size();
+ if ( indexInExtras > 0x3FFF ) {
+ _diagnostics.error("rebase overflow in v2 page extras");
+ return;
+ }
+ pageExtras.push_back(startValue);
+ startValue = indexInExtras | DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA;
+ }
+ pageExtras.push_back(i);
+ }
+ lastLocationOffset = offset;
+ }
+ }
+ if ( lastLocationOffset != 0xFFFF ) {
+ // mark end of chain
+ pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset];
+ pint_t lastValue = (pint_t)P::getP(*lastLoc);
+ pint_t newValue = ((lastValue - valueAdd) & valueMask);
+ P::setP(*lastLoc, newValue);
+ }
+ if ( startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
+ // add end bit to extras
+ pageExtras.back() |= DYLD_CACHE_SLIDE_PAGE_ATTR_END;
+ }
+ pageStarts.push_back(startValue);
+}
+
+template <typename P>
+void SharedCacheBuilder::writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount)
+{
+ typedef typename P::uint_t pint_t;
+ typedef typename P::E E;
+ const uint32_t pageSize = 4096;
+
+ // fill in fixed info
+ assert(_slideInfoFileOffset != 0);
+ dyld_cache_slide_info2* info = (dyld_cache_slide_info2*)_readOnlyRegion.buffer;
+ info->version = 2;
+ info->page_size = pageSize;
+ info->delta_mask = _archLayout->pointerDeltaMask;
+ info->value_add = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0;
+
+ // set page starts and extras for each page
+ std::vector<uint16_t> pageStarts;
+ std::vector<uint16_t> pageExtras;
+ pageStarts.reserve(dataPageCount);
+ uint8_t* pageContent = _readWriteRegion.buffer;
+ const bool* bitmapForPage = bitmap;
+ for (unsigned i=0; i < dataPageCount; ++i) {
+ //warning("page[%d]", i);
+ addPageStartsV2<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
+ if ( _diagnostics.hasError() ) {
+ return;
+ }
+ pageContent += pageSize;
+ bitmapForPage += (sizeof(bool)*(pageSize/4));
+ }
+
+ // fill in computed info
+ info->page_starts_offset = sizeof(dyld_cache_slide_info2);
+ info->page_starts_count = (unsigned)pageStarts.size();
+ info->page_extras_offset = (unsigned)(sizeof(dyld_cache_slide_info2)+pageStarts.size()*sizeof(uint16_t));
+ info->page_extras_count = (unsigned)pageExtras.size();
+ uint16_t* pageStartsBuffer = (uint16_t*)((char*)info + info->page_starts_offset);
+ uint16_t* pageExtrasBuffer = (uint16_t*)((char*)info + info->page_extras_offset);
+ for (unsigned i=0; i < pageStarts.size(); ++i)
+ pageStartsBuffer[i] = pageStarts[i];
+ for (unsigned i=0; i < pageExtras.size(); ++i)
+ pageExtrasBuffer[i] = pageExtras[i];
+ // update header with final size
+ uint64_t slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2);
+ if ( slideInfoSize > _slideInfoBufferSizeAllocated ) {
+ _diagnostics.error("kernel slide info overflow buffer");
+ }
+ ((dyld_cache_header*)_readExecuteRegion.buffer)->slideInfoSize = slideInfoSize;
+ //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
+}
+
+#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
+// fits in to int16_t
+static bool smallValue(uint64_t value)
+{
+ uint32_t high = (value & 0xFFFF8000);
+ return (high == 0) || (high == 0xFFFF8000);
+}
+
+template <typename P>
+bool SharedCacheBuilder::makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info4* info)
+{
+ typedef typename P::uint_t pint_t;
+
+ const pint_t deltaMask = (pint_t)(info->delta_mask);
+ const pint_t valueMask = ~deltaMask;
+ const pint_t valueAdd = (pint_t)(info->value_add);
+ const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
+ const uint32_t maxDelta = (uint32_t)(deltaMask >> deltaShift);
+
+ pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0];
+ pint_t lastValue = (pint_t)P::getP(*lastLoc);
+ if ( (lastValue - valueAdd) & deltaMask ) {
+ std::string dylibName;
+ std::string segName;
+ findDylibAndSegment((void*)pageContent, dylibName, segName);
+ _diagnostics.error("rebase pointer does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
+ lastLocationOffset, segName.c_str(), dylibName.c_str());
+ return false;
+ }
+ if ( offset <= (lastLocationOffset+maxDelta) ) {
+ // previous location in range, make link from it
+ // encode this location into last value
+ pint_t delta = offset - lastLocationOffset;
+ pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift);
+ //warning(" add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
+ // offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
+ P::setP(*lastLoc, newLastValue);
+ return true;
+ }
+ //fprintf(stderr, " too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
+
+ // distance between rebase locations is too far
+ // see if we can make a chain from non-rebase locations
+ uint16_t nonRebaseLocationOffsets[1024];
+ unsigned nrIndex = 0;
+ for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) {
+ nonRebaseLocationOffsets[nrIndex] = 0;
+ for (int j=maxDelta; j > 0; j -= 4) {
+ pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]);
+ if ( smallValue(value) ) {
+ // Steal values of 0 to be used in the rebase chain
+ nonRebaseLocationOffsets[nrIndex] = i+j;
+ break;
+ }
+ }
+ if ( nonRebaseLocationOffsets[nrIndex] == 0 ) {
+ lastValue = (pint_t)P::getP(*lastLoc);
+ pint_t newValue = ((lastValue - valueAdd) & valueMask);
+ //fprintf(stderr, " no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX\n",
+ // lastLocationOffset, (long)lastValue, (long)newValue);
+ P::setP(*lastLoc, newValue);
+ return false;
+ }
+ i = nonRebaseLocationOffsets[nrIndex];
+ ++nrIndex;
+ }
+
+ // we can make chain. go back and add each non-rebase location to chain
+ uint16_t prevOffset = lastLocationOffset;
+ pint_t* prevLoc = (pint_t*)&pageContent[prevOffset];
+ for (unsigned n=0; n < nrIndex; ++n) {
+ uint16_t nOffset = nonRebaseLocationOffsets[n];
+ assert(nOffset != 0);
+ pint_t* nLoc = (pint_t*)&pageContent[nOffset];
+ uint32_t delta2 = nOffset - prevOffset;
+ pint_t value = (pint_t)P::getP(*prevLoc);
+ pint_t newValue;
+ if ( smallValue(value) )
+ newValue = (value & valueMask) | (delta2 << deltaShift);
+ else
+ newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift);
+ //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
+ P::setP(*prevLoc, newValue);
+ prevOffset = nOffset;
+ prevLoc = nLoc;
+ }
+ uint32_t delta3 = offset - prevOffset;
+ pint_t value = (pint_t)P::getP(*prevLoc);
+ pint_t newValue;
+ if ( smallValue(value) )
+ newValue = (value & valueMask) | (delta3 << deltaShift);
+ else
+ newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift);
+ //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
+ P::setP(*prevLoc, newValue);
+
+ return true;
+}
+
+
+template <typename P>
+void SharedCacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info,
+ std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
+{
+ typedef typename P::uint_t pint_t;
+
+ const pint_t deltaMask = (pint_t)(info->delta_mask);
+ const pint_t valueMask = ~deltaMask;
+ const uint32_t pageSize = info->page_size;
+ const pint_t valueAdd = (pint_t)(info->value_add);
+
+ uint16_t startValue = DYLD_CACHE_SLIDE4_PAGE_NO_REBASE;
+ uint16_t lastLocationOffset = 0xFFFF;
+ for(uint32_t i=0; i < pageSize/4; ++i) {
+ unsigned offset = i*4;
+ if ( bitmap[i] ) {
+ if ( startValue == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
+ // found first rebase location in page
+ startValue = i;
+ }
+ else if ( !makeRebaseChainV4<P>(pageContent, lastLocationOffset, offset, info) ) {
+ // can't record all rebasings in one chain
+ if ( (startValue & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) == 0 ) {
+ // switch page_start to "extras" which is a list of chain starts
+ unsigned indexInExtras = (unsigned)pageExtras.size();
+ if ( indexInExtras >= DYLD_CACHE_SLIDE4_PAGE_INDEX ) {
+ _diagnostics.error("rebase overflow in v4 page extras");
+ return;
+ }
+ pageExtras.push_back(startValue);
+ startValue = indexInExtras | DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA;
+ }
+ pageExtras.push_back(i);
+ }
+ lastLocationOffset = offset;
+ }
+ }
+ if ( lastLocationOffset != 0xFFFF ) {
+ // mark end of chain
+ pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset];
+ pint_t lastValue = (pint_t)P::getP(*lastLoc);
+ pint_t newValue = ((lastValue - valueAdd) & valueMask);
+ P::setP(*lastLoc, newValue);
+ }
+ if ( startValue & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
+ // add end bit to extras
+ pageExtras.back() |= DYLD_CACHE_SLIDE4_PAGE_EXTRA_END;
+ }
+ pageStarts.push_back(startValue);
+}
+
+
+
+template <typename P>
+void SharedCacheBuilder::writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount)
+{
+ typedef typename P::uint_t pint_t;
+ typedef typename P::E E;
+ const uint32_t pageSize = 4096;
+
+ // fill in fixed info
+ assert(_slideInfoFileOffset != 0);
+ dyld_cache_slide_info4* info = (dyld_cache_slide_info4*)_readOnlyRegion.buffer;
+ info->version = 4;
+ info->page_size = pageSize;
+ info->delta_mask = _archLayout->pointerDeltaMask;
+ info->value_add = info->value_add = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0;
+
+ // set page starts and extras for each page
+ std::vector<uint16_t> pageStarts;
+ std::vector<uint16_t> pageExtras;
+ pageStarts.reserve(dataPageCount);
+ uint8_t* pageContent = _readWriteRegion.buffer;
+ const bool* bitmapForPage = bitmap;
+ for (unsigned i=0; i < dataPageCount; ++i) {
+ addPageStartsV4<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
+ if ( _diagnostics.hasError() ) {
+ return;
+ }
+ pageContent += pageSize;
+ bitmapForPage += (sizeof(bool)*(pageSize/4));
+ }
+ // fill in computed info
+ info->page_starts_offset = sizeof(dyld_cache_slide_info4);
+ info->page_starts_count = (unsigned)pageStarts.size();
+ info->page_extras_offset = (unsigned)(sizeof(dyld_cache_slide_info4)+pageStarts.size()*sizeof(uint16_t));
+ info->page_extras_count = (unsigned)pageExtras.size();
+ uint16_t* pageStartsBuffer = (uint16_t*)((char*)info + info->page_starts_offset);
+ uint16_t* pageExtrasBuffer = (uint16_t*)((char*)info + info->page_extras_offset);
+ for (unsigned i=0; i < pageStarts.size(); ++i)
+ pageStartsBuffer[i] = pageStarts[i];
+ for (unsigned i=0; i < pageExtras.size(); ++i)
+ pageExtrasBuffer[i] = pageExtras[i];
+ // update header with final size
+ uint64_t slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2);
+ if ( slideInfoSize > _slideInfoBufferSizeAllocated ) {
+ _diagnostics.error("kernel slide info v4 overflow buffer, need %lldKB, have room for %lldKB", slideInfoSize, _slideInfoBufferSizeAllocated);
+ }
+ ((dyld_cache_header*)_readExecuteRegion.buffer)->slideInfoSize = slideInfoSize;
+ //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
+}
+#endif
+
+/*
+void CacheBuilder::writeSlideInfoV1()
+{
+ // build one 128-byte bitmap per page (4096) of DATA
+ uint8_t* const dataStart = (uint8_t*)_buffer.get() + regions[1].fileOffset;
+ uint8_t* const dataEnd = dataStart + regions[1].size;
+ const long bitmapSize = (dataEnd - dataStart)/(4*8);
+ uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1);
+ for (void* p : _pointersForASLR) {
+ if ( (p < dataStart) || ( p > dataEnd) )
+ terminate("DATA pointer for sliding, out of range\n");
+ long offset = (long)((uint8_t*)p - dataStart);
+ if ( (offset % 4) != 0 )
+ terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset);
+ long byteIndex = offset / (4*8);
+ long bitInByte = (offset % 32) >> 2;
+ bitmap[byteIndex] |= (1 << bitInByte);
+ }
+
+ // allocate worst case size block of all slide info
+ const unsigned entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes.
+ const unsigned toc_count = (unsigned)bitmapSize/entry_size;
+ dyld_cache_slide_info* slideInfo = (dyld_cache_slide_info*)((uint8_t*)_buffer + _slideInfoFileOffset);
+ slideInfo->version = 1;
+ slideInfo->toc_offset = sizeof(dyld_cache_slide_info);
+ slideInfo->toc_count = toc_count;
+ slideInfo->entries_offset = (slideInfo->toc_offset+2*toc_count+127)&(-128);
+ slideInfo->entries_count = 0;
+ slideInfo->entries_size = entry_size;
+ // append each unique entry
+ const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap;
+ dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset());
+ int entry_count = 0;
+ for (int i=0; i < toc_count; ++i) {
+ const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i];
+ // see if it is same as one already added
+ bool found = false;
+ for (int j=0; j < entry_count; ++j) {
+ if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) {
+ slideInfo->set_toc(i, j);
+ found = true;
+ break;
+ }
+ }
+ if ( !found ) {
+ // append to end
+ memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size);
+ slideInfo->set_toc(i, entry_count++);
+ }
+ }
+ slideInfo->entries_count = entry_count;
+ ::free((void*)bitmap);
+
+ _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2);
+}
+
+*/
+
+
+void SharedCacheBuilder::setPointerContentV3(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc, uint64_t targetVMAddr, size_t next)
+{
+ assert(targetVMAddr > _readExecuteRegion.unslidLoadAddress);
+ assert(targetVMAddr < _readOnlyRegion.unslidLoadAddress+_readOnlyRegion.sizeInUse);
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp;
+ uint16_t diversity;
+ bool hasAddrDiv;
+ uint8_t key;
+ if ( _aslrTracker.hasAuthData(loc, &diversity, &hasAddrDiv, &key) ) {
+ // if base cache address cannot fit into target, then use offset
+ tmp.arm64e.authRebase.target = _readExecuteRegion.unslidLoadAddress;
+ if ( tmp.arm64e.authRebase.target != _readExecuteRegion.unslidLoadAddress )
+ targetVMAddr -= _readExecuteRegion.unslidLoadAddress;
+ loc->arm64e.authRebase.target = targetVMAddr;
+ loc->arm64e.authRebase.diversity = diversity;
+ loc->arm64e.authRebase.addrDiv = hasAddrDiv;
+ loc->arm64e.authRebase.key = key;
+ loc->arm64e.authRebase.next = next;
+ loc->arm64e.authRebase.bind = 0;
+ loc->arm64e.authRebase.auth = 1;
+ assert(loc->arm64e.authRebase.target == targetVMAddr && "target truncated");
+ assert(loc->arm64e.authRebase.next == next && "next location truncated");
+ }
+ else {
+ uint8_t highByte = 0;
+ _aslrTracker.hasHigh8(loc, &highByte);
+ // if base cache address cannot fit into target, then use offset
+ tmp.arm64e.rebase.target = _readExecuteRegion.unslidLoadAddress;
+ if ( tmp.arm64e.rebase.target != _readExecuteRegion.unslidLoadAddress )
+ targetVMAddr -= _readExecuteRegion.unslidLoadAddress;
+ loc->arm64e.rebase.target = targetVMAddr;
+ loc->arm64e.rebase.high8 = highByte;
+ loc->arm64e.rebase.next = next;
+ loc->arm64e.rebase.bind = 0;
+ loc->arm64e.rebase.auth = 0;
+ assert(loc->arm64e.rebase.target == targetVMAddr && "target truncated");
+ assert(loc->arm64e.rebase.next == next && "next location truncated");
+ }
+}
+
+uint16_t SharedCacheBuilder::pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[])
+{
+ const int maxPerPage = pageSize / 4;
+ uint16_t result = DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE;
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk* lastLoc = nullptr;
+ for (int i=0; i < maxPerPage; ++i) {
+ if ( bitmap[i] ) {
+ if ( result == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) {
+ // found first rebase location in page
+ result = i * 4;
+ }
+ dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)(pageContent + i*4);;
+ if ( lastLoc != nullptr ) {
+ // convert vmaddr based pointers to arm64e dyld cache chains
+ setPointerContentV3(lastLoc, lastLoc->raw64, loc - lastLoc);
+ }
+ lastLoc = loc;
+ }
+ }
+ if ( lastLoc != nullptr ) {
+ // convert vmaddr based pointers to arm64e dyld cache chain, and mark end of chain
+ setPointerContentV3(lastLoc, lastLoc->raw64, 0);
+ }
+ return result;
+}
+
+
+void SharedCacheBuilder::writeSlideInfoV3(const bool bitmap[], unsigned dataPageCount)
+{
+ const uint32_t pageSize = 4096;
+
+ // fill in fixed info
+ assert(_slideInfoFileOffset != 0);
+ dyld_cache_slide_info3* info = (dyld_cache_slide_info3*)_readOnlyRegion.buffer;
+ info->version = 3;
+ info->page_size = pageSize;
+ info->page_starts_count = dataPageCount;
+ info->auth_value_add = _archLayout->sharedMemoryStart;
+
+ // fill in per-page starts
+ uint8_t* pageContent = _readWriteRegion.buffer;
+ const bool* bitmapForPage = bitmap;
+ for (unsigned i=0; i < dataPageCount; ++i) {
+ info->page_starts[i] = pageStartV3(pageContent, pageSize, bitmapForPage);
+ pageContent += pageSize;
+ bitmapForPage += (sizeof(bool)*(pageSize/4));
+ }
+
+ // update header with final size
+ dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+ dyldCacheHeader->slideInfoSize = align(__offsetof(dyld_cache_slide_info3, page_starts[dataPageCount]), _archLayout->sharedRegionAlignP2);
+ if ( dyldCacheHeader->slideInfoSize > _slideInfoBufferSizeAllocated ) {
+ _diagnostics.error("kernel slide info overflow buffer");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#ifndef SharedCacheBuilder_h
+#define SharedCacheBuilder_h
+
+#include "CacheBuilder.h"
+#include "DyldSharedCache.h"
+#include "ClosureFileSystem.h"
+
+class SharedCacheBuilder : public CacheBuilder {
+public:
+ SharedCacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem);
+
+ void build(std::vector<InputFile>& inputFiles,
+ std::vector<DyldSharedCache::FileAlias>& aliases);
+ void build(const std::vector<LoadedMachO>& dylibs,
+ const std::vector<LoadedMachO>& otherOsDylibsInput,
+ const std::vector<LoadedMachO>& osExecutables,
+ std::vector<DyldSharedCache::FileAlias>& aliases);
+ void build(const std::vector<DyldSharedCache::MappedMachO>& dylibsToCache,
+ const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibs,
+ const std::vector<DyldSharedCache::MappedMachO>& osExecutables,
+ std::vector<DyldSharedCache::FileAlias>& aliases);
+
+ void writeFile(const std::string& path);
+ void writeBuffer(uint8_t*& buffer, uint64_t& size);
+ void writeMapFile(const std::string& path);
+ std::string getMapFileBuffer(const std::string& cacheDisposition) const;
+ void deleteBuffer();
+ const std::set<std::string> warnings();
+ const std::set<const dyld3::MachOAnalyzer*> evictions();
+ const bool agileSignature();
+ const std::string cdHashFirst();
+ const std::string cdHashSecond();
+ const std::string uuid() const;
+
+ void forEachCacheDylib(void (^callback)(const std::string& path));
+
+private:
+
+ void writeSlideInfoV1();
+
+ template <typename P> void writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount);
+ template <typename P> bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info);
+ template <typename P> void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info,
+ std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
+
+ void writeSlideInfoV3(const bool bitmap[], unsigned dataPageCoun);
+ uint16_t pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[]);
+ void setPointerContentV3(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc, uint64_t targetVMAddr, size_t next);
+
+ template <typename P> void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount);
+ template <typename P> bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info);
+ template <typename P> void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info,
+ std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
+
+ struct ArchLayout
+ {
+ uint64_t sharedMemoryStart;
+ uint64_t sharedMemorySize;
+ uint64_t textAndDataMaxSize;
+ uint64_t sharedRegionPadding;
+ uint64_t pointerDeltaMask;
+ const char* archName;
+ uint16_t csPageSize;
+ uint8_t sharedRegionAlignP2;
+ uint8_t slideInfoBytesPerPage;
+ bool sharedRegionsAreDiscontiguous;
+ bool is64;
+ bool useValueAdd;
+ };
+
+ static const ArchLayout _s_archLayout[];
+ static const char* const _s_neverStubEliminateDylibs[];
+ static const char* const _s_neverStubEliminateSymbols[];
+
+ void makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder);
+ void processSelectorStrings(const std::vector<LoadedMachO>& executables);
+ void parseCoalescableSegments();
+ void assignSegmentAddresses();
+
+ uint64_t cacheOverflowAmount();
+ size_t evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs);
+
+ void fipsSign();
+ void codeSign();
+ uint64_t pathHash(const char* path);
+ void writeCacheHeader();
+ void findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
+ void addImageArray();
+ void buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases);
+ void addOtherImageArray(const std::vector<LoadedMachO>&, std::vector<const LoadedMachO*>& overflowDylibs);
+ void addClosures(const std::vector<LoadedMachO>&);
+ void markPaddingInaccessible();
+
+ bool writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset));
+
+ // implemented in OptimizerObjC.cpp
+ void optimizeObjC();
+ uint32_t computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount);
+ uint32_t computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount);
+
+ // implemented in OptimizerBranches.cpp
+ void optimizeAwayStubs();
+
+ typedef std::unordered_map<std::string, const dyld3::MachOAnalyzer*> InstallNameToMA;
+
+ typedef uint64_t CacheOffset;
+
+ UnmappedRegion _codeSignatureRegion;
+ std::set<const dyld3::MachOAnalyzer*> _evictions;
+ const ArchLayout* _archLayout = nullptr;
+ uint32_t _aliasCount = 0;
+ uint64_t _slideInfoFileOffset = 0;
+ uint64_t _slideInfoBufferSizeAllocated = 0;
+ uint8_t* _objcReadOnlyBuffer = nullptr;
+ uint64_t _objcReadOnlyBufferSizeUsed = 0;
+ uint64_t _objcReadOnlyBufferSizeAllocated = 0;
+ uint8_t* _objcReadWriteBuffer = nullptr;
+ uint64_t _objcReadWriteBufferSizeAllocated = 0;
+ uint64_t _selectorStringsFromExecutables = 0;
+ InstallNameToMA _installNameToCacheDylib;
+ std::unordered_map<std::string, uint32_t> _dataDirtySegsOrder;
+ std::map<void*, std::string> _missingWeakImports;
+ const dyld3::closure::ImageArray* _imageArray = nullptr;
+ uint8_t _cdHashFirst[20];
+ uint8_t _cdHashSecond[20];
+ bool _someDylibsUsedChainedFixups = false;
+ std::unordered_map<const dyld3::MachOLoaded*, std::set<CacheOffset>> _dylibToItsExports;
+ std::set<std::pair<const dyld3::MachOLoaded*, CacheOffset>> _dylibWeakExports;
+ std::unordered_map<CacheOffset, std::vector<dyld_cache_patchable_location>> _exportsToUses;
+ std::unordered_map<CacheOffset, std::string> _exportsToName;
+};
+
+
+
+#endif /* SharedCacheBuilder_h */
#include <spawn.h>
#include <Bom/Bom.h>
+#include <Foundation/NSData.h>
+#include <Foundation/NSDictionary.h>
+#include <Foundation/NSPropertyList.h>
+#include <Foundation/NSString.h>
-#include "Manifest.h"
#include "Diagnostics.h"
#include "DyldSharedCache.h"
-#include "BuilderUtils.h"
#include "FileUtils.h"
#include "JSONReader.h"
#include "JSONWriter.h"
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";
::fclose(froots);
}
-BOMCopierCopyOperation filteredCopyExcludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
-{
- std::string absolutePath = &path[1];
- void *userData = BOMCopierUserData(copier);
- std::set<std::string> *cachePaths = (std::set<std::string>*)userData;
- if (cachePaths->count(absolutePath)) {
- return BOMCopierSkipFile;
- }
- return BOMCopierContinue;
-}
-
BOMCopierCopyOperation filteredCopyIncludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
{
std::string absolutePath = &path[1];
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;
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;
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;
return NoFlags;
}
-static dyld3::json::Node getBuildOptionsNode(BuildOptions_v1 buildOptions) {
- dyld3::json::Node buildOptionsNode;
- buildOptionsNode.map["version"].value = dyld3::json::decimal(buildOptions.version);
- buildOptionsNode.map["updateName"].value = buildOptions.updateName;
- buildOptionsNode.map["deviceName"].value = buildOptions.deviceName;
- buildOptionsNode.map["disposition"].value = dispositionToString(buildOptions.disposition);
- buildOptionsNode.map["platform"].value = platformToString(buildOptions.platform);
- for (unsigned i = 0; i != buildOptions.numArchs; ++i) {
- dyld3::json::Node archNode;
- archNode.value = buildOptions.archs[i];
- buildOptionsNode.map["archs"].array.push_back(archNode);
- }
- return buildOptionsNode;
-}
-
struct SharedCacheBuilderOptions {
Diagnostics diags;
std::set<std::string> roots;
std::string dstRoot;
std::string emitJSONPath;
std::string buildAllPath;
- std::string configuration;
std::string resultPath;
std::string baselineDifferenceResultPath;
std::string baselineCacheMapPath;
};
static void loadMRMFiles(Diagnostics& diags,
- SharedCacheBuilder* sharedCacheBuilder,
+ MRMSharedCacheBuilder* sharedCacheBuilder,
const std::vector<std::tuple<std::string, std::string, FileFlags>>& inputFiles,
std::vector<std::pair<const void*, size_t>>& mappedFiles,
const std::set<std::string>& baselineCacheFiles) {
::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);
}
// 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() ) {
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) {
}
}
-static void reportUnknownConfiguration(const std::string& configuration, dyld3::Manifest& manifest) {
- fprintf(stderr, "** Unknown config '%s' for build %s.\n",
- configuration.c_str(), manifest.build().c_str());
-
- // Look for available configurations that the user might have meant.
- // Substring match: print configs that contain the user's string.
- // Regex match: if user wants "N61OS" then match .*N.*6.*1.*O.*S.*
-
- std::string patternString = ".*";
- for (auto c : configuration) {
- if (isalnum(c)) { // filter regex special characters
- patternString += c;
- patternString += ".*";
- }
- }
- std::regex pattern(patternString);
-
- std::vector<std::string> likelyConfigs{};
- std::vector<std::string> allConfigs{};
- manifest.forEachConfiguration([&](const std::string& configName) {
- allConfigs.push_back(configName);
- if (!configuration.empty()) {
- if (configName.find(configuration) != std::string::npos || std::regex_match(configName, pattern)) {
- likelyConfigs.push_back(configName);
- }
- }
- });
-
- if (!likelyConfigs.empty()) {
- fprintf(stderr, "\nDid you mean:\n");
- for (auto configName: likelyConfigs) {
- fprintf(stderr, "%s\n", configName.c_str());
- }
- }
-
- fprintf(stderr, "\nAvailable configurations:\n");
- for (auto configName : allConfigs) {
- fprintf(stderr, "%s\n", configName.c_str());
- }
-}
-
-static void buildCacheFromPListManifest(Diagnostics& diags, const SharedCacheBuilderOptions& options) {
- // Get the list of configurations, without fetching all of the files.
- auto manifest = dyld3::Manifest(diags, options.dylibCacheDir + "/Manifest.plist", false);
-
- if (manifest.build().empty()) {
- fprintf(stderr, "No manifest found at '%s/Manifest.plist'\n", options.dylibCacheDir.c_str());
- exit(-1);
- }
-
- // List configurations if requested.
- if (options.listConfigs) {
- manifest.forEachConfiguration([](const std::string& configName) {
- printf("%s\n", configName.c_str());
- });
- // If we weren't passed a configuration then exit
- if (options.configuration.empty())
- exit(0);
- }
-
- // Stop if the requested configuration is unavailable.
- if (!manifest.filterForConfig(options.configuration)) {
- reportUnknownConfiguration(options.configuration, manifest);
- exit(-1);
- }
-
- // Now finish initializing the manifest.
- // This step is slow so we defer it until after the checks above.
- fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str());
- manifest.populate(options.roots);
-
- (void)mkpath_np((options.dstRoot + "/System/Library/Caches/com.apple.dyld/").c_str(), 0755);
- bool cacheBuildSuccess = false;
- if (options.useMRM) {
-
- std::ofstream jsonFile;
- if (!options.emitJSONPath.empty()) {
- jsonFile.open(options.emitJSONPath, std::ofstream::out);
- if (!jsonFile.is_open()) {
- diags.verbose("can't open file '%s'\n", options.emitJSONPath.c_str());
- return;
- }
- }
- dyld3::json::Node buildInvocationNode;
-
- buildInvocationNode.map["version"].value = "1";
-
- // Find the archs for the configuration we want.
- __block std::set<std::string> validArchs;
- manifest.configuration(options.configuration).forEachArchitecture(^(const std::string& path) {
- validArchs.insert(path);
- });
-
- if (validArchs.size() != 1) {
- fprintf(stderr, "MRM doesn't support more than one arch per configuration: %s\n",
- options.configuration.c_str());
- exit(-1);
- }
-
- const char* archs[validArchs.size()];
- uint64_t archIndex = 0;
- for (const std::string& arch : validArchs) {
- archs[archIndex++] = arch.c_str();
- }
-
- BuildOptions_v1 buildOptions;
- buildOptions.version = 1;
- buildOptions.updateName = manifest.build().c_str();
- buildOptions.deviceName = options.configuration.c_str();
- buildOptions.disposition = Disposition::Unknown;
- buildOptions.platform = (Platform)manifest.platform();
- buildOptions.archs = archs;
- buildOptions.numArchs = validArchs.size();
- buildOptions.verboseDiagnostics = options.debug;
- buildOptions.isLocallyBuiltCache = true;
-
- __block struct SharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions);
- buildInvocationNode.map["buildOptions"] = getBuildOptionsNode(buildOptions);
-
- std::set<std::string> requiredBinaries = {
- "/usr/lib/libSystem.B.dylib"
- };
-
- // Get the file data for every MachO in the BOM.
- __block dyld3::json::Node filesNode;
- __block std::vector<std::pair<const void*, size_t>> mappedFiles;
- manifest.forEachMachO(options.configuration, ^(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf) {
-
- // Filter based on arch as the Manifest adds the file once for each UUID.
- if (!validArchs.count(arch))
- return;
-
- struct stat stat_buf;
- int fd = ::open(buildPath.c_str(), O_RDONLY, 0);
- if (fd == -1) {
- diags.verbose("can't open file '%s', errno=%d\n", buildPath.c_str(), errno);
- return;
- }
-
- if (fstat(fd, &stat_buf) == -1) {
- diags.verbose("can't stat open file '%s', errno=%d\n", buildPath.c_str(), errno);
- ::close(fd);
- return;
- }
-
- const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (buffer == MAP_FAILED) {
- diags.verbose("mmap() for file at %s failed, errno=%d\n", buildPath.c_str(), errno);
- ::close(fd);
- }
- ::close(fd);
-
- mappedFiles.emplace_back(buffer, (size_t)stat_buf.st_size);
- FileFlags fileFlags = FileFlags::NoFlags;
- if (requiredBinaries.count(runtimePath))
- fileFlags = FileFlags::RequiredClosure;
- addFile(sharedCacheBuilder, runtimePath.c_str(), (uint8_t*)buffer, (size_t)stat_buf.st_size, fileFlags);
-
- dyld3::json::Node fileNode;
- fileNode.map["path"].value = runtimePath;
- fileNode.map["flags"].value = fileFlagsToString(fileFlags);
- filesNode.array.push_back(fileNode);
- });
-
- __block dyld3::json::Node symlinksNode;
- manifest.forEachSymlink(options.configuration, ^(const std::string &fromPath, const std::string &toPath) {
- addSymlink(sharedCacheBuilder, fromPath.c_str(), toPath.c_str());
-
- dyld3::json::Node symlinkNode;
- symlinkNode.map["from-path"].value = fromPath;
- symlinkNode.map["to-path"].value = toPath;
- symlinksNode.array.push_back(symlinkNode);
- });
-
- buildInvocationNode.map["symlinks"] = symlinksNode;
-
- std::string orderFileData;
- if (!manifest.dylibOrderFile().empty()) {
- orderFileData = loadOrderFile(manifest.dylibOrderFile());
- if (!orderFileData.empty()) {
- addFile(sharedCacheBuilder, "*order file data*", (uint8_t*)orderFileData.data(), orderFileData.size(), FileFlags::DylibOrderFile);
- dyld3::json::Node fileNode;
- fileNode.map["path"].value = manifest.dylibOrderFile();
- fileNode.map["flags"].value = fileFlagsToString(FileFlags::DylibOrderFile);
- filesNode.array.push_back(fileNode);
- }
- }
-
- std::string dirtyDataOrderFileData;
- if (!manifest.dirtyDataOrderFile().empty()) {
- dirtyDataOrderFileData = loadOrderFile(manifest.dirtyDataOrderFile());
- if (!dirtyDataOrderFileData.empty()) {
- addFile(sharedCacheBuilder, "*dirty data order file data*", (uint8_t*)dirtyDataOrderFileData.data(), dirtyDataOrderFileData.size(), FileFlags::DirtyDataOrderFile);
- dyld3::json::Node fileNode;
- fileNode.map["path"].value = manifest.dirtyDataOrderFile();
- fileNode.map["flags"].value = fileFlagsToString(FileFlags::DirtyDataOrderFile);
- filesNode.array.push_back(fileNode);
- }
- }
-
- buildInvocationNode.map["files"] = filesNode;
-
- if (jsonFile.is_open()) {
- dyld3::json::printJSON(buildInvocationNode, 0, jsonFile);
- jsonFile.close();
- }
-
- cacheBuildSuccess = runSharedCacheBuilder(sharedCacheBuilder);
-
- writeMRMResults(cacheBuildSuccess, sharedCacheBuilder, options);
-
- destroySharedCacheBuilder(sharedCacheBuilder);
-
- for (auto mappedFile : mappedFiles)
- ::munmap((void*)mappedFile.first, mappedFile.second);
- } else {
- manifest.calculateClosure();
-
- cacheBuildSuccess = build(diags, manifest, options.dstRoot, false, options.debug, false, false, options.emitDevCaches, true);
- }
-
- if (!cacheBuildSuccess) {
- exit(-1);
- }
-
- // Compare this cache to the baseline cache and see if we have any roots to copy over
- if (!options.baselineDifferenceResultPath.empty() || options.baselineCopyRoots) {
- std::set<std::string> baselineDylibs = manifest.resultsForConfiguration(options.configuration);
-
- std::set<std::string> newDylibs;
- std::map<std::string, std::string> missingDylibReasons;
- manifest.forEachConfiguration([&manifest, &newDylibs, &missingDylibReasons](const std::string& configName) {
- for (auto& arch : manifest.configuration(configName).architectures) {
- for (auto& dylib : arch.second.results.dylibs) {
- if (dylib.second.included) {
- newDylibs.insert(manifest.installNameForUUID(dylib.first));
- } else {
- missingDylibReasons[manifest.installNameForUUID(dylib.first)] = dylib.second.exclusionInfo;
- }
- }
- }
- });
-
- // Work out the set of dylibs in the old cache but not the new one
- std::map<std::string, std::string> dylibsMissingFromNewCache;
- if (options.baselineCopyRoots || !options.baselineDifferenceResultPath.empty()) {
- for (const std::string& baselineDylib : baselineDylibs) {
- if (!newDylibs.count(baselineDylib)) {
- auto reasonIt = missingDylibReasons.find(baselineDylib);
- if (reasonIt != missingDylibReasons.end())
- dylibsMissingFromNewCache[baselineDylib] = reasonIt->second;
- else
- dylibsMissingFromNewCache[baselineDylib] = "";
- }
- }
-
- if (!dylibsMissingFromNewCache.empty()) {
- // Work out which dylibs are missing from the new cache, but are not
- // coming from the -root which already has them on disk
- std::set<std::string> pathsNotInRoots;
- for (std::pair<std::string, std::string> dylibMissingFromNewCache : dylibsMissingFromNewCache) {
- const std::string& dylibInstallName = dylibMissingFromNewCache.first;
- bool foundInRoot = false;
- for (auto& root : options.roots) {
- struct stat sb;
- std::string filePath = root + "/" + dylibInstallName;
- if (!stat(filePath.c_str(), &sb)) {
- foundInRoot = true;
- }
- }
- if (!foundInRoot)
- pathsNotInRoots.insert(dylibInstallName);
- }
-
- BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
- BOMCopierSetUserData(copier, (void*)&pathsNotInRoots);
- BOMCopierSetCopyFileStartedHandler(copier, filteredCopyIncludingPaths);
- std::string dylibCacheRootDir = realFilePath(options.dylibCacheDir + "/Root");
- if (dylibCacheRootDir == "") {
- fprintf(stderr, "Could not find dylib Root directory to copy baseline roots from\n");
- exit(1);
- }
- BOMCopierCopy(copier, dylibCacheRootDir.c_str(), options.dstRoot.c_str());
- BOMCopierFree(copier);
-
- for (std::pair<std::string, std::string> dylibMissingFromNewCache : dylibsMissingFromNewCache) {
- if (dylibMissingFromNewCache.second.empty())
- diags.verbose("Dylib missing from new cache: '%s'\n", dylibMissingFromNewCache.first.c_str());
- else
- diags.verbose("Dylib missing from new cache: '%s' because '%s'\n",
- dylibMissingFromNewCache.first.c_str(), dylibMissingFromNewCache.second.c_str());
- }
- }
- }
-
- if (!options.baselineDifferenceResultPath.empty()) {
- auto cppToObjStr = [](const std::string& str) {
- return [NSString stringWithUTF8String:str.c_str()];
- };
-
- // Work out the set of dylibs in the cache and taken from the -root
- NSMutableArray<NSString*>* dylibsFromRoots = [NSMutableArray array];
- for (auto& root : options.roots) {
- for (const std::string& dylibInstallName : newDylibs) {
- struct stat sb;
- std::string filePath = root + "/" + dylibInstallName;
- if (!stat(filePath.c_str(), &sb)) {
- [dylibsFromRoots addObject:cppToObjStr(dylibInstallName)];
- }
- }
- }
-
- // Work out the set of dylibs in the new cache but not in the baseline cache.
- NSMutableArray<NSString*>* dylibsMissingFromBaselineCache = [NSMutableArray array];
- for (const std::string& newDylib : newDylibs) {
- if (!baselineDylibs.count(newDylib))
- [dylibsMissingFromBaselineCache addObject:cppToObjStr(newDylib)];
- }
-
- // If a dylib which was cached is no longer eligible, say why
- NSMutableArray<NSDictionary*>* dylibsReasonsMissingFromNewCache = [NSMutableArray array];
- for (std::pair<std::string, std::string> dylibMissingFromNewCache : dylibsMissingFromNewCache) {
- NSMutableDictionary* reasonDict = [[NSMutableDictionary alloc] init];
- reasonDict[@"path"] = cppToObjStr(dylibMissingFromNewCache.first);
- reasonDict[@"reason"] = cppToObjStr(dylibMissingFromNewCache.second);
- [dylibsReasonsMissingFromNewCache addObject:reasonDict];
- }
-
- NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
- cacheDict[@"root-paths-in-cache"] = dylibsFromRoots;
- cacheDict[@"device-paths-to-delete"] = dylibsMissingFromBaselineCache;
- cacheDict[@"baseline-paths-evicted-from-cache"] = dylibsReasonsMissingFromNewCache;
-
- NSError* error = nil;
- NSData* outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
- format:NSPropertyListBinaryFormat_v1_0
- options:0
- error:&error];
- (void)[outData writeToFile:cppToObjStr(options.baselineDifferenceResultPath) atomically:YES];
- }
- }
-
- if (options.copyRoots) {
- std::set<std::string> cachePaths;
- manifest.forEachConfiguration([&manifest, &cachePaths](const std::string& configName) {
- for (auto& arch : manifest.configuration(configName).architectures) {
- for (auto& dylib : arch.second.results.dylibs) {
- if (dylib.second.included) {
- cachePaths.insert(manifest.installNameForUUID(dylib.first));
- }
- }
- }
- });
-
- BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
- BOMCopierSetUserData(copier, (void*)&cachePaths);
- BOMCopierSetCopyFileStartedHandler(copier, filteredCopyExcludingPaths);
- for (auto& root : options.roots) {
- BOMCopierCopy(copier, root.c_str(), options.dstRoot.c_str());
- }
- BOMCopierFree(copier);
- }
-
- int err = sync_volume_np(options.dstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT);
- if (err) {
- fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err));
- }
-
- // Now that all the build commands have been issued lets put a barrier in after then which can tear down the app after
- // everything is written.
-
- if (!options.resultPath.empty()) {
- manifest.write(options.resultPath);
- }
-}
-
static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuilderOptions& options,
const std::string& jsonManifestPath) {
dyld3::json::Node manifestNode = dyld3::json::readJSON(diags, jsonManifestPath.c_str());
if (diags.hasError())
return;
- struct SharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions);
+ struct MRMSharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions);
// Parse the files
if (filesNode.array.empty()) {
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
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);
}
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);
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);
}
}
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];
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2018-2019 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include "MachOAnalyzer.h"
#include "ClosureFileSystemPhysical.h"
-static bool printSharedRegion = false;
-static bool printFunctionStarts = false;
-static bool printDataCode = false;
-
static void versionToString(uint32_t value, char buffer[32])
{
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:
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;
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;
_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;
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) {
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;
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();
}
}
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();
"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;
}
});
});
+ if ( diag.hasError() )
+ fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
}
static void printOpcodeFixups(const dyld3::MachOAnalyzer* ma)
}
+static inline std::string decimal(int64_t value) {
+ char buff[64];
+ sprintf(buff, "%lld", value);
+ return buff;
+}
+
+static std::string rebaseTargetString(const dyld3::MachOAnalyzer* ma, uint64_t vmAddr)
+{
+ uint64_t targetLoadAddr = (uint64_t)ma+vmAddr;
+ const char* targetSymbolName;
+ uint64_t targetSymbolLoadAddr;
+ if ( ma->findClosestSymbol(targetLoadAddr, &targetSymbolName, &targetSymbolLoadAddr) ) {
+ uint64_t delta = targetLoadAddr - targetSymbolLoadAddr;
+ if ( delta == 0 ) {
+ return targetSymbolName;
+ }
+ else {
+ return std::string(targetSymbolName) + std::string("+") + decimal(delta);
+ }
+ }
+ else {
+ __block std::string result;
+ ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+ if ( (sectInfo.sectAddr <= vmAddr) && (vmAddr < sectInfo.sectAddr+sectInfo.sectSize) ) {
+ if ( (sectInfo.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) {
+ const char* cstring = (char*)ma + (vmAddr-ma->preferredLoadAddress());
+ result = std::string("\"") + cstring + std::string("\"");
+ }
+ else {
+ result = std::string(sectInfo.segInfo.segName) + "/" + sectInfo.sectName + "+" + decimal(vmAddr - sectInfo.sectAddr);
+ }
+ }
+ });
+ return result;
+ }
+}
+
+static void printSymbolicChainedFixups(const dyld3::MachOAnalyzer* ma)
+{
+ // build array of targets
+ __block Diagnostics diag;
+ __block std::vector<FixupTarget> targets;
+ ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+ FixupTarget target;
+ target.value = 0;
+ target.dylib = ordinalName(ma, libOrdinal);
+ target.symbolName = symbolName;
+ target.addend = addend;
+ target.weakImport = weakImport;
+ targets.push_back(target);
+ });
+ if ( diag.hasError() )
+ return;
+
+ // walk all fixup chains
+ uint64_t baseAddress = ma->preferredLoadAddress();
+ SectionFinder sectionInfo(ma);
+ __block uint64_t lastSymbolVmOffset = 0;
+ __block bool lastSymbolIsSectionStart = false;
+ ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
+ ma->forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+ uint64_t fixupVmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
+ if ( sectionInfo.isNewSection(fixupVmOffset) ) {
+ printf(" 0x%08llX %-12s %-16s \n", sectionInfo.currentSectionAddress(), sectionInfo.segmentName(fixupVmOffset), sectionInfo.sectionName(fixupVmOffset));
+ lastSymbolVmOffset = sectionInfo.currentSectionAddress()-sectionInfo.baseAddress();
+ lastSymbolIsSectionStart = true;
+ }
+ const char* symbolName;
+ uint64_t symbolLoadAddr = 0;
+ if ( ma->findClosestSymbol((uint64_t)fixupLoc, &symbolName, &symbolLoadAddr) ) {
+ uint64_t symbolVmOffset = symbolLoadAddr - (uint64_t)ma;
+ if ( (symbolVmOffset != lastSymbolVmOffset) || lastSymbolIsSectionStart ) {
+ printf(" %s:\n", symbolName);
+ lastSymbolVmOffset = symbolVmOffset;
+ lastSymbolIsSectionStart = false;
+ }
+ }
+ const char* fixupKind = "";
+ std::string fixupTarget;
+ char authInfo[64];
+ switch (segInfo->pointer_format) {
+ case DYLD_CHAINED_PTR_ARM64E:
+ if ( fixupLoc->arm64e.authRebase.auth ) {
+ sprintf(authInfo, "(0x%04X %d %s)", fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.keyName());
+ if ( fixupLoc->arm64e.authBind.bind ) {
+ const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal];
+ fixupKind = "bind authptr";
+ fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
+ if ( bindTarget.addend )
+ fixupTarget += std::string("+") + decimal(bindTarget.addend);
+ if ( bindTarget.weakImport )
+ fixupTarget += " [weak-import]";
+ }
+ else {
+ uint64_t targetVmAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
+ fixupKind = "rebase authptr";
+ fixupTarget = rebaseTargetString(ma, targetVmAddr);
+ }
+ }
+ else {
+ authInfo[0] = '\0';
+ if ( fixupLoc->arm64e.rebase.bind ) {
+ const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
+ uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
+ fixupKind = "bind pointer";
+ fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
+ if ( fullAddend )
+ fixupTarget += std::string("+") + decimal(fullAddend);
+ if ( bindTarget.weakImport )
+ fixupTarget += " [weak-import]";
+ }
+ else {
+ uint64_t targetVmAddr = fixupLoc->arm64e.unpackTarget();
+ fixupKind = "rebase pointer";
+ fixupTarget = rebaseTargetString(ma, targetVmAddr);
+ }
+ }
+ printf(" +0x%04llX %16s %30s %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, authInfo, fixupTarget.c_str());
+ break;
+ case DYLD_CHAINED_PTR_ARM64E_OFFSET:
+ if ( fixupLoc->arm64e.authRebase.auth ) {
+ sprintf(authInfo, "(0x%04X %d %s)", fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.keyName());
+ if ( fixupLoc->arm64e.authBind.bind ) {
+ const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal];
+ fixupKind = "bind authptr";
+ fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
+ if ( bindTarget.addend )
+ fixupTarget += std::string("+") + decimal(bindTarget.addend);
+ if ( bindTarget.weakImport )
+ fixupTarget += " [weak-import]";
+ }
+ else {
+ uint64_t targetVmAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
+ fixupKind = "rebase authptr";
+ fixupTarget = rebaseTargetString(ma, targetVmAddr);
+ }
+ }
+ else {
+ authInfo[0] = '\0';
+ if ( fixupLoc->arm64e.rebase.bind ) {
+ const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
+ uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
+ fixupKind = "bind pointer";
+ fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
+ if ( fullAddend )
+ fixupTarget += std::string("+") + decimal(fullAddend);
+ if ( bindTarget.weakImport )
+ fixupTarget += " [weak-import]";
+ }
+ else {
+ uint64_t targetVmAddr = fixupLoc->arm64e.unpackTarget() + baseAddress;
+ fixupKind = "rebase pointer";
+ fixupTarget = rebaseTargetString(ma, targetVmAddr);
+ }
+ }
+ printf(" +0x%04llX %16s %30s %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, authInfo, fixupTarget.c_str());
+ break;
+ case DYLD_CHAINED_PTR_64:
+ if ( fixupLoc->generic64.rebase.bind ) {
+ const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal];
+ fixupKind = "bind pointer";
+ uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend();
+ fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
+ if ( fullAddend )
+ fixupTarget += std::string("+") + decimal(fullAddend);
+ if ( bindTarget.weakImport )
+ fixupTarget += " [weak-import]";
+ }
+ else {
+ uint64_t targetVmAddr = fixupLoc->generic64.unpackedTarget();
+ fixupKind = "rebase pointer";
+ fixupTarget = rebaseTargetString(ma, targetVmAddr);
+ }
+ printf(" +0x%04llX %16s %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, fixupTarget.c_str());
+ break;
+ case DYLD_CHAINED_PTR_64_OFFSET:
+ if ( fixupLoc->generic64.rebase.bind ) {
+ const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal];
+ fixupKind = "bind pointer";
+ uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend();
+ fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
+ if ( fullAddend )
+ fixupTarget += std::string("+") + decimal(fullAddend);
+ if ( bindTarget.weakImport )
+ fixupTarget += " [weak-import]";
+ }
+ else {
+ uint64_t targetVmAddr = fixupLoc->generic64.unpackedTarget() + baseAddress;
+ fixupKind = "rebase pointer";
+ fixupTarget = rebaseTargetString(ma, targetVmAddr);
+ }
+ printf(" +0x%04llX %16s %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, fixupTarget.c_str());
+ break;
+ case DYLD_CHAINED_PTR_32:
+ if ( fixupLoc->generic32.rebase.bind ) {
+ const FixupTarget& bindTarget = targets[fixupLoc->generic32.bind.ordinal];
+ uint32_t fullAddend = (uint32_t)bindTarget.addend + fixupLoc->generic32.bind.addend;
+ fixupKind = "bind pointer";
+ fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
+ if ( fullAddend )
+ fixupTarget += std::string("+") + decimal(fullAddend);
+ if ( bindTarget.weakImport )
+ fixupTarget += " [weak-import]";
+ }
+ else {
+ uint32_t targetAddr = fixupLoc->generic32.rebase.target;
+ fixupKind = "rebase pointer";
+ fixupTarget = rebaseTargetString(ma, targetAddr);
+ }
+ printf(" +0x%04llX %16s %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, fixupTarget.c_str());
+ break;
+ default:
+ fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format);
+ break;
+ }
+ });
+ });
+ if ( diag.hasError() )
+ fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
+}
+
+struct SymbolicFixupInfo
+{
+ uint64_t address;
+ const char* kind;
+ std::string target;
+};
+
+static void printSymbolicOpcodeFixups(const dyld3::MachOAnalyzer* ma)
+{
+ Diagnostics diag;
+ __block std::vector<SymbolicFixupInfo> fixups;
+ SectionFinder namer(ma);
+ ma->forEachRebase(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[],
+ bool segIndexSet, uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset, uint8_t type, bool& stop) {
+ const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex];
+ uint64_t locVmAddr = segment.vmAddr + segOffset;
+ uint64_t runtimeOffset = locVmAddr - namer.baseAddress();
+ const uint8_t* loc = ((uint8_t*)ma + runtimeOffset);
+ uint64_t value = (pointerSize == 8) ? *((uint64_t*)(loc)) : *((uint32_t*)(loc));
+ SymbolicFixupInfo fixup;
+ fixup.address = locVmAddr;
+ fixup.kind = rebaseTypeName(type);
+ fixup.target = rebaseTargetString(ma, value);
+ fixups.push_back(fixup);
+ });
+
+ ma->forEachBind(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[],
+ bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
+ uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset,
+ uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
+ const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex];
+ uint64_t locVmAddr = segment.vmAddr + segOffset;
+ SymbolicFixupInfo fixup;
+ fixup.address = locVmAddr;
+ fixup.kind = bindTypeName(type);
+ fixup.target = std::string(ordinalName(ma, libOrdinal)) + "/" + symbolName;
+ if ( addend != 0 )
+ fixup.target += std::string("+") + decimal(addend);
+ if ( weakImport )
+ fixup.target += " [weak-import]";
+ fixups.push_back(fixup);
+ },^(const char* symbolName) {
+ },^() { });
+
+
+ std::sort(fixups.begin(), fixups.end(), [](const SymbolicFixupInfo& l, const SymbolicFixupInfo& r) {
+ if ( &l == &r )
+ return false;
+ return ( l.address < r.address );
+ });
+
+ SectionFinder sectionTracker(ma);
+ uint64_t lastSymbolVmOffset = 0;
+ for (const SymbolicFixupInfo& fixup : fixups) {
+ uint64_t vmOffset = fixup.address;
+ uint64_t vmAddr = sectionTracker.baseAddress() + vmOffset;
+ if ( sectionTracker.isNewSection(vmOffset) ) {
+ printf(" 0x%08llX %-12s %-16s \n", vmAddr, sectionTracker.segmentName(vmOffset), sectionTracker.sectionName(vmOffset));
+ lastSymbolVmOffset = vmOffset;
+ }
+ const char* symbolName;
+ uint64_t symbolLoadAddr = 0;
+ if ( ma->findClosestSymbol((uint64_t)ma+vmOffset, &symbolName, &symbolLoadAddr) ) {
+ uint64_t symbolVmOffset = symbolLoadAddr - (uint64_t)ma;
+ if ( symbolVmOffset != lastSymbolVmOffset ) {
+ printf(" %s:\n", symbolName);
+ lastSymbolVmOffset = symbolVmOffset;
+ }
+ }
+ printf(" +0x%04llX %16s %s\n", vmOffset - lastSymbolVmOffset, fixup.kind, fixup.target.c_str());
+ }
+}
static void printFixups(const dyld3::MachOAnalyzer* ma)
{
printf(" -fixups:\n");
- if ( ma->isPreload() || ma->isStaticExecutable() ) {
+ if ( ma->isPreload() || (ma->isStaticExecutable() && !ma->hasChainedFixups()) ) {
printPreloadChainedFixups(ma);
}
else if ( ma->hasChainedFixups() ) {
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)
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 ) {
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
- *
- * Copyright (c) 2016 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <fcntl.h>
-#include <dlfcn.h>
-#include <signal.h>
-#include <errno.h>
-#include <assert.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/resource.h>
-#include <dirent.h>
-#include <rootless.h>
-#include <dscsym.h>
-#include <dispatch/dispatch.h>
-#include <pthread/pthread.h>
-
-#include <algorithm>
-#include <vector>
-#include <unordered_set>
-#include <unordered_set>
-#include <iostream>
-#include <fstream>
-
-#include "MachOFile.h"
-#include "FileUtils.h"
-#include "StringUtils.h"
-#include "DyldSharedCache.h"
-
-
-
-struct MappedMachOsByCategory
-{
- std::string archName;
- std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
- std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles;
- std::vector<DyldSharedCache::MappedMachO> mainExecutables;
-};
-
-static bool verbose = false;
-
-
-static bool addIfMachO(const std::string& buildRootPath, const std::string& runtimePath, const struct stat& statBuf, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
-{
- // read start of file to determine if it is mach-o or a fat file
- std::string fullPath = buildRootPath + runtimePath;
- int fd = ::open(fullPath.c_str(), O_RDONLY);
- if ( fd < 0 )
- return false;
- bool result = false;
- const void* wholeFile = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if ( wholeFile != MAP_FAILED ) {
- Diagnostics diag;
- bool usedWholeFile = false;
- for (MappedMachOsByCategory& file : files) {
- uint64_t sliceOffset = 0;
- uint64_t sliceLength = statBuf.st_size;
- bool fatButMissingSlice;
- const void* slice = MAP_FAILED;
- const dyld3::FatFile* fh = (dyld3::FatFile*)wholeFile;
- const dyld3::MachOFile* mh = (dyld3::MachOFile*)wholeFile;
- if ( fh->isFatFileWithSlice(diag, statBuf.st_size, file.archName.c_str(), sliceOffset, sliceLength, fatButMissingSlice) ) {
- slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
- if ( slice != MAP_FAILED ) {
- //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str());
- mh = (dyld3::MachOFile*)slice;
- if ( !mh->isMachO(diag, sliceLength) ) {
- ::munmap((void*)slice, sliceLength);
- slice = MAP_FAILED;
- }
- }
- }
- else if ( !fatButMissingSlice && mh->isMachO(diag, sliceLength) ) {
- slice = wholeFile;
- sliceLength = statBuf.st_size;
- sliceOffset = 0;
- usedWholeFile = true;
- //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str());
- }
- if ( slice != MAP_FAILED ) {
- mh = (dyld3::MachOFile*)slice;
- if ( mh->platform() != platform ) {
- fprintf(stderr, "skipped wrong platform binary: %s\n", fullPath.c_str());
- result = false;
- }
- else {
- bool sip = true; // assume anything found in the simulator runtime is a platform binary
- if ( mh->isDynamicExecutable() ) {
- bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
- file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
- }
- else {
- if ( parser.canBePlacedInDyldCache(runtimePath) ) {
- file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino);
- }
- }
- result = true;
- }
- }
- }
- if ( !usedWholeFile )
- ::munmap((void*)wholeFile, statBuf.st_size);
- }
- ::close(fd);
- return result;
-}
-
-
-static bool parsePathsFile(const std::string& filePath, std::vector<std::string>& paths) {
- std::ifstream myfile( filePath );
- if ( myfile.is_open() ) {
- std::string line;
- while ( std::getline(myfile, line) ) {
- size_t pos = line.find('#');
- if ( pos != std::string::npos )
- line.resize(pos);
- while ( line.size() != 0 && isspace(line.back()) ) {
- line.pop_back();
- }
- if ( !line.empty() )
- paths.push_back(line);
- }
- myfile.close();
- return true;
- }
- return false;
-}
-
-
-static void mapAllFiles(const std::string& dylibsRootDir, const std::vector<std::string>& paths, dyld3::Platform platform, std::vector<MappedMachOsByCategory>& files)
-{
- for (const std::string& runtimePath : paths) {
- std::string fullPath = dylibsRootDir + runtimePath;
- struct stat statBuf;
- if ( (stat(fullPath.c_str(), &statBuf) != 0) || !addIfMachO(dylibsRootDir, runtimePath, statBuf, platform, files) )
- fprintf(stderr, "could not load: %s\n", fullPath.c_str());
- }
-}
-
-
-
-inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
-{
- return (uint32_t)(abstime/1000/1000);
-}
-
-
-#define TERMINATE_IF_LAST_ARG( s ) \
- do { \
- if ( i == argc - 1 ) { \
- fprintf(stderr, s ); \
- return 1; \
- } \
- } while ( 0 )
-
-int main(int argc, const char* argv[])
-{
- std::string rootPath;
- std::string dylibListFile;
- bool force = false;
- std::string cacheDir;
- std::string dylibsList;
- std::unordered_set<std::string> archStrs;
-
- dyld3::Platform platform = dyld3::Platform::iOS;
-
- // parse command line options
- for (int i = 1; i < argc; ++i) {
- const char* arg = argv[i];
- if (strcmp(arg, "-debug") == 0) {
- verbose = true;
- }
- else if (strcmp(arg, "-verbose") == 0) {
- verbose = true;
- }
- else if (strcmp(arg, "-tvOS") == 0) {
- platform = dyld3::Platform::tvOS;
- }
- else if (strcmp(arg, "-iOS") == 0) {
- platform = dyld3::Platform::iOS;
- }
- else if (strcmp(arg, "-watchOS") == 0) {
- platform = dyld3::Platform::watchOS;
- }
- else if ( strcmp(arg, "-root") == 0 ) {
- TERMINATE_IF_LAST_ARG("-root missing path argument\n");
- rootPath = argv[++i];
- }
- else if ( strcmp(arg, "-dylibs_list") == 0 ) {
- TERMINATE_IF_LAST_ARG("-dylibs_list missing path argument\n");
- dylibsList = argv[++i];
- }
- else if (strcmp(arg, "-cache_dir") == 0) {
- TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n");
- cacheDir = argv[++i];
- }
- else if (strcmp(arg, "-arch") == 0) {
- TERMINATE_IF_LAST_ARG("-arch missing argument\n");
- archStrs.insert(argv[++i]);
- }
- else if (strcmp(arg, "-force") == 0) {
- force = true;
- }
- else {
- //usage();
- fprintf(stderr, "update_dyld_sim_shared_cache: unknown option: %s\n", arg);
- return 1;
- }
- }
-
- if ( cacheDir.empty() ) {
- fprintf(stderr, "missing -cache_dir <path> option to specify directory in which to write cache file(s)\n");
- return 1;
- }
-
- if ( rootPath.empty() ) {
- fprintf(stderr, "missing -runtime_dir <path> option to specify directory which is root of simulator runtime)\n");
- return 1;
- }
- else {
- // canonicalize rootPath
- char resolvedPath[PATH_MAX];
- if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
- rootPath = resolvedPath;
- }
- if ( rootPath.back() != '/' )
- rootPath = rootPath + "/";
- }
-
- int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
- if ( (err != 0) && (err != EEXIST) ) {
- fprintf(stderr, "mkpath_np fail: %d", err);
- return 1;
- }
-
- if ( archStrs.empty() ) {
- switch ( platform ) {
- case dyld3::Platform::iOS:
- case dyld3::Platform::tvOS:
- archStrs.insert("arm64");
- break;
- case dyld3::Platform::watchOS:
- archStrs.insert("armv7k");
- archStrs.insert("arm64_32");
- break;
- case dyld3::Platform::unknown:
- case dyld3::Platform::macOS:
- assert(0 && "macOS not support with this tool");
- break;
- }
- }
-
- uint64_t t1 = mach_absolute_time();
-
- // find all mach-o files for requested architectures
- std::vector<MappedMachOsByCategory> allFileSets;
- if ( archStrs.count("arm64") )
- allFileSets.push_back({"arm64"});
- if ( archStrs.count("arm64_32") )
- allFileSets.push_back({"arm64_32"});
- if ( archStrs.count("armv7k") )
- allFileSets.push_back({"armv7k"});
- std::vector<std::string> paths;
- parsePathsFile(dylibsList, paths);
- mapAllFiles(rootPath, paths, platform, allFileSets);
-
- uint64_t t2 = mach_absolute_time();
-
- fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
-
- // build all caches in parallel
- __block bool cacheBuildFailure = false;
- dispatch_apply(allFileSets.size(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
- const MappedMachOsByCategory& fileSet = allFileSets[index];
- const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName;
-
- fprintf(stderr, "make %s cache with %lu dylibs, %lu other dylibs, %lu programs\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size());
-
- // build cache new cache file
- DyldSharedCache::CreateOptions options;
- options.archName = fileSet.archName;
- options.platform = platform;
- options.excludeLocalSymbols = true;
- options.optimizeStubs = false;
- options.optimizeObjC = true;
- options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ?
- DyldSharedCache::Agile : DyldSharedCache::SHA256only;
- options.dylibsRemovedDuringMastering = true;
- options.inodesAreSameAsRuntime = false;
- options.cacheSupportsASLR = true;
- options.forSimulator = false;
- options.isLocallyBuiltCache = true;
- options.verbose = verbose;
- options.evictLeafDylibsOnOverflow = false;
- DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
-
- // print any warnings
- for (const std::string& warn : results.warnings) {
- fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str());
- }
- if ( !results.errorMessage.empty() ) {
- // print error (if one)
- fprintf(stderr, "update_dyld_sim_shared_cache: %s\n", results.errorMessage.c_str());
- cacheBuildFailure = true;
- }
- else {
- // save new cache file to disk and write new .map file
- assert(results.cacheContent != nullptr);
- if ( !safeSave(results.cacheContent, results.cacheLength, outFile) )
- cacheBuildFailure = true;
- if ( !cacheBuildFailure ) {
- std::string mapStr = results.cacheContent->mapFile();
- std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
- safeSave(mapStr.c_str(), mapStr.size(), outFileMap);
- }
- // free created cache buffer
- vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength);
- }
- });
-
- // we could unmap all input files, but tool is about to quit
-
- return (cacheBuildFailure ? 1 : 0);
-}
-
*/
#include "mrm_shared_cache_builder.h"
-#include "CacheBuilder.h"
+#include "SharedCacheBuilder.h"
#include "ClosureFileSystem.h"
#include "FileUtils.h"
#include <pthread.h>
struct BuildInstance {
std::unique_ptr<DyldSharedCache::CreateOptions> options;
- std::unique_ptr<CacheBuilder> builder;
+ std::unique_ptr<SharedCacheBuilder> builder;
std::vector<CacheBuilder::InputFile> inputFiles;
std::vector<const char*> errors;
std::vector<const char*> warnings;
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;
}
};
-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);
}
*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);
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;
}
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;
}
}
}
-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");
}
options->dylibOrdering = parseOrderFile(builder->dylibOrderFileData);
options->dirtyDataSegmentOrdering = parseOrderFile(builder->dirtyDataOrderFileData);
- auto cacheBuilder = std::make_unique<CacheBuilder>(*options.get(), builder->fileSystem);
+ auto cacheBuilder = std::make_unique<SharedCacheBuilder>(*options.get(), builder->fileSystem);
builder->builders.emplace_back((BuildInstance) { std::move(options), std::move(cacheBuilder), inputFiles });
}
};
// 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.
// 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;
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) {
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
}
+++ /dev/null
-/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
- *
- * Copyright (c) 2016 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <Bom/Bom.h>
-#include <dispatch/dispatch.h>
-#include <copyfile.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <fcntl.h>
-#include <dlfcn.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/resource.h>
-#include <dirent.h>
-#include <libgen.h>
-#include <pthread.h>
-
-#include <vector>
-#include <array>
-#include <set>
-#include <map>
-#include <algorithm>
-
-#include "Manifest.h"
-#include "FileUtils.h"
-#include "BuilderUtils.h"
-
-#define CACHE_BUILDER_COPY_FILE_MODE COPYFILE_ALL
-
-#if !__has_feature(objc_arc)
-#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks
-#endif
-
-static dispatch_queue_t build_queue = dispatch_queue_create("com.apple.dyld.cache-builder.build", DISPATCH_QUEUE_CONCURRENT);
-
-#define kDylibCachePrefix "/AppleInternal/Developer/DylibCaches/"
-
-void createArtifact(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& dylibCachePath, bool includeExecutables)
-{
- auto copy_state = copyfile_state_alloc();
- mkpath_np((dylibCachePath + "/Metadata").c_str(), 0755);
- (void)copyfile(manifest.metabomFile().c_str(), (dylibCachePath + "/Metadata/metabom.bom").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
-
- if (!manifest.dylibOrderFile().empty()) {
- (void)copyfile(realPath(manifest.dylibOrderFile()).c_str(), (dylibCachePath + "/Metadata/dylibOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
- }
-
- if (!manifest.dirtyDataOrderFile().empty()) {
- (void)copyfile(realPath(manifest.dirtyDataOrderFile()).c_str(), (dylibCachePath + "/Metadata/dirtyDataOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
- }
-
- std::set<dyld3::UUID> uuids;
- std::map<std::string, std::string> copy_pairs;
-
- manifest.forEachConfiguration([&manifest, &uuids](const std::string& config) {
- manifest.configuration(config).forEachArchitecture([&manifest, &config, &uuids](const std::string& arch) {
- auto results = manifest.configuration(config).architecture(arch).results;
- for (const auto& image : results.dylibs) {
- uuids.insert(image.first);
- }
- for (const auto& image : results.bundles) {
- uuids.insert(image.first);
- }
- for (const auto& image : results.executables) {
- uuids.insert(image.first);
- }
- });
- });
-
- for (auto& uuid : uuids) {
- auto buildPath = manifest.buildPathForUUID(uuid);
- auto installPath = manifest.runtimePathForUUID(uuid);
- assert(!buildPath.empty() && !installPath.empty());
- copy_pairs.insert(std::make_pair(installPath, buildPath));
- }
-
- for (const auto& copy_pair : copy_pairs) {
- std::string from = realPath(copy_pair.second);
- std::string to = dylibCachePath + "/Root/" + copy_pair.first;
- mkpath_np(dirPath(to).c_str(), 0755);
- int err = copyfile(from.c_str(), to.c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE);
- diags.verbose("COPYING (%d) %s -> %s\n", err, from.c_str(), to.c_str());
-
- }
- copyfile_state_free(copy_state);
-
- fprintf(stderr, "[Artifact] dylibs copied\n");
-}
-
-void addArtifactPaths(Diagnostics& diags, dyld3::Manifest& manifest)
-{
- manifest.setDylibOrderFile("./Metadata/dylibOrderFile.txt");
- manifest.setDirtyDataOrderFile("./Metadata/dirtyDataOrderFile.txt");
- manifest.setMetabomFile("./Metadata/metabom.bom");
-
- for (auto& projects : manifest.projects()) {
- manifest.addProjectSource(projects.first, "./Root", true);
- }
-}
-
-int main(int argc, const char* argv[])
-{
- @autoreleasepool {
- __block Diagnostics diags;
- bool verbose = false;
- std::string masterDstRoot;
- std::string dylibCacheDir;
- std::string resultPath;
- std::string manifestPath;
- bool preflight = false;
- __block bool allBuildsSucceeded = true;
- bool skipWrites = false;
- bool skipBuilds = false;
- bool agileChooseSHA256CdHash = false;
- time_t mytime = time(0);
- fprintf(stderr, "Started: %s", asctime(localtime(&mytime)));
-
- // parse command line options
- for (int i = 1; i < argc; ++i) {
- const char* arg = argv[i];
- if (arg[0] == '-') {
- if (strcmp(arg, "-debug") == 0) {
- verbose = true;
- diags = Diagnostics(true);
- } else if (strcmp(arg, "-skip_writes") == 0) {
- skipWrites = true;
- } else if (strcmp(arg, "-skip_builds") == 0) {
- skipBuilds = true;
- } else if (strcmp(arg, "-delete_writes") == 0) {
- skipWrites = true;
- } else if (strcmp(arg, "-dylib_cache") == 0) {
- dylibCacheDir = argv[++i];
- } else if (strcmp(arg, "-preflight") == 0) {
- preflight = true;
- skipWrites = true;
- } else if (strcmp(arg, "-master_dst_root") == 0) {
- masterDstRoot = argv[++i];
- if (masterDstRoot.empty()) {
- diags.error("-master_dst_root missing path argument");
- }
- } else if (strcmp(arg, "-results") == 0) {
- resultPath = argv[++i];
- } else if (strcmp(arg, "-plist") == 0) {
- manifestPath = argv[++i];
- } else if (strcmp(arg, "-agile_choose_sha256_cdhash") == 0) {
- agileChooseSHA256CdHash = true;
- } else {
- // usage();
- diags.error("unknown option: %s", arg);
- }
- } else {
- manifestPath = argv[i];
- }
- }
-
- if (getenv("LGG_SKIP_CACHE_FUN") != nullptr) {
- skipBuilds = true;
- }
-
- if (diags.hasError()) {
- printf("%s\n", diags.errorMessage().c_str());
- exit(-1);
- }
-
- dispatch_async(dispatch_get_main_queue(), ^{
- if (manifestPath.empty()) {
- fprintf(stderr, "mainfest path argument is required\n");
- exit(-1);
- }
-
- (void)chdir(dirname(strdup(manifestPath.c_str())));
- __block auto manifest = dyld3::Manifest(diags, manifestPath);
-
- if (manifest.build().empty()) {
- fprintf(stderr, "No version found in manifest\n");
- exit(-1);
- }
-
- fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str());
-
- if (masterDstRoot.empty()) {
- fprintf(stderr, "-master_dst_root required path argument\n");
- exit(-1);
- }
-
- if (manifest.version() < 4) {
- fprintf(stderr, "must specify valid manifest file\n");
- exit(-1);
- }
-
- struct rlimit rl = { OPEN_MAX, OPEN_MAX };
- (void)setrlimit(RLIMIT_NOFILE, &rl);
-
- manifest.calculateClosure();
-
- if (!skipWrites && !skipBuilds) {
- (void)mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755);
- dispatch_group_async(buildGroup(), build_queue, ^{
- createArtifact(diags, manifest, masterDstRoot + "/Artifact.dlc/", true);
- });
- }
-
- if (!dylibCacheDir.empty()) {
- dispatch_group_async(buildGroup(), build_queue, ^{
- createArtifact(diags, manifest, dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/", false);
- });
- }
-
- if (!skipBuilds) {
- dispatch_group_async(buildGroup(), build_queue, ^{
- makeBoms(manifest, masterDstRoot);
- });
- allBuildsSucceeded = build(diags, manifest, masterDstRoot, true, verbose, skipWrites,
- agileChooseSHA256CdHash, true, false);
- }
-
- manifest.write(resultPath);
-
- addArtifactPaths(diags, manifest);
- if (!dylibCacheDir.empty()) {
- manifest.write(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.plist");
- manifest.writeJSON(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.json");
- }
-
- if (!skipWrites) {
- mkpath_np((masterDstRoot + "/Artifact.dlc").c_str(), 0755);
- auto copy_state = copyfile_state_alloc();
- (void)copyfile(realPath(manifestPath).c_str(), (masterDstRoot + "/Artifact.dlc/BNIManifest.plist").c_str(), copy_state, COPYFILE_ALL);
- copyfile_state_free(copy_state);
- manifest.write(masterDstRoot + "/Artifact.dlc/Manifest.plist");
- manifest.writeJSON(masterDstRoot + "/Artifact.dlc/Manifest.json");
- }
-
- dispatch_group_wait(buildGroup(), DISPATCH_TIME_FOREVER);
- time_t mytime = time(0);
- fprintf(stderr, "Finished: %s", asctime(localtime(&mytime)));
-
- if (preflight && !allBuildsSucceeded) {
- exit(-1);
- }
-
- exit(0);
- });
- }
- dispatch_main();
- return 0;
-}
*/
#ifndef __MACH_O_FIXUP_CHAINS__
-#define __MACH_O_FIXUP_CHAINS__
+#define __MACH_O_FIXUP_CHAINS__ 4
#include <stdint.h>
// 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
};
{
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
};
diversity : 16,
addrDiv : 1,
key : 2,
- next : 11, // 8-byte stide
+ next : 11, // 4 or 8-byte stide
bind : 1, // == 0
auth : 1; // == 1
};
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
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
}
}
- if (strcmp(segname, "__DATA") == 0)
- return getSection("__DATA_CONST", sectname);
+ if (strcmp(segname, "__DATA") == 0) {
+ if (const macho_section<P>* dataConst = getSection("__DATA_CONST", sectname))
+ return dataConst;
+ if (const macho_section<P>* dataDirty = getSection("__DATA_DIRTY", sectname))
+ return dataDirty;
+ }
return NULL;
}
#include <map>
#include <vector>
#include <iostream>
+#include <optional>
+#include "ClosureBuilder.h"
#include "DyldSharedCache.h"
+#include "ClosureFileSystemPhysical.h"
+#include "JSONWriter.h"
#include "Trie.hpp"
#include "objc-shared-cache.h"
#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)
{
modeInfo,
modeSize,
modeObjCProtocols,
+ modeObjCClasses,
+ modeObjCSelectors,
modeExtract
};
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;
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 ) {
}
}
}
+ else if ( options.mode == modeObjCClasses ) {
+ using dyld3::json::Node;
+ using ObjCClassInfo = dyld3::MachOAnalyzer::ObjCClassInfo;
+ const bool rebased = false;
+
+ // Build a map of class vm addrs to their names so that categories know the
+ // name of the class they are attaching to
+ __block std::map<uint64_t, const char*> classVMAddrToName;
+ __block std::map<uint64_t, const char*> metaclassVMAddrToName;
+ dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+ const uint32_t pointerSize = ma->pointerSize();
+
+ auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
+ uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+ const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+ dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
+ const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
+ if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+ return;
+
+ if (isMetaClass)
+ metaclassVMAddrToName[classVMAddr] = className;
+ else
+ classVMAddrToName[classVMAddr] = className;
+ };
+
+ Diagnostics diag;
+ ma->forEachObjCClass(diag, rebased, visitClass);
+ });
+
+ // These are used only for the on-disk binaries we analyze
+ __block std::vector<const char*> onDiskChainedFixupBindTargets;
+ __block std::map<uint64_t, const char*> onDiskClassVMAddrToName;
+ __block std::map<uint64_t, const char*> onDiskMetaclassVMAddrToName;
+
+ __block Node root;
+ auto makeNode = [](std::string str) -> Node {
+ Node node;
+ node.value = str;
+ return node;
+ };
+
+ auto getProperties = ^(const dyld3::MachOAnalyzer* ma, uint64_t propertiesVMAddr) {
+ __block Node propertiesNode;
+ auto visitProperty = ^(uint64_t propertyVMAddr, const dyld3::MachOAnalyzer::ObjCProperty& property) {
+ // Get the name
+ dyld3::MachOAnalyzer::PrintableStringResult propertyNameResult;
+ const char* propertyName = ma->getPrintableString(property.nameVMAddr, propertyNameResult);
+ if (propertyNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+ return;
+
+ // Get the attributes
+ dyld3::MachOAnalyzer::PrintableStringResult propertyAttributesResult;
+ const char* propertyAttributes = ma->getPrintableString(property.attributesVMAddr, propertyAttributesResult);
+ if (propertyAttributesResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+ return;
+
+ Node propertyNode;
+ propertyNode.map["name"] = makeNode(propertyName);
+ propertyNode.map["attributes"] = makeNode(propertyAttributes);
+ propertiesNode.array.push_back(propertyNode);
+ };
+ ma->forEachObjCProperty(propertiesVMAddr, rebased, visitProperty);
+ return propertiesNode.array.empty() ? std::optional<Node>() : propertiesNode;
+ };
+
+ auto getClasses = ^(const dyld3::MachOAnalyzer* ma) {
+ Diagnostics diag;
+ const uint32_t pointerSize = ma->pointerSize();
+
+ __block Node classesNode;
+ __block bool skippedPreviousClass = false;
+ auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
+ uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+ const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+ if (isMetaClass) {
+ if (skippedPreviousClass) {
+ // If the class was bad, then skip the meta class too
+ skippedPreviousClass = false;
+ return;
+ }
+ } else {
+ skippedPreviousClass = true;
+ }
+
+ std::string classType = "-";
+ if (isMetaClass)
+ classType = "+";
+ dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
+ const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
+ if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) {
+ return;
+ }
+
+ const char* superClassName = nullptr;
+ if ( ma->inDyldCache() ) {
+ if ( objcClass.superclassVMAddr != 0 ) {
+ if (isMetaClass) {
+ // If we are root class, then our superclass should actually point to our own class
+ const uint32_t RO_ROOT = (1<<1);
+ if ( objcClass.flags(pointerSize) & RO_ROOT ) {
+ auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
+ assert(it != classVMAddrToName.end());
+ superClassName = it->second;
+ } else {
+ auto it = metaclassVMAddrToName.find(objcClass.superclassVMAddr);
+ assert(it != metaclassVMAddrToName.end());
+ superClassName = it->second;
+ }
+ } else {
+ auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
+ assert(it != classVMAddrToName.end());
+ superClassName = it->second;
+ }
+ }
+ } else {
+ // On-disk binary. Lets crack the chain to work out what we are pointing at
+ dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup;
+ fixup.raw64 = objcClass.superclassVMAddr;
+ assert(!fixup.arm64e.authBind.auth);
+ if (fixup.arm64e.bind.bind) {
+ // Bind to another image. Use the bind table to work out which name to bind to
+ const char* symbolName = onDiskChainedFixupBindTargets[fixup.arm64e.bind.ordinal];
+ if (isMetaClass) {
+ if ( strstr(symbolName, "_OBJC_METACLASS_$_") == symbolName ) {
+ superClassName = symbolName + strlen("_OBJC_METACLASS_$_");
+ } else {
+ // Swift classes don't start with these prefixes so just skip them
+ if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
+ return;
+ }
+ } else {
+ if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
+ superClassName = symbolName + strlen("_OBJC_CLASS_$_");
+ } else {
+ // Swift classes don't start with these prefixes so just skip them
+ if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
+ return;
+ }
+ }
+ } else {
+ // Rebase within this image.
+ if (isMetaClass) {
+ auto it = onDiskMetaclassVMAddrToName.find(objcClass.superclassVMAddr);
+ assert(it != onDiskMetaclassVMAddrToName.end());
+ superClassName = it->second;
+ } else {
+ auto it = onDiskClassVMAddrToName.find(objcClass.superclassVMAddr);
+ assert(it != onDiskClassVMAddrToName.end());
+ superClassName = it->second;
+ }
+ }
+ }
+
+ // Print the methods on this class
+ __block Node methodsNode;
+ auto visitMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+ dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
+ const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
+ if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+ return;
+ methodsNode.array.push_back(makeNode(classType + methodName));
+ };
+ ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), rebased,
+ visitMethod);
+
+ std::optional<Node> properties = getProperties(ma, objcClass.basePropertiesVMAddr(pointerSize));
+
+ if (isMetaClass) {
+ assert(!classesNode.array.empty());
+ Node& currentClassNode = classesNode.array.back();
+ assert(currentClassNode.map["className"].value == className);
+ if (!methodsNode.array.empty()) {
+ Node& currentMethodsNode = currentClassNode.map["methods"];
+ currentMethodsNode.array.insert(currentMethodsNode.array.end(),
+ methodsNode.array.begin(),
+ methodsNode.array.end());
+ }
+ if (properties.has_value()) {
+ Node& currentPropertiesNode = currentClassNode.map["properties"];
+ currentPropertiesNode.array.insert(currentPropertiesNode.array.end(),
+ properties->array.begin(),
+ properties->array.end());
+ }
+ return;
+ }
+
+ Node currentClassNode;
+ currentClassNode.map["className"] = makeNode(className);
+ if ( superClassName != nullptr )
+ currentClassNode.map["superClassName"] = makeNode(superClassName);
+ if (!methodsNode.array.empty())
+ currentClassNode.map["methods"] = methodsNode;
+ if (properties.has_value())
+ currentClassNode.map["properties"] = properties.value();
+
+ // We didn't skip this class so mark it as such
+ skippedPreviousClass = false;
+
+ classesNode.array.push_back(currentClassNode);
+ };
+
+ ma->forEachObjCClass(diag, rebased, visitClass);
+ return classesNode.array.empty() ? std::optional<Node>() : classesNode;
+ };
+
+ auto getCategories = ^(const dyld3::MachOAnalyzer* ma) {
+ Diagnostics diag;
+
+ __block Node categoriesNode;
+ auto visitCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr,
+ const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
+ dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult;
+ const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult);
+ if (categoryNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+ return;
+
+ const char* className = nullptr;
+ if ( ma->inDyldCache() ) {
+ auto it = classVMAddrToName.find(objcCategory.clsVMAddr);
+ assert(it != classVMAddrToName.end());
+ className = it->second;
+ } else {
+ // On-disk binary. Lets crack the chain to work out what we are pointing at
+ dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup;
+ fixup.raw64 = objcCategory.clsVMAddr;
+ assert(!fixup.arm64e.authBind.auth);
+ if (fixup.arm64e.bind.bind) {
+ // Bind to another image. Use the bind table to work out which name to bind to
+ const char* symbolName = onDiskChainedFixupBindTargets[fixup.arm64e.bind.ordinal];
+ if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
+ className = symbolName + strlen("_OBJC_CLASS_$_");
+ } else {
+ // Swift classes don't start with these prefixes so just skip them
+ // We don't know that this is a Swift class/category though, but skip it anyway
+ return;
+ }
+ } else {
+ auto it = onDiskClassVMAddrToName.find(objcCategory.clsVMAddr);
+ if (it == onDiskClassVMAddrToName.end()) {
+ // This is an odd binary with perhaps a Swift class. Just skip this entry
+ return;
+ }
+ className = it->second;
+ }
+ }
+
+ // Print the instance methods on this category
+ __block Node methodsNode;
+ auto visitInstanceMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+ dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
+ const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
+ if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+ return;
+ methodsNode.array.push_back(makeNode(std::string("-") + methodName));
+ };
+ ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, rebased,
+ visitInstanceMethod);
+
+ // Print the instance methods on this category
+ __block Node classMethodsNode;
+ auto visitClassMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+ dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
+ const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
+ if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+ return;
+ methodsNode.array.push_back(makeNode(std::string("+") + methodName));
+ };
+ ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, rebased,
+ visitClassMethod);
+
+ Node currentCategoryNode;
+ currentCategoryNode.map["categoryName"] = makeNode(categoryName);
+ currentCategoryNode.map["className"] = makeNode(className);
+ if (!methodsNode.array.empty())
+ currentCategoryNode.map["methods"] = methodsNode;
+ if (std::optional<Node> properties = getProperties(ma, objcCategory.instancePropertiesVMAddr))
+ currentCategoryNode.map["properties"] = properties.value();
+
+ categoriesNode.array.push_back(currentCategoryNode);
+ };
+
+ ma->forEachObjCCategory(diag, rebased, visitCategory);
+ return categoriesNode.array.empty() ? std::optional<Node>() : categoriesNode;
+ };
+
+ dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+
+ Node imageRecord;
+ imageRecord.map["imagePath"] = makeNode(installName);
+ imageRecord.map["imageType"] = makeNode("cache-dylib");
+ std::optional<Node> classes = getClasses(ma);
+ std::optional<Node> categories = getCategories(ma);
+
+ // Skip emitting images with no objc data
+ if (!classes.has_value() && !categories.has_value())
+ return;
+ if (classes.has_value())
+ imageRecord.map["classes"] = classes.value();
+ if (categories.has_value())
+ imageRecord.map["categories"] = categories.value();
+
+ root.array.push_back(imageRecord);
+ });
+
+ FileSystemPhysical fileSystem;
+ dyld3::Platform platform = dyldCache->platform();
+ const dyld3::GradedArchs& archs = dyld3::GradedArchs::forName(dyldCache->archName(), true);
+
+ dyldCache->forEachLaunchClosure(^(const char *executableRuntimePath, const dyld3::closure::LaunchClosure *closure) {
+ Diagnostics diag;
+ char realerPath[MAXPATHLEN];
+ dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, executableRuntimePath, archs, platform, realerPath);
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+ uint32_t pointerSize = ma->pointerSize();
+
+ // Populate the bind targets for classes from other images
+ onDiskChainedFixupBindTargets.clear();
+ ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+ onDiskChainedFixupBindTargets.push_back(symbolName);
+ });
+ if ( diag.hasError() )
+ return;
+
+ // Populate the rebase targets for class names
+ onDiskMetaclassVMAddrToName.clear();
+ onDiskClassVMAddrToName.clear();
+ auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
+ uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+ const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+ dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
+ const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
+ if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+ return;
+
+ if (isMetaClass)
+ onDiskMetaclassVMAddrToName[classVMAddr] = className;
+ else
+ onDiskClassVMAddrToName[classVMAddr] = className;
+ };
+
+ ma->forEachObjCClass(diag, rebased, visitClass);
+
+ Node imageRecord;
+ imageRecord.map["imagePath"] = makeNode(executableRuntimePath);
+ imageRecord.map["imageType"] = makeNode("executable");
+ std::optional<Node> classes = getClasses(ma);
+ std::optional<Node> categories = getCategories(ma);
+
+ // Skip emitting images with no objc data
+ if (!classes.has_value() && !categories.has_value())
+ return;
+ if (classes.has_value())
+ imageRecord.map["classes"] = classes.value();
+ if (categories.has_value())
+ imageRecord.map["categories"] = categories.value();
+
+ root.array.push_back(imageRecord);
+ });
+
+ dyldCache->forEachDlopenImage(^(const char *runtimePath, const dyld3::closure::Image *image) {
+ Diagnostics diag;
+ char realerPath[MAXPATHLEN];
+ dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath, archs, platform, realerPath);
+ const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+ uint32_t pointerSize = ma->pointerSize();
+
+ // Populate the bind targets for classes from other images
+ onDiskChainedFixupBindTargets.clear();
+ ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+ onDiskChainedFixupBindTargets.push_back(symbolName);
+ });
+ if ( diag.hasError() )
+ return;
+
+ // Populate the rebase targets for class names
+ onDiskMetaclassVMAddrToName.clear();
+ onDiskClassVMAddrToName.clear();
+ auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
+ uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+ const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+ dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
+ const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
+ if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+ return;
+
+ if (isMetaClass)
+ onDiskMetaclassVMAddrToName[classVMAddr] = className;
+ else
+ onDiskClassVMAddrToName[classVMAddr] = className;
+ };
+
+ ma->forEachObjCClass(diag, rebased, visitClass);
+
+ Node imageRecord;
+ imageRecord.map["imagePath"] = makeNode(runtimePath);
+ imageRecord.map["imageType"] = makeNode("non-cache-dylib");
+ std::optional<Node> classes = getClasses(ma);
+ std::optional<Node> categories = getCategories(ma);
+
+ // Skip emitting images with no objc data
+ if (!classes.has_value() && !categories.has_value())
+ return;
+ if (classes.has_value())
+ imageRecord.map["classes"] = classes.value();
+ if (categories.has_value())
+ imageRecord.map["categories"] = categories.value();
+
+ root.array.push_back(imageRecord);
+ });
+
+ dyld3::json::printJSON(root, 0, std::cout);
+ }
+ else if ( options.mode == modeObjCSelectors ) {
+ if ( dyldCache->objcOpt() == nullptr ) {
+ fprintf(stderr, "Error: could not get optimized objc\n");
+ return 1;
+ }
+ const objc_opt::objc_selopt_t* selectors = dyldCache->objcOpt()->selopt();
+ if ( selectors == nullptr ) {
+ fprintf(stderr, "Error: could not get optimized objc selectors\n");
+ return 1;
+ }
+
+ std::vector<const char*> selNames;
+ for (uint64_t index = 0; index != selectors->capacity; ++index) {
+ objc_opt::objc_stringhash_offset_t offset = selectors->offsets()[index];
+ if ( offset == 0 )
+ continue;
+ const char* selName = selectors->getEntryForIndex((uint32_t)index);
+ selNames.push_back(selName);
+ }
+
+ std::sort(selNames.begin(), selNames.end(),
+ [](const char* a, const char* b) {
+ // Sort by offset, not string value
+ return a < b;
+ });
+
+ auto makeNode = [](std::string str) {
+ dyld3::json::Node node;
+ node.value = str;
+ return node;
+ };
+
+ dyld3::json::Node root;
+ for (const char* selName : selNames) {
+ dyld3::json::Node selNode;
+ selNode.map["selectorName"] = makeNode(selName);
+ selNode.map["offset"] = makeNode(dyld3::json::decimal((uint64_t)selName - (uint64_t)dyldCache));
+
+ root.array.push_back(selNode);
+ }
+
+ dyld3::json::printJSON(root, 0, std::cout);
+ }
else if ( options.mode == modeExtract ) {
char pathBuffer[PATH_MAX];
uint32_t bufferSize = PATH_MAX;
case modeSectionSizes:
case modeStrings:
case modeObjCProtocols:
+ case modeObjCClasses:
+ case modeObjCSelectors:
case modeExtract:
break;
}
--- /dev/null
+/*
+* Copyright (c) 2019 Apple Inc. All rights reserved.
+*
+* @APPLE_LICENSE_HEADER_START@
+*
+* "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
+* Reserved. This file contains Original Code and/or Modifications of
+* Original Code as defined in and that are subject to the Apple Public
+* Source License Version 1.0 (the 'License'). You may not use this file
+* except in compliance with the License. Please obtain a copy of the
+* License at http://www.apple.com/publicsource and read it before using
+* this file.
+*
+* The Original Code and all software distributed under the License are
+* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+* License for the specific language governing rights and limitations
+* under the License."
+*
+* @APPLE_LICENSE_HEADER_END@
+*/
+
+#import <XCTest/XCTest.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <objc/runtime.h>
+
+#include "test_support.h"
+
+struct TestInfo
+{
+ const char* testName;
+ const char* runLine;
+};
+
+#include "XCTestGenerated.h"
+
+@interface ContainerizedTestRunner : XCTestCase
+- (void)launchTest:(const char *)test withRunLine:(const char *)runLine;
+@end
+
+@implementation ContainerizedTestRunner
+#if 1
++ (void)registerTest:(const TestInfo &)info withEnv:(const char *)env andExtensions:(const char *)extension {
+ char *fullRunline = NULL;
+ asprintf(&fullRunline, "TMPDIR=/tmp/ TEST_OUTPUT=XCTest %s %s", env, info.runLine);
+ unsigned char hash[CC_SHA1_DIGEST_LENGTH];
+ char hashStr[CC_SHA1_DIGEST_LENGTH*2+1];
+ CC_SHA1(fullRunline, (CC_LONG)strlen(fullRunline), &hash[0]);
+ snprintf(&hashStr[0], CC_SHA1_DIGEST_LENGTH*2+1, "%x%x%x%x", hash[0], hash[1], hash[2], hash[3]);
+ char buffer[4096];
+ snprintf(&buffer[0], 4096, "test_%s_%s_%s", info.testName, extension, hashStr);
+ SEL newSel = sel_registerName(buffer);
+ IMP newIMP = imp_implementationWithBlock(^(id self) {
+ [self launchTest:info.testName withRunLine:fullRunline];
+ });
+ class_addMethod([self class], newSel, newIMP, "v@:");
+}
+
++ (void)load {
+ for (const TestInfo& info : sTests) {
+ [self registerTest:info withEnv:"TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0" andExtensions:"dyld2"];
+ [self registerTest:info withEnv:"TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0 DYLD_SHARED_REGION=avoid" andExtensions:"dyld2_nocache"];
+ [self registerTest:info withEnv:"TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1" andExtensions:"dyld3"];
+ [self registerTest:info withEnv:"TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 DYLD_SHARED_REGION=avoid" andExtensions:"dyld3_nocache"];
+#if 0
+ [self registerTest:info withEnv:"TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0 MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld2_leaks"];
+ [self registerTest:info withEnv:"TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0 DYLD_SHARED_REGION=avoid MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld2_nocache_leaks"];
+ [self registerTest:info withEnv:"TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld3_leaks"];
+ [self registerTest:info withEnv:"TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 DYLD_SHARED_REGION=avoid MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld3_nocache_leaks"];
+#endif
+ };
+}
+
+#else
+// This would be the way to do it if XCTest did not insist on using the selector name for differentiating results in the Xcode UI
++ (NSArray<NSInvocation *> *)testInvocations {
+ static NSArray<NSInvocation *> *invocations = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSMutableArray<NSInvocation *> *invocationArray = [[NSMutableArray alloc] init];
+ for (const TestInfo& info : sTests) {
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[ContainerizedTestRunner class]
+ instanceMethodSignatureForSelector:@selector(launchTest:withRunLine:)]];
+ invocation.selector = @selector(launchTest:withRunLine:);
+ [invocation setArgument:(void*)&info.testName atIndex:2];
+ [invocation setArgument:(void*)&info.runLine atIndex:3];
+ [invocationArray addObject:invocation];
+ }
+ invocations = [NSArray arrayWithArray:invocationArray];
+ });
+ return [invocations copy];
+}
+
+- (NSString *) name {
+ const char *testName = NULL;
+ const char *runLine = NULL;
+ [self.invocation getArgument:(void*)&testName atIndex:2];
+ [self.invocation getArgument:(void*)&runLine atIndex:3];
+ return [NSString stringWithFormat:@"%s: %s", testName, runLine];
+}
+
+- (NSString *)nameForLegacyLogging
+{
+ return self.name;
+}
+#endif
+
+- (void)setUp {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ _process process;
+ fprintf(stderr, "CHROOT: %s\n", CHROOT_PATH);
+ process.set_executable_path("/usr/sbin/chroot");
+ const char *args[] = {CHROOT_PATH, "/bin/sh", "-c", "/sbin/mount -t devfs devfs /dev", NULL};
+ process.set_args(args);
+ process.launch();
+ });
+}
+
+- (void)tearDown {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+}
+
+- (void) executeCommandInContainer:(const char *)command {
+ _process process;
+ process.set_executable_path("/usr/sbin/chroot");
+ const char *args[] = {CHROOT_PATH, "/bin/sh", "-c", command, NULL};
+ process.set_args(args);
+ __block dispatch_data_t output = NULL;
+ process.set_stdout_handler(^(int fd) {
+ ssize_t size = 0;
+ do {
+ char buffer[16384];
+ size = read(fd, &buffer[0], 16384);
+ if (size == -1) { break; }
+ dispatch_data_t data = dispatch_data_create(&buffer[0], size, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
+ if (!output) {
+ output = data;
+ } else {
+ output = dispatch_data_create_concat(output, data);
+ }
+ } while (size > 0);
+ });
+ process.set_exit_handler(^(pid_t pid) {
+ int status = 0;
+ (void)waitpid(pid, &status, 0);
+
+ int exitStatus = WEXITSTATUS(status);
+ if (exitStatus != 0) {
+ NSString *failure = [NSString stringWithFormat:@"Test exited with return code %d\n%s", exitStatus, command];
+ [self recordFailureWithDescription:failure inFile:@__FILE__ atLine:__LINE__ expected:NO];
+ return;
+ }
+ if (!output) {
+ NSString *failure = [NSString stringWithFormat:@"Test did not write any data to stdout\n%s", command];
+ [self recordFailureWithDescription:failure inFile:@__FILE__ atLine:__LINE__ expected:NO];
+ return;
+ }
+ NSError *error = nil;
+ NSDictionary *dict = [NSPropertyListSerialization propertyListWithData:(NSData *)output options:NSPropertyListImmutable format:nil error:&error];
+ if (!dict) {
+ NSString *failure = [NSString stringWithFormat:@"Could not convert stdout \"%@\" to property list. Got Error %@\n%s", output, error, command];
+ [self recordFailureWithDescription:failure inFile:@__FILE__ atLine:__LINE__ expected:NO];
+ return;
+ }
+ if (![dict[@"PASS"] boolValue]) {
+ NSString *failure = [NSString stringWithFormat:@"%@\n%s", dict[@"INFO"], command];
+ [self recordFailureWithDescription:failure inFile:dict[@"FILE"] atLine:[dict[@"LINE"] unsignedIntegerValue] expected:NO];
+ return;
+ }
+ });
+ process.launch();
+}
+
+
+- (void) launchTest:(const char *)test withRunLine:(const char *)runLine {
+ char command[4096];
+ snprintf(&command[0], 4096, "cd /AppleInternal/CoreOS/tests/dyld/%s; %s", test, runLine);
+ [self executeCommandInContainer:command];
+// sudo chroot . /bin/sh -c 'TEST_OUTPUT=BATS /AppleInternal/CoreOS/tests/dyld/dyld_get_sdk_version/sdk-check.exe'
+
+}
+
+//
+//- (void)testExample {
+// // This is an example of a functional test case.
+// // Use XCTAssert and related functions to verify your tests produce the correct results.
+//}
+//
+//- (void)testPerformanceExample {
+// // This is an example of a performance test case.
+// [self measureBlock:^{
+// // Put the code you want to measure the time of here.
+// }];
+//}
+
+@end
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
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
#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
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;
}
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 ) {
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);
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,
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 )
// 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);
logFixups = nullptr;
ml->fixupAllChainedFixups(diag, starts, fSlide, targetAddrs, logFixups);
+ if ( diag.hasError() )
+ throw strdup(diag.errorMessage());
}
void ImageLoaderMachOCompressed::registerInterposing(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:
if ( sAllCacheImagesProxy->hasDylib(path, &cacheIndex) )
return sAllCacheImagesProxy;
}
-#endif
-#if TARGET_OS_SIMULATOR
- // in simulators, 'path' has DYLD_ROOT_PATH prepended, but cache index does not have the prefix, so use orgPath
- const char* pathToFindInCache = orgPath;
-#else
- const char* pathToFindInCache = path;
#endif
uint statErrNo;
struct stat statBuf;
bool didStat = false;
bool existsOnDisk;
- dyld3::SharedCacheFindDylibResults shareCacheResults;
+ __block dyld3::SharedCacheFindDylibResults shareCacheResults;
shareCacheResults.image = nullptr;
- if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, pathToFindInCache, &shareCacheResults) ) {
+
+#if TARGET_OS_SIMULATOR
+
+ auto findSharedCacheImage = ^() {
+ // in simulators, 'path' has DYLD_ROOT_PATH prepended, but cache index does not have the prefix, so use orgPath
+ return dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, orgPath, &shareCacheResults);
+ };
+
+#else
+
+ auto findSharedCacheImage = ^() {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ if ( gLinkContext.iOSonMac ) {
+ // On iOSMac, we are also running with DYLD_ROOT_PATH set, but want to look up the orgPath
+ if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, orgPath, &shareCacheResults) )
+ return true;
+ }
+#endif
+ return dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults);
+ };
+
+#endif
+
+ if ( findSharedCacheImage() ) {
// see if this image in the cache was already loaded via a different path
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) {
ImageLoader* anImage = *it;
}
#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,
&munmap,
&madvise,
&stat,
- (fcntl_proc_t)&fcntl,
- (ioctl_proc_t)&ioctl,
+ &fcntl,
+ &ioctl,
&issetugid,
&getcwd,
&realpath,
// 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);
}
}
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);
#ifndef __DYLD_SYSCALL_HELPERS__
#define __DYLD_SYSCALL_HELPERS__
+#include <sys/cdefs.h>
+
#include <dirent.h>
+#if __has_include(<libamfi.h>)
+#include <libamfi.h>
+#else
+__BEGIN_DECLS
+extern int amfi_check_dyld_policy_self(uint64_t input_flags, uint64_t* output_flags);
+__END_DECLS
+#endif
+#include <libkern/OSAtomic.h>
+#include <libproc.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
#include <mach-o/loader.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/kdebug_private.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <System/sys/reason.h>
+
+#define DYLD_SYSCALL_VTABLE_ENTRY(x) __typeof__ (x) *x
#if __cplusplus
namespace dyld {
#endif
-
//
// This file contains the table of function pointers the host dyld supplies
// to the iOS simulator dyld.
struct SyscallHelpers
{
uintptr_t version;
- int (*open)(const char* path, int oflag, int extra);
- int (*close)(int fd);
- ssize_t (*pread)(int fd, void* buf, size_t nbyte, off_t offset);
- ssize_t (*write)(int fd, const void* buf, size_t nbyte);
- void* (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset);
- int (*munmap)(void* addr, size_t len);
- int (*madvise)(void* addr, size_t len, int advice);
- int (*stat)(const char* path, struct stat* buf);
- int (*fcntl)(int fildes, int cmd, void* result);
- int (*ioctl)(int fildes, unsigned long request, void* result);
- int (*issetugid)(void);
- char* (*getcwd)(char* buf, size_t size);
- char* (*realpath)(const char* file_name, char* resolved_name);
- kern_return_t (*vm_allocate)(vm_map_t target_task, vm_address_t *address, vm_size_t size, int flags);
- kern_return_t (*vm_deallocate)(vm_map_t target_task, vm_address_t address, vm_size_t size);
- kern_return_t (*vm_protect)(vm_map_t target_task, vm_address_t address, vm_size_t size, boolean_t max, vm_prot_t prot);
+ DYLD_SYSCALL_VTABLE_ENTRY(open);
+ DYLD_SYSCALL_VTABLE_ENTRY(close);
+ DYLD_SYSCALL_VTABLE_ENTRY(pread);
+ DYLD_SYSCALL_VTABLE_ENTRY(write);
+ DYLD_SYSCALL_VTABLE_ENTRY(mmap);
+ DYLD_SYSCALL_VTABLE_ENTRY(munmap);
+ DYLD_SYSCALL_VTABLE_ENTRY(madvise);
+ DYLD_SYSCALL_VTABLE_ENTRY(stat);
+ DYLD_SYSCALL_VTABLE_ENTRY(fcntl);
+ DYLD_SYSCALL_VTABLE_ENTRY(ioctl);
+ DYLD_SYSCALL_VTABLE_ENTRY(issetugid);
+ DYLD_SYSCALL_VTABLE_ENTRY(getcwd);
+ DYLD_SYSCALL_VTABLE_ENTRY(realpath);
+ DYLD_SYSCALL_VTABLE_ENTRY(vm_allocate);
+ DYLD_SYSCALL_VTABLE_ENTRY(vm_deallocate);
+ DYLD_SYSCALL_VTABLE_ENTRY(vm_protect);
void (*vlog)(const char* format, va_list list);
void (*vwarn)(const char* format, va_list list);
- int (*pthread_mutex_lock)(pthread_mutex_t* m);
- int (*pthread_mutex_unlock)(pthread_mutex_t* m);
- mach_port_t (*mach_thread_self)(void);
- kern_return_t (*mach_port_deallocate)(ipc_space_t task, mach_port_name_t name);
- mach_port_name_t(*task_self_trap)(void);
- kern_return_t (*mach_timebase_info)(mach_timebase_info_t info);
+ DYLD_SYSCALL_VTABLE_ENTRY(pthread_mutex_lock);
+ DYLD_SYSCALL_VTABLE_ENTRY(pthread_mutex_unlock);
+ DYLD_SYSCALL_VTABLE_ENTRY(mach_thread_self);
+ DYLD_SYSCALL_VTABLE_ENTRY(mach_port_deallocate);
+ DYLD_SYSCALL_VTABLE_ENTRY(task_self_trap);
+ DYLD_SYSCALL_VTABLE_ENTRY(mach_timebase_info);
+#if OSATOMIC_USE_INLINED
bool (*OSAtomicCompareAndSwapPtrBarrier)(void* old, void* nw, void * volatile *value);
void (*OSMemoryBarrier)(void);
+#else
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+ DYLD_SYSCALL_VTABLE_ENTRY(OSAtomicCompareAndSwapPtrBarrier);
+ DYLD_SYSCALL_VTABLE_ENTRY(OSMemoryBarrier);
+#pragma clang diagnostic pop
+#endif
void* (*getProcessInfo)(void); // returns dyld_all_image_infos*;
int* (*errnoAddress)(void);
- uint64_t (*mach_absolute_time)(void);
+ DYLD_SYSCALL_VTABLE_ENTRY(mach_absolute_time);
// Added in version 2
- kern_return_t (*thread_switch)(mach_port_name_t, int, mach_msg_timeout_t);
+ DYLD_SYSCALL_VTABLE_ENTRY(thread_switch);
// Added in version 3
- DIR* (*opendir)(const char* path);
- int (*readdir_r)(DIR* dirp, struct dirent* entry, struct dirent **result);
- int (*closedir)(DIR* dirp);
+ DYLD_SYSCALL_VTABLE_ENTRY(opendir);
+ DYLD_SYSCALL_VTABLE_ENTRY(readdir_r);
+ DYLD_SYSCALL_VTABLE_ENTRY(closedir);
// Added in version 4
void (*coresymbolication_load_notifier)(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header);
void (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header);
// Added in version 5
- int (*proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t buffersize);
- int (*getpid)(void);
- kern_return_t (*mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly);
- kern_return_t (*mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*);
- kern_return_t (*mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t);
+ DYLD_SYSCALL_VTABLE_ENTRY(proc_regionfilename);
+ DYLD_SYSCALL_VTABLE_ENTRY(getpid);
+ DYLD_SYSCALL_VTABLE_ENTRY(mach_port_insert_right);
+ DYLD_SYSCALL_VTABLE_ENTRY(mach_port_allocate);
+ DYLD_SYSCALL_VTABLE_ENTRY(mach_msg);
// Added in version 6
- void (*abort_with_payload)(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags);
+ DYLD_SYSCALL_VTABLE_ENTRY(abort_with_payload);
// Add in version 7
- kern_return_t (*task_register_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt);
- kern_return_t (*task_unregister_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt);
- kern_return_t (*task_get_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t *dyld_images, mach_msg_type_number_t *dyld_imagesCnt);
- kern_return_t (*task_register_dyld_shared_cache_image_info)(task_t task, dyld_kernel_image_info_t dyld_cache_image, boolean_t no_cache, boolean_t private_cache);
- kern_return_t (*task_register_dyld_set_dyld_state)(task_t task, uint8_t dyld_state);
- kern_return_t (*task_register_dyld_get_process_state)(task_t task, dyld_kernel_process_info_t *dyld_process_state);
- kern_return_t (*task_info)(task_name_t target_task, task_flavor_t flavor, task_info_t task_info_out, mach_msg_type_number_t *task_info_outCnt);
- kern_return_t (*thread_info)(thread_inspect_t target_act, thread_flavor_t flavor, thread_info_t thread_info_out, mach_msg_type_number_t *thread_info_outCnt);
- // Add in version 8
- bool (*kdebug_is_enabled)(uint32_t code);
- int (*kdebug_trace)(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
- // Add in version 9
- uint64_t (*kdebug_trace_string)(uint32_t debugid, uint64_t str_id, const char *str);
- // Add in version 10
- int (*amfi_check_dyld_policy_self)(uint64_t input_flags, uint64_t* output_flags);
- // Add in version 11
- void (*notifyMonitoringDyldMain)(void);
- void (*notifyMonitoringDyld)(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[], const char* imagePaths[]);
- // Add in version 12
- void (*mach_msg_destroy)(mach_msg_header_t *msg);
- kern_return_t (*mach_port_construct)(ipc_space_t task, mach_port_options_ptr_t options, mach_port_context_t context, mach_port_name_t *name);
- kern_return_t (*mach_port_destruct)(ipc_space_t task, mach_port_name_t name, mach_port_delta_t srdelta, mach_port_context_t guard);
- };
+ DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_image_infos);
+ DYLD_SYSCALL_VTABLE_ENTRY(task_unregister_dyld_image_infos);
+ DYLD_SYSCALL_VTABLE_ENTRY(task_get_dyld_image_infos);
+ DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_shared_cache_image_info);
+ DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_set_dyld_state);
+ DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_get_process_state);
+ DYLD_SYSCALL_VTABLE_ENTRY(task_info);
+ DYLD_SYSCALL_VTABLE_ENTRY(thread_info);
+ // Add in version 8
+ DYLD_SYSCALL_VTABLE_ENTRY(kdebug_is_enabled);
+ DYLD_SYSCALL_VTABLE_ENTRY(kdebug_trace);
+ // Add in version 9
+ DYLD_SYSCALL_VTABLE_ENTRY(kdebug_trace_string);
+ // Add in version 10
+ DYLD_SYSCALL_VTABLE_ENTRY(amfi_check_dyld_policy_self);
+ // Add in version 11
+ void (*notifyMonitoringDyldMain)(void);
+ void (*notifyMonitoringDyld)(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[], const char* imagePaths[]);
+ // Add in version 12
+ DYLD_SYSCALL_VTABLE_ENTRY(mach_msg_destroy);
+ DYLD_SYSCALL_VTABLE_ENTRY(mach_port_construct);
+ DYLD_SYSCALL_VTABLE_ENTRY(mach_port_destruct);
+ };
extern const struct SyscallHelpers* gSyscallHelpers;
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();
}
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);
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
// 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.
--- /dev/null
+#!/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")
+
+++ /dev/null
-#!/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)
-
+++ /dev/null
-#include <mach/mach.h>
-
-#include "darwintest.h"
-
-T_GLOBAL_META(T_META_NAMESPACE("dyld"));
-
-extern char** environ;
-
-#if __x86_64__
-cpu_type_t otherArch[] = { CPU_TYPE_I386 };
-#elif __i386__
-cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
-#elif __arm64__
-cpu_type_t otherArch[] = { CPU_TYPE_ARM };
-#elif __arm__
-cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
-#endif
-
-#define T_DECL_DYLD(name, description, ...) \
- static void dyld_test_ ## name(void); \
- T_DECL(name, description, ## __VA_ARGS__) { \
- dyld_test_ ## name(); \
- } \
- static void dyld_test_ ## name(void)
-
-/* Since the test runner manually invokes us in both dyld2 and dyld3 mode we do not need this yet
-
- T_DECL(name ## _dyld3, description, T_META_ENVVAR("DYLD_USE_CLOSURES=1"), ## __VA_ARGS__) { \
- dyld_test_ ## name(); \
- } \
-
- */
-
-
-#define T_DLOPEN_EXPECT_NOTNULL(path, mode) \
-({ \
- void* handle = dlopen(path, mode); \
- T_QUIET; T_ASSERT_NOTNULL(handle, "Image \"%s\" failed to load with error: %s", path, dlerror()); \
- T_QUIET; T_ASSERT_NULL(dlerror(), "dlerror() should be null after successfull dloepn()"); \
- handle; \
-})
-
-#define T_DLSYM_EXPECT_NOTNULL(handle, symbol) \
-({ \
- const void* sym = dlsym((void *)handle, symbol); \
- T_QUIET; T_ASSERT_NOTNULL(sym, "dlsym(%s) should not be null", symbol); \
- sym; \
-})
-
-#define T_DLCLOSE_EXPECT_NULL(handle) \
-({ \
- int result = dlclose((void *)handle); \
- T_QUIET; T_ASSERT_EQ_INT(result, 0, "dlclose() failed with error: %s", dlerror()); \
- result; \
-})
-
-#define T_POSIXSPAWN_ASSERT(launchSuspended, launchOtherArch, program) \
-({ \
- pid_t result = 0; \
- posix_spawnattr_t attr = 0; \
- T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_init(&attr), 0, "dyld_process_info posix_spawnattr_init() failed"); \
- if ( launchSuspended ) { \
- int result = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED); \
- T_QUIET; T_ASSERT_EQ_INT(result, 0, "posix_spawnattr_setflags() failed"); \
- } \
- if ( launchOtherArch ) { \
- size_t copied; \
- int result = posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied); \
- T_QUIET; T_ASSERT_EQ_INT(result, 0, "posix_spawnattr_setbinpref_np(), &copied) failed"); \
- } \
- const char* argv[] = { program, NULL }; \
- int psResult = posix_spawn(&result, program, NULL, &attr, (char**)argv, environ); \
- T_QUIET; T_ASSERT_EQ_INT(psResult, 0, "dyld_process_info posix_spawn(%s) failed, err=%d\n", program, psResult); \
- T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_destroy(&attr), KERN_SUCCESS, "posix_spawnattr_destroy() failed"); \
- result; \
-})
-
-#define T_TASK_FOR_PID_ASSERT(pid) \
-({ \
- task_t result; \
- T_QUIET; T_ASSERT_MACH_SUCCESS(task_for_pid(mach_task_self(), pid, &result), "task_for_pid() failed"); \
- result; \
-})
-
-#pragma pack(4)
-typedef struct exception_data {
- mach_msg_header_t Head;
- mach_msg_body_t msgh_body;
- mach_msg_port_descriptor_t thread;
- mach_msg_port_descriptor_t task;
- NDR_record_t NDR;
- exception_type_t exception;
- mach_msg_type_number_t codeCnt;
- __int64_t code[2];
-} exception_data;
-#pragma pack()
-
-typedef bool(^exceptionValidator)(task_t task);
-
-#define T_POSIXSPAWN_CRASH(program, validatorFunc, ...) \
-({ \
- pid_t pid = 0; \
- posix_spawnattr_t attr = 0; \
- mach_port_t exceptionPort = MACH_PORT_NULL; \
- T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_init(&attr), 0, "dyld_process_info posix_spawnattr_init() failed"); \
- mach_port_options_t options = { .flags = MPO_CONTEXT_AS_GUARD | MPO_STRICT | MPO_INSERT_SEND_RIGHT, .mpl = { 1 }}; \
- T_QUIET; T_ASSERT_MACH_SUCCESS(mach_port_construct(mach_task_self(), &options, (mach_port_context_t)exceptionPort, \
- &exceptionPort), "mach_port_construct() failed"); \
- int epResult = posix_spawnattr_setexceptionports_np(&attr, EXC_MASK_CORPSE_NOTIFY, exceptionPort, \
- EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0); \
- T_QUIET; T_ASSERT_EQ_INT(epResult, 0, "posix_spawnattr_setexceptionports_np() failed"); \
- const char* argv[] = { program, NULL }; \
- int psResult = posix_spawn(&pid, program, NULL, &attr, (char**)argv, environ); \
- T_QUIET; T_ASSERT_EQ_INT(psResult, 0, "dyld_process_info posix_spawn(%s) failed, err=%d\n", program, psResult); \
- T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_destroy(&attr), KERN_SUCCESS, "posix_spawnattr_destroy() failed"); \
- uint8_t data[MACH_MSG_SIZE_RELIABLE]; \
- exception_data* request = (exception_data*)&data[0]; \
- T_QUIET; T_ASSERT_MACH_SUCCESS(mach_msg(&request->Head, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, \
- MACH_MSG_SIZE_RELIABLE, exceptionPort, 10000, MACH_PORT_NULL), "mach_msg() failed"); \
- validatorFunc((task_t)request->task.name); \
-})
-#include <stdio.h>
-#include <stdarg.h>
+#ifndef __DYLD_TEST_SUPPORT_H__
+#define __DYLD_TEST_SUPPORT_H__ 1
+
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
#include <unistd.h>
-#include <mach-o/dyld.h>
-#include <sys/param.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <dispatch/dispatch.h>
+
+#if __cplusplus
+};
+
+// Only allow this interface for Objective-C++ due to typename and ARC issues in the default constructor
+
+typedef void (^_dyld_test_reader_t)(int fd);
+typedef void (^_dyld_test_exit_handler_t)(pid_t pid);
+typedef void (^_dyld_test_crash_handler_t)(task_t task);
+
+struct _process {
+ _process();
+ ~_process();
+ void set_executable_path(const char* EP);
+ void set_args(const char** A);
+ void set_env(const char** E);
+ void set_stdout_handler(_dyld_test_reader_t SOH);
+ void set_stderr_handler(_dyld_test_reader_t SEH);
+ void set_exit_handler(_dyld_test_exit_handler_t EH);
+ void set_crash_handler(_dyld_test_crash_handler_t CH);
+ void set_launch_suspended(bool S);
+ void set_launch_async(bool S);
+ void set_launch_arch(cpu_type_t A);
+ pid_t launch();
+ void *operator new(size_t size);
+ void operator delete(void *ptr);
+private:
+ const char* executablePath;
+ const char** args;
+ const char** env;
+ _dyld_test_reader_t stdoutHandler;
+ _dyld_test_reader_t stderrHandler;
+ _dyld_test_crash_handler_t crashHandler;
+ _dyld_test_exit_handler_t exitHandler;
+ pid_t pid;
+ cpu_type_t arch;
+ bool suspended;
+ bool async;
+};
+#endif /* __cplusplus */
+
+#define PASS(...) _PASS(__FILE__,__LINE__,__VA_ARGS__)
+#define FAIL(...) _FAIL(__FILE__,__LINE__,__VA_ARGS__)
+#define LOG(...) _LOG(__FILE__,__LINE__,__VA_ARGS__)
+#define TIMEOUT(seconds) _TIMEOUT(__FILE__,__LINE__,seconds)
+
+// MARK: Private implementation details
-#if __LP64__
-extern struct mach_header_64 __dso_handle;
-#else
-extern struct mach_header __dso_handle;
-#endif
+#if __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+__attribute__((format(printf, 3, 4)))
+__attribute__ ((noreturn))
+extern void _PASS(const char* file, unsigned line, const char* format, ...);
-static bool sIsATTY = false;
-static const char * sTestName = NULL;
-static uint64_t sTestCount = 0;
+__attribute__((format(printf, 3, 4)))
+__attribute__ ((noreturn))
+extern void _FAIL(const char* file, unsigned line, const char* format, ...);
-__attribute__((constructor))
-static
-void BEGIN(int argc, const char* argv[], const char* envp[]) {
- // Set up values we need to print in PASS() and FAIL()
- sIsATTY = isatty(fileno(stdout));
- sTestName = argv[0];
- // Early returnif this not the main executbale, we only need to print the [BEGIN] line once
- if (__dso_handle.filetype != MH_EXECUTE) {
- return;
- }
- printf("[BEGIN]");
- for (uint32_t i = 0; envp[i] != NULL; ++i) {
- if (strncmp("DYLD_", envp[i], 5) == 0) {
- printf(" %s", envp[i]);
- }
- }
- char buffer[MAXPATHLEN];
- uint32_t bufsize = MAXPATHLEN;
- if (_NSGetExecutablePath(buffer, &bufsize) == 0) {
- printf(" %s", buffer);
- } else {
- printf(" %s", argv[0]);
- }
- for (uint32_t i = 1; i < argc; ++i) {
- printf (" %s", argv[i]);
- }
- printf("\n");
-}
+__attribute__((format(printf, 3, 4)))
+extern void _LOG(const char* file, unsigned line, const char* format, ...);
-__attribute__((format(printf, 1, 2)))
-static
-void PASS(const char *format, ...) {
- if (sIsATTY) {
- printf("[\033[0;32mPASS\033[0m] %s (%llu): ", sTestName, sTestCount++);
- } else {
- printf("[PASS] %s (%llu): ", sTestName, sTestCount++);
- }
- va_list args;
- va_start (args, format);
- vprintf (format, args);
- va_end (args);
- printf("\n");
-}
+extern void _TIMEOUT(const char* file, unsigned line, uint64_t seconds);
+#if __cplusplus
+};
+#endif /* __cplusplus */
-__attribute__((format(printf, 1, 2)))
-static
-void FAIL(const char *format, ...) {
- if (sIsATTY) {
- printf("[\033[0;31mFAIL\033[0m] %s (%llu): ", sTestName, sTestCount++);
- } else {
- printf("[FAIL] %s (%llu): ", sTestName, sTestCount++);
- }
- va_list args;
- va_start (args, format);
- vprintf (format, args);
- va_end (args);
- printf("\n");
-}
+#endif /* __DYLD_TEST_SUPPORT_H__ */
--- /dev/null
+#include <mach/mach_exc.defs>
+
--- /dev/null
+#include <dlfcn.h>
+#include <Block.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <assert.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <os/lock.h>
+#include <sys/attr.h>
+#include <sys/wait.h>
+#include <mach/mach.h>
+#include <mach-o/dyld.h>
+#include <mach/mach_vm.h>
+#include <sys/fsgetpath.h>
+#include <mach-o/getsect.h>
+#include <mach/vm_region.h>
+#include <dispatch/private.h>
+#include <dispatch/dispatch.h>
+
+
+#include <atomic>
+#include <utility>
+#include <algorithm>
+
+extern "C" {
+#include "execserverServer.h"
+
+union catch_mach_exc_request_reply {
+ union __RequestUnion__catch_mach_exc_subsystem request;
+ union __ReplyUnion__catch_mach_exc_subsystem reply;
+};
+};
+
+#include "test_support.h"
+
+extern const int NXArgc;
+extern const char** NXArgv;
+extern const char** environ;
+extern char* __progname;
+#if __x86_64__
+static const cpu_type_t currentArch = CPU_TYPE_X86_64;
+#elif __i386__
+static const cpu_type_t currentArch = CPU_TYPE_I386;
+#elif __arm64__
+static const cpu_type_t currentArch = CPU_TYPE_ARM64;
+#elif __arm__
+static const cpu_type_t currentArch = CPU_TYPE_ARM;
+#endif
+
+namespace {
+struct ScopedLock {
+ ScopedLock() : _lock(OS_UNFAIR_LOCK_INIT) {}
+ template<typename F>
+ void withLock(F f) {
+ os_unfair_lock_lock(&_lock);
+ f();
+ os_unfair_lock_unlock(&_lock);
+ }
+private:
+ os_unfair_lock _lock;
+};
+
+template <typename T, int QUANT=4, int INIT=1>
+class GrowableArray
+{
+public:
+
+ T& operator[](size_t idx) { assert(idx < _usedCount); return _elements[idx]; }
+ const T& operator[](size_t idx) const { assert(idx < _usedCount); return _elements[idx]; }
+ T& back() { assert(_usedCount > 0); return _elements[_usedCount-1]; }
+ uintptr_t count() const { return _usedCount; }
+ uintptr_t maxCount() const { return _allocCount; }
+ bool empty() const { return (_usedCount == 0); }
+ uintptr_t index(const T& element) { return &element - _elements; }
+ void push_back(const T& t) { verifySpace(1); _elements[_usedCount++] = t; }
+ void pop_back() { assert(_usedCount > 0); _usedCount--; }
+ T* begin() { return &_elements[0]; }
+ T* end() { return &_elements[_usedCount]; }
+ const T* begin() const { return &_elements[0]; }
+ const T* end() const { return &_elements[_usedCount]; }
+ bool contains(const T& targ) const { for (const T& a : *this) { if ( a == targ ) return true; } return false; }
+ void erase(T& targ);
+
+protected:
+ void growTo(uintptr_t n);
+ void verifySpace(uintptr_t n) { if (this->_usedCount+n > this->_allocCount) growTo(this->_usedCount + n); }
+
+private:
+ T* _elements = _initialAlloc;
+ uintptr_t _allocCount = INIT;
+ uintptr_t _usedCount = 0;
+ T _initialAlloc[INIT] = { };
+};
+
+
+template <typename T, int QUANT, int INIT>
+inline void GrowableArray<T,QUANT,INIT>::growTo(uintptr_t n)
+{
+ uintptr_t newCount = (n + QUANT - 1) & (-QUANT);
+ T* newArray = (T*)::malloc(sizeof(T)*newCount);
+ T* oldArray = this->_elements;
+ if ( this->_usedCount != 0 )
+ ::memcpy(newArray, oldArray, sizeof(T)*this->_usedCount);
+ this->_elements = newArray;
+ this->_allocCount = newCount;
+ if ( oldArray != this->_initialAlloc )
+ ::free(oldArray);
+}
+
+template <typename T, int QUANT, int INIT>
+inline void GrowableArray<T,QUANT,INIT>::erase(T& targ)
+{
+ intptr_t index = &targ - _elements;
+ assert(index >= 0);
+ assert(index < (intptr_t)_usedCount);
+ intptr_t moveCount = _usedCount-index-1;
+ if ( moveCount > 0 )
+ ::memcpy(&_elements[index], &_elements[index+1], moveCount*sizeof(T));
+ _usedCount -= 1;
+}
+
+struct TestState {
+ TestState();
+ static TestState* getState();
+ void _PASSV(const char* file, unsigned line, const char* format, va_list args) __attribute__ ((noreturn));
+ void _FAILV(const char* file, unsigned line, const char* format, va_list args) __attribute__ ((noreturn));
+ void _LOGV(const char* file, unsigned line, const char* format, va_list args);
+ GrowableArray<std::pair<mach_port_t, _dyld_test_crash_handler_t>>& getCrashHandlers();
+private:
+ enum OutputStyle {
+ None,
+ BATS,
+ Console,
+ XCTest
+ };
+ void runLeaks();
+ void dumpLogs();
+ static uint8_t hexCharToUInt(const char hexByte, uint8_t* value);
+ static uint64_t hexToUInt64(const char* startHexByte, const char** endHexByte);
+
+ ScopedLock _IOlock;
+ GrowableArray<const char *> logs;
+ const char *testName;
+ bool logImmediate;
+ bool logOnSuccess;
+ bool checkForLeaks;
+ OutputStyle output;
+ GrowableArray<std::pair<mach_port_t, _dyld_test_crash_handler_t>> crashHandlers;
+};
+
+// Okay, this is tricky. We need something with roughly he semantics of a weak def, but without using weak defs as their presence
+// m ay impact certain tests. Instead we do the following:
+//
+// 1. Embed a stuct containing a lock and a pointer to our global state object in eahc binary
+// 2. Once per binary we walk the entire image list looking for the first entry that also has state data
+// 3. If it has state we lock its initializaion lock, and if it is not initialized we initialize it
+// 4. We then copy the initalized pointer into our own state, and unlock the initializer lock
+//
+// This should work because the image list forms a stable ordering. The one loose end is if an executable is running where logging
+// is only used in dylibs that are all being dlopned() and dlclosed. Since many dylibs cannot be dlclosed that should be a non-issue
+// in practice.
+};
+
+__attribute__((section("__DATA,__dyld_test")))
+static std::atomic<TestState*> sState;
+
+kern_return_t
+catch_mach_exception_raise(mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt)
+{
+ _dyld_test_crash_handler_t crashHandler = NULL;
+ for (const auto& handler : TestState::getState()->getCrashHandlers()) {
+ if (handler.first == exception_port) {
+ crashHandler = handler.second;
+ }
+ }
+ if (crashHandler) {
+ if (exception == EXC_CORPSE_NOTIFY) {
+ crashHandler(task);
+ } else {
+ return KERN_FAILURE;
+ }
+ }
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+catch_mach_exception_raise_state(mach_port_t exception_port,
+ exception_type_t exception,
+ const mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt,
+ int * flavor,
+ const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t * new_stateCnt)
+{
+ return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t
+catch_mach_exception_raise_state_identity(mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt,
+ int * flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t * new_stateCnt)
+{
+ return KERN_NOT_SUPPORTED;
+}
+
+_process::_process() : executablePath(nullptr), args(nullptr), env(nullptr), stdoutHandler(nullptr), stderrHandler(nullptr),
+ crashHandler(nullptr), exitHandler(nullptr), pid(0), arch(currentArch), suspended(false), async(false) {}
+_process::~_process() {
+ if (stdoutHandler) { Block_release(stdoutHandler);}
+ if (stderrHandler) { Block_release(stderrHandler);}
+ if (crashHandler) { Block_release(crashHandler);}
+ if (exitHandler) { Block_release(exitHandler);}
+}
+
+void _process::set_executable_path(const char* EP) { executablePath = EP; }
+void _process::set_args(const char** A) { args = A; }
+void _process::set_env(const char** E) { env = E; }
+void _process::set_stdout_handler(_dyld_test_reader_t SOH) { stdoutHandler = Block_copy(SOH); };
+void _process::set_stderr_handler(_dyld_test_reader_t SEH) { stderrHandler = Block_copy(SEH); }
+void _process::set_exit_handler(_dyld_test_exit_handler_t EH) { exitHandler = Block_copy(EH); }
+void _process::set_crash_handler(_dyld_test_crash_handler_t CH) { crashHandler = Block_copy(CH); }
+void _process::set_launch_suspended(bool S) { suspended = S; }
+void _process::set_launch_async(bool S) { async = S; }
+void _process::set_launch_arch(cpu_type_t A) { arch = A; }
+
+pid_t _process::launch() {
+ dispatch_queue_t queue = dispatch_queue_create("com.apple.dyld.test.launch", NULL);
+ dispatch_block_t oneShotSemaphoreBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
+ posix_spawn_file_actions_t fileActions = NULL;
+ posix_spawnattr_t attr = NULL;
+ dispatch_source_t stdoutSource = NULL;
+ dispatch_source_t stderrSource = NULL;
+ int stdoutPipe[2];
+ int stderrPipe[2];
+
+ if (posix_spawn_file_actions_init(&fileActions) != 0) {
+ FAIL("Setting up spawn filea actions");
+ }
+ if (posix_spawnattr_init(&attr) != 0) { FAIL("Setting up spawn attr"); }
+ if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0) {
+ FAIL("Setting up spawn attr: POSIX_SPAWN_START_SUSPENDED");
+ }
+
+ if (pipe(stdoutPipe) != 0) { FAIL("Setting up pipe"); }
+ if (posix_spawn_file_actions_addclose(&fileActions, stdoutPipe[0]) != 0) { FAIL("Setting up pipe"); }
+ if (posix_spawn_file_actions_adddup2(&fileActions, stdoutPipe[1], STDOUT_FILENO) != 0) { FAIL("Setting up pipe"); }
+ if (posix_spawn_file_actions_addclose(&fileActions, stdoutPipe[1]) != 0) { FAIL("Setting up pipe"); }
+ fcntl((int)stdoutPipe[0], F_SETFL, O_NONBLOCK);
+ stdoutSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)stdoutPipe[0], 0, queue);
+ dispatch_source_set_event_handler(stdoutSource, ^{
+ int fd = (int)dispatch_source_get_handle(stdoutSource);
+ if (stdoutHandler) {
+ stdoutHandler(fd);
+ } else {
+ char buffer[16384];
+ ssize_t size = 0;
+ do {
+ size = read(fd, &buffer[0], 16384);
+ } while (size > 0);
+ }
+ });
+ dispatch_source_set_cancel_handler(stdoutSource, ^{
+ dispatch_release(stdoutSource);
+ });
+ dispatch_resume(stdoutSource);
+
+ if (pipe(stderrPipe) != 0) { FAIL("Setting up pipe"); }
+ if (posix_spawn_file_actions_addclose(&fileActions, stderrPipe[0]) != 0) { FAIL("Setting up pipe"); }
+ if (posix_spawn_file_actions_adddup2(&fileActions, stderrPipe[1], STDERR_FILENO) != 0) { FAIL("Setting up pipe"); }
+ if (posix_spawn_file_actions_addclose(&fileActions, stderrPipe[1]) != 0) { FAIL("Setting up pipe"); }
+ fcntl((int)stderrPipe[0], F_SETFL, O_NONBLOCK);
+ stderrSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)stderrPipe[0], 0, queue);
+ dispatch_source_set_event_handler(stderrSource, ^{
+ int fd = (int)dispatch_source_get_handle(stderrSource);
+ if (stderrHandler) {
+ stderrHandler(fd);
+ } else {
+ char buffer[16384];
+ ssize_t size = 0;
+ do {
+ size = read(fd, &buffer[0], 16384);
+ } while (size > 0);
+ }
+ });
+ dispatch_source_set_cancel_handler(stderrSource, ^{
+ dispatch_release(stderrSource);
+ });
+ dispatch_resume(stderrSource);
+
+ if (crashHandler) {
+ auto& crashHandlers = TestState::getState()->getCrashHandlers();
+ mach_port_t exceptionPort = MACH_PORT_NULL;
+ mach_port_options_t options = { .flags = MPO_CONTEXT_AS_GUARD | MPO_STRICT | MPO_INSERT_SEND_RIGHT, .mpl = { 1 }};
+ if ( mach_port_construct(mach_task_self(), &options, (mach_port_context_t)exceptionPort, &exceptionPort) != KERN_SUCCESS ) {
+ FAIL("Could not construct port");
+ }
+ if (posix_spawnattr_setexceptionports_np(&attr, EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exceptionPort,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0) != 0) {
+ FAIL("posix_spawnattr_setexceptionports_np failed");
+ }
+ crashHandlers.push_back(std::make_pair(exceptionPort, crashHandler));
+ dispatch_source_t crashSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, exceptionPort, 0, queue);
+ dispatch_source_set_event_handler(crashSource, ^{
+ dispatch_mig_server(crashSource, sizeof(union catch_mach_exc_request_reply), ::mach_exc_server);
+ });
+ dispatch_source_set_cancel_handler(crashSource, ^{
+ mach_port_destruct(mach_task_self(), exceptionPort, 0, (mach_port_context_t)exceptionPort);
+ });
+ dispatch_resume(crashSource);
+ }
+
+ pid_t pid;
+ uint32_t argc = 0;
+ if (args) {
+ for (argc = 0; args[argc] != NULL; ++argc) {}
+ }
+ ++argc;
+ const char *argv[argc+1];
+ argv[0] = executablePath;
+ for (uint32_t i = 1; i < argc; ++i) {
+ argv[i] = args[i-1];
+ }
+ argv[argc] = NULL;
+
+ int result = posix_spawn(&pid, executablePath, &fileActions, &attr, (char **)argv, (char **)env);
+ if ( result != 0 ) {
+ FAIL("posix_spawn(%s) failed, err=%d", executablePath, result);
+ }
+ dispatch_source_t exitSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (pid_t)pid,
+ DISPATCH_PROC_EXIT, queue);
+ dispatch_source_set_event_handler(exitSource, ^{
+ if (exitHandler) {
+ exitHandler((pid_t)dispatch_source_get_handle(exitSource));
+ }
+ dispatch_source_cancel(exitSource);
+ if (stdoutSource) {
+ dispatch_source_cancel(stdoutSource);
+ }
+ if (stderrSource) {
+ dispatch_source_cancel(stderrSource);
+ }
+ oneShotSemaphoreBlock();
+ dispatch_source_cancel(exitSource);
+ });
+ dispatch_resume(exitSource);
+
+ if (stdoutHandler) {
+ close(stdoutPipe[1]);
+ }
+ if (stderrHandler) {
+ close(stderrPipe[1]);
+ }
+ if (fileActions) {
+ posix_spawn_file_actions_destroy(&fileActions);
+ }
+ posix_spawnattr_destroy(&attr);
+ if (!suspended) {
+ kill(pid, SIGCONT);
+ }
+ if (!async) {
+ dispatch_block_wait(oneShotSemaphoreBlock, DISPATCH_TIME_FOREVER);
+ }
+ Block_release(oneShotSemaphoreBlock);
+ dispatch_release(queue);
+ return pid;
+}
+
+void *_process::operator new(size_t size) {
+ return malloc(size);
+}
+
+void _process::operator delete(void *ptr) {
+ free(ptr);
+}
+
+// MARK: Private implementation details
+
+template<typename F>
+static
+void forEachEnvVar(const char* envp[], F&& f) {
+ for (uint32_t i = 0; envp[i] != nullptr; ++i) {
+ const char* envBegin = envp[i];
+ const char* envEnd = strchr(envp[i], '=');
+ if (!envEnd) { continue; }
+ size_t envSize = (envEnd-envBegin)+1;
+ const char* valBegin = envEnd+1;
+ const char* valEnd = strchr(envp[i], '\0');
+ if (!valEnd) { continue; }
+ size_t valSize = (valEnd-valBegin)+1;
+ char env[envSize];
+ char val[valSize];
+ strlcpy(&env[0], envBegin, envSize);
+ strlcpy(&val[0], valBegin, valSize);
+ f(&env[0], &val[0]);
+ }
+}
+
+uint8_t TestState::hexCharToUInt(const char hexByte, uint8_t* value) {
+ if (hexByte >= '0' && hexByte <= '9') {
+ *value = hexByte - '0';
+ return true;
+ } else if (hexByte >= 'A' && hexByte <= 'F') {
+ *value = hexByte - 'A' + 10;
+ return true;
+ } else if (hexByte >= 'a' && hexByte <= 'f') {
+ *value = hexByte - 'a' + 10;
+ return true;
+ }
+
+ return false;
+}
+
+uint64_t TestState::hexToUInt64(const char* startHexByte, const char** endHexByte) {
+ const char* scratch;
+ if (endHexByte == NULL) {
+ endHexByte = &scratch;
+ }
+ if (startHexByte == NULL)
+ return 0;
+ uint64_t retval = 0;
+ if (startHexByte[0] == '0' && startHexByte[1] == 'x') {
+ startHexByte +=2;
+ }
+ *endHexByte = startHexByte + 16;
+
+ //FIXME overrun?
+ for (uint32_t i = 0; i < 16; ++i) {
+ uint8_t value;
+ if (!hexCharToUInt(startHexByte[i], &value)) {
+ *endHexByte = &startHexByte[i];
+ break;
+ }
+ retval = (retval << 4) + value;
+ }
+ return retval;
+}
+
+TestState::TestState() : testName(__progname), logImmediate(false), logOnSuccess(false), checkForLeaks(false), output(Console) {
+ forEachEnvVar(environ, [this](const char* env, const char* val) {
+ if (strcmp(env, "TEST_LOG_IMMEDIATE") == 0) {
+ logImmediate = true;
+ }
+ if (strcmp(env, "TEST_LOG_ON_SUCCESS") == 0) {
+ logOnSuccess = true;
+ }
+ if (strcmp(env, "MallocStackLogging") == 0) {
+ checkForLeaks = true;
+ }
+ if (strcmp(env, "TEST_OUTPUT") == 0) {
+ if (strcmp(val, "BATS") == 0) {
+ output = BATS;
+ } else if (strcmp(val, "XCTest") == 0) {
+ output = XCTest;
+ }
+ }
+ });
+ if (output == BATS) {
+ printf("[BEGIN]");
+ if (checkForLeaks) {
+ printf(" MallocStackLogging=1 MallocDebugReport=none");
+ }
+ forEachEnvVar(environ, [this](const char* env, const char* val) {
+ if ((strncmp(env, "DYLD_", 5) == 0) || (strncmp(env, "TEST_", 5) == 0)) {
+ printf(" %s=%s", env, val);
+ }
+ });
+ printf(" %s", testName);
+ for (uint32_t i = 1; i < NXArgc; ++i) {
+ printf(" %s", NXArgv[i]);
+ }
+ printf("\n");
+ }
+}
+
+static std::atomic<TestState*>& getExecutableImageState() {
+ uint32_t imageCnt = _dyld_image_count();
+ for (uint32_t i = 0; i < imageCnt; ++i) {
+ #if __LP64__
+ const struct mach_header_64* mh = (const struct mach_header_64*)_dyld_get_image_header(i);
+ #else
+ const struct mach_header* mh = _dyld_get_image_header(i);
+ #endif
+ if (mh->filetype != MH_EXECUTE) {
+ continue;
+ }
+ size_t size = 0;
+ auto state = (std::atomic<TestState*>*)getsectiondata(mh, "__DATA", "__dyld_test", &size);
+ if (!state) {
+ fprintf(stderr, "Could not find test state in main executable TestState\n");
+ exit(0);
+ }
+ return *state;
+ }
+ fprintf(stderr, "Could not find test state in main executable\n");
+ exit(0);
+}
+
+GrowableArray<std::pair<mach_port_t, _dyld_test_crash_handler_t>>& TestState::getCrashHandlers() {
+ return crashHandlers;
+}
+
+TestState* TestState::getState() {
+ if (!sState) {
+ auto& state = getExecutableImageState();
+ if (state == nullptr) {
+ void *temp = malloc(sizeof(TestState));
+ auto newState = new (temp) TestState();
+ TestState* expected = nullptr;
+ if(!state.compare_exchange_strong(expected, newState)) {
+ newState->~TestState();
+ free(temp);
+ }
+ }
+ sState.store(state);
+ }
+ assert(sState != nullptr);
+ return sState;
+}
+
+__attribute__((noreturn))
+void TestState::runLeaks(void) {
+ auto testState = TestState::getState();
+ pid_t pid = getpid();
+ char pidString[32];
+ sprintf(&pidString[0], "%d", pid);
+ if (getuid() != 0) {
+ printf("Insufficient priviledges, skipping Leak check: %s\n", testState->testName);
+ exit(0);
+ }
+ const char *args[] = { pidString, NULL };
+ // We do this instead of using a dispatch_semaphore to prevent priority inversions
+ __block dispatch_data_t leaksOutput = NULL;
+ _process process;
+ process.set_executable_path("/usr/bin/leaks");
+ process.set_args(args);
+ process.set_stdout_handler(^(int fd) {
+ ssize_t size = 0;
+ do {
+ char buffer[16384];
+ size = read(fd, &buffer[0], 16384);
+ if (size == -1) { break; }
+ dispatch_data_t data = dispatch_data_create(&buffer[0], size, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
+ if (!leaksOutput) {
+ leaksOutput = data;
+ } else {
+ leaksOutput = dispatch_data_create_concat(leaksOutput, data);
+ }
+ } while (size > 0);
+ });
+ process.set_exit_handler(^(pid_t pid) {
+ int status = 0;
+ (void)waitpid(pid, &status, 0);
+
+ int exitStatus = WEXITSTATUS(status);
+ if (exitStatus == 0) {
+ PASS("No leaks");
+ } else {
+ if (leaksOutput) {
+ const void * buffer;
+ size_t size;
+ __unused dispatch_data_t map = dispatch_data_create_map(leaksOutput, &buffer, &size);
+ FAIL("Found Leaks:\n\n%s", buffer);
+ }
+ }
+ });
+
+ testState->checkForLeaks = false;
+ (void)process.launch();
+ exit(0);
+}
+
+void TestState::_PASSV(const char* file, unsigned line, const char* format, va_list args) {
+ if (output == None) {
+ exit(0);
+ }
+ if (checkForLeaks) {
+ runLeaks();
+ } else {
+ _IOlock.withLock([this,&format,&args,&file,&line](){
+ if (output == Console) {
+ printf("[\033[0;32mPASS\033[0m] %s: ", testName);
+ vprintf(format, args);
+ printf("\n");
+ if (logOnSuccess && logs.count()) {
+ printf("[\033[0;33mLOG\033[0m]\n");
+ for (const auto& log : logs) {
+ printf("\t%s\n", log);
+ }
+ }
+ } else if (output == BATS) {
+ printf("[PASS] %s: ", testName);
+ vprintf(format, args);
+ printf("\n");
+ if (logOnSuccess && logs.count()) {
+ printf("[LOG]\n");
+ for (const auto& log : logs) {
+ printf("\t%s\n", log);
+ }
+ }
+ } else if (output == XCTest) {
+ printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ printf("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
+ printf("<plist version=\"1.0\">");
+ printf("<dict>");
+ printf("<key>PASS</key><true />");
+ printf("</dict>");
+ printf("</plist>");
+ }
+ });
+ exit(0);
+ }
+}
+
+void _PASS(const char* file, unsigned line, const char* format, ...) {
+ va_list args;
+ va_start (args, format);
+ TestState::getState()->_PASSV(file, line, format, args);
+ va_end (args);
+}
+
+void TestState::_FAILV(const char* file, unsigned line, const char* format, va_list args) {
+ if (output == None) {
+ exit(0);
+ }
+ _IOlock.withLock([this,&format,&args,&file,&line](){
+ if (output == Console) {
+ printf("[\033[0;31mFAIL\033[0m] %s: ", testName);
+ vprintf(format, args);
+ printf("\n");
+ printf("[\033[0;33mLOG\033[0m]\n");
+ if (logs.count()) {
+ for (const auto& log : logs) {
+ printf("\t%s\n", log);
+ }
+ }
+ } else if (output == BATS) {
+ printf("[FAIL] %s: ", testName);
+ vprintf(format, args);
+ printf("\n");
+ if (logs.count()) {
+ printf("[LOG]\n");
+ for (const auto& log : logs) {
+ printf("\t%s\n", log);
+ }
+ }
+ } else if (output == XCTest) {
+ char *buffer;
+ printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ printf("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
+ printf("<plist version=\"1.0\">");
+ printf("<dict>");
+ printf("<key>PASS</key><false />");
+ printf("<key>FILE</key><string>%s</string>", file);
+ printf("<key>LINE</key><integer>%u</integer>", line);
+ vasprintf(&buffer, format, args);
+ printf("<key>INFO</key><string>%s</string>", buffer);
+ free(buffer);
+ printf("</dict>");
+ printf("</plist>");
+ }
+ });
+ exit(0);
+}
+
+void _FAIL(const char* file, unsigned line, const char* format, ...) {
+ va_list args;
+ va_start (args, format);
+ TestState::getState()->_FAILV(file, line, format, args);
+ va_end (args);
+}
+
+void TestState::_LOGV(const char* file, unsigned line, const char* format, va_list args) {
+ _IOlock.withLock([this,&format,&args](){
+ if (logImmediate) {
+ vprintf(format, args);
+ printf("\n");
+ } else {
+ char *str;
+ vasprintf(&str, format, args);
+ logs.push_back(str);
+ }
+ });
+}
+
+void _LOG(const char* file, unsigned line, const char* format, ...) {
+ va_list args;
+ va_start (args, format);
+ TestState::getState()->_LOGV(file, line, format, args);
+ va_end (args);
+}
+
+void _TIMEOUT(const char* file, unsigned line, uint64_t seconds) {
+ _LOG(file, line, "Registering %llu second test timeout", seconds);
+ dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
+ dispatch_time_t milestone = dispatch_time(DISPATCH_WALLTIME_NOW, seconds * NSEC_PER_SEC);
+ dispatch_source_set_timer(source, milestone, 0, 0);
+ dispatch_source_set_event_handler(source, ^{
+ FAIL("Test timed out");
+ });
+ dispatch_resume(source);
+}
--- /dev/null
+__PASS
+__FAIL
+__LOG
+__TIMEOUT
+__ZN8_process*
+__process*
+++ /dev/null
-#include <mach/mach_exc.defs>
+++ /dev/null
-#include "execserverServer.h"
-
-#include <mach/mach.h>
-#include <mach/vm_map.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <err.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <dispatch/dispatch.h>
-#include <errno.h>
-#include <signal.h>
-#include <libproc.h>
-#include <System/sys/reason.h>
-#include <System/sys/proc_info.h>
-
-
-
-static pid_t sChildPid;
-static dispatch_semaphore_t sServerRunning;
-static bool sChildCrashed = false;
-static bool sChildTerminatedByDyld = false;
-
-/*
- * setup exception handling port for EXC_CRASH and EXC_CORPSE_NOTIFY.
- * runs mach_msg_server once for receiving exception messages from kernel.
- */
-static void* serverCode(void* arg)
-{
- mach_port_t exception_port;
-
- kern_return_t kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port);
- if (kret != KERN_SUCCESS)
- errx(1, "mach_port_allocate: %s (%d)", mach_error_string(kret), kret);
-
- kret = mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND);
- if (kret != KERN_SUCCESS)
- errx(1, "mach_port_insert_right: %s (%d)", mach_error_string(kret), kret);
-
- kret = task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exception_port,
- EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0);
- if (kret != KERN_SUCCESS)
- errx(1, "task_set_exception_ports: %s (%d)", mach_error_string(kret), kret);
-
- dispatch_semaphore_signal(sServerRunning);
-
- kret = mach_msg_server(mach_exc_server, MACH_MSG_SIZE_RELIABLE, exception_port, 0);
- if (kret != KERN_SUCCESS)
- errx(1, "mach_msg_server: %s (%d)", mach_error_string(kret), kret);
-
- return NULL;
-}
-
-
-static void childDied(int sig)
-{
- struct proc_exitreasoninfo info;
- bzero(&info, sizeof(info));
- uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE];
- bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE);
- info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
- info.eri_kcd_buf = (user_addr_t)packReasonData;
- //fprintf(stderr, "info=%p\n", &info);
- if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) {
- printf("bad return size from proc_pidinfo()\n");
- return;
- }
- sChildTerminatedByDyld = (info.eri_namespace == OS_REASON_DYLD);
- }
-
-
-int main(int argc, const char* argv[])
-{
- if ( argc < 2 ) {
- fprintf(stderr, "usage: nocr [-require_crash] prog args...\n");
- return EXIT_FAILURE;
- }
- unsigned progArgIndex = 1;
- bool requireCrash = false;
- const char* testName = NULL;
- if ( strcmp(argv[1], "-require_crash") == 0 ) {
- progArgIndex = 2;
- requireCrash = true;
- testName = getenv("NOCR_TEST_NAME");
- if ( testName )
- printf("[BEGIN] %s\n", testName);
- }
-
- signal(SIGCHLD, childDied);
-
- sServerRunning = dispatch_semaphore_create(0);
-
- // start up thread for mach server which handles mach exception ports
- pthread_t serverThread;
- int result = pthread_create(&serverThread, NULL, serverCode, NULL);
- if ( result )
- err(EXIT_FAILURE, "pthread_create");
-
- // wait until server is up before starting child
- dispatch_semaphore_wait(sServerRunning, DISPATCH_TIME_FOREVER);
-
- // fork and exec child
- sChildPid = fork();
- if ( sChildPid < 0 )
- err(EXIT_FAILURE, "fork");
- if ( sChildPid == 0 ) {
- // child side
- result = execvp(argv[progArgIndex], (char**)&argv[progArgIndex]);
- err(EXIT_FAILURE, "exec(\"%s\",...)", argv[progArgIndex]);
- }
-
- // wait for child to finish (including crash)
- int status;
- int waitResult;
- int childResult = EXIT_FAILURE;
- do {
- waitResult = waitpid(sChildPid, &status, 0);
- } while ( (waitResult == -1) && (errno == EINTR) );
- if ( waitResult != -1 ) {
- if ( WIFEXITED(status) ) {
- childResult = WEXITSTATUS(status);
- }
- }
-
- if ( requireCrash ) {
- if ( testName ) {
- if ( sChildCrashed || sChildTerminatedByDyld )
- printf("[PASS] %s\n", testName);
- else
- printf("[FAIL] %s\n", testName);
- }
- return sChildCrashed ? EXIT_SUCCESS : EXIT_FAILURE;
- }
- else
- return childResult;
-}
-
-
-
-
-// Mach exception handler routines needed by execserverServer.c
-
-kern_return_t
-catch_mach_exception_raise(mach_port_t exception_port,
- mach_port_t thread,
- mach_port_t task,
- exception_type_t exception,
- mach_exception_data_t code,
- mach_msg_type_number_t codeCnt)
-{
- //fprintf(stderr, "child crashed\n");
- sChildCrashed = true;
- return KERN_SUCCESS;
-}
-
-kern_return_t
-catch_mach_exception_raise_state(mach_port_t exception_port,
- exception_type_t exception,
- const mach_exception_data_t code,
- mach_msg_type_number_t codeCnt,
- int * flavor,
- const thread_state_t old_state,
- mach_msg_type_number_t old_stateCnt,
- thread_state_t new_state,
- mach_msg_type_number_t * new_stateCnt)
-{
- errx(1, "Unsupported catch_mach_exception_raise_state");
- return KERN_NOT_SUPPORTED;
-}
-
-kern_return_t
-catch_mach_exception_raise_state_identity(mach_port_t exception_port,
- mach_port_t thread,
- mach_port_t task,
- exception_type_t exception,
- mach_exception_data_t code,
- mach_msg_type_number_t codeCnt,
- int * flavor,
- thread_state_t old_state,
- mach_msg_type_number_t old_stateCnt,
- thread_state_t new_state,
- mach_msg_type_number_t * new_stateCnt)
-{
- errx(1, "Unsupported catch_mach_exception_raise_state_identity");
- return KERN_NOT_SUPPORTED;
-}
-
-
-
--- /dev/null
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <mach/mach.h>
+
+#include "test_support.h"
+
+extern const char** environ;
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ if ( argc < 2 ) {
+ fprintf(stderr, "usage: nocr prog args...\n");
+ return EXIT_FAILURE;
+ }
+
+ _process process;
+ process.set_executable_path(argv[1]);
+ process.set_args(&argv[2]);
+ process.set_env(environ);
+ process.set_crash_handler(^(task_t task) {
+ exit(0);
+ });
+ process.set_exit_handler(^(pid_t pid) {
+ int status = 0;
+ (void)waitpid(pid, &status, 0);
+
+ // Only call exit if the child exited normally, otherwise keep running to consume the crash
+ if (WIFEXITED(status)) {
+ exit(0);
+ }
+ });
+ process.launch();
+ dispatch_main();
+}
-// BUILD: mkdir -p $BUILD_DIR/hideyhole
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/hideyhole/libfoo1.dylib -install_name /bad/path/libfoo1.dylib
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/hideyhole/libfoo2.dylib -install_name /bad/path2/libfoo2.dylib
// BUILD: $CC main.c -o $BUILD_DIR/LC_DYLD_ENV-DYLD_LIBRARY_PATH-main1.exe $BUILD_DIR/hideyhole/libfoo1.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@executable_path/hideyhole
#include <stdlib.h>
#include <dlfcn.h>
+#include "test_support.h"
+
/// Test that main executable's LC_DYLD_ENVIRONMENT can set DYLD_LIBRARY_PATH with @executable_path or @loader_path relative paths
extern char* __progname;
-int main()
-{
- printf("[BEGIN] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
void*h = dlopen("/other/path/libfoo2.dylib", 0);
if ( h != NULL )
- printf("[PASS] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
+ PASS("Success");
else
- printf("[FAIL] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
-
- return 0;
+ FAIL("Could not load libfoo2.dylib via LC_DYLD_ENVIRONMENT -> DYLD_LIBRARY_PATH");
}
#include <dlfcn.h>
#include <mach-o/dyld.h>
+#include "test_support.h"
-int main(int arg, const char* argv[])
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
const char* path = argv[1];
- printf("[BEGIN] NSAddImage-basic %s\n", path);
const struct mach_header* mh = NSAddImage(path, NSADDIMAGE_OPTION_WITH_SEARCHING);
if ( mh == NULL )
- printf("[FAIL] NSAddImage-basic %s\n", path);
+ FAIL("Could not load \"%s\"", path);
else
- printf("[PASS] NSAddImage-basic %s\n", path);
-
- return 0;
+ PASS("Success");
}
+++ /dev/null
-// BUILD_ONLY: MacOSX
-
-// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations
-
-// RUN: ./NSAddImage-fail.exe return
-// RUN: NOCR_TEST_NAME="NSAddImage-fail expected abort" $REQUIRE_CRASH ./NSAddImage-fail.exe abort
-
-
-
-#include <stdio.h>
-#include <string.h>
-#include <dlfcn.h>
-#include <mach-o/dyld.h>
-
-
-int main(int argc, const char* argv[])
-{
- const char* arg = argv[1];
-
- if ( strcmp(arg, "return") == 0 ) {
- printf("[BEGIN] NSAddImage-fail %s\n", arg);
- const struct mach_header* mh = NSAddImage("/xqz/42/libnotfound.xxx", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
- if ( mh == NULL )
- printf("[PASS] NSAddImage-fail %s\n", arg);
- else
- printf("[FAIL] NSAddImage-fail %s\n", arg);
- }
- else {
- // run with nocr which print BEGIN/PASS/FAIL
- NSAddImage("/xqz/42/libnotfound.xxx", 0);
- }
-
- return 0;
-}
-
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CXX main.cpp -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations -DRUN_DIR="$RUN_DIR"
+
+// RUN: ./NSAddImage-fail.exe return
+// RUN: ./NSAddImage-fail.exe abort
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <libproc.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+#include <System/sys/reason.h>
+#include <System/sys/proc_info.h>
+#include <System/kern/kern_cdata.h>
+
+#include "test_support.h"
+
+//FIXME:
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ const char* arg = argv[1];
+
+ if ( strcmp(arg, "return") == 0 ) {
+ const struct mach_header* mh = NSAddImage("/xqz/42/libnotfound.xxx", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
+ if ( mh == NULL )
+ PASS("Success");
+ else
+ FAIL("Got mh non-existent image");
+ } else if (strcmp(arg, "abort-child") == 0) {
+ // run with nocr which print BEGIN/PASS/FAIL
+ NSAddImage("/xqz/42/libnotfound.xxx", 0);
+ } else if (strcmp(arg, "abort") == 0) {
+ _process process;
+ process.set_executable_path(RUN_DIR "/NSAddImage-fail.exe");
+ const char* args[] = {"abort-child", NULL };
+ process.set_args(args);
+ const char* env[] = { "TEST_OUTPUT=None", NULL};
+ process.set_env(env);
+ process.set_crash_handler(^(task_t task) {
+ LOG("Crash for task=%u", task);
+ vm_address_t corpse_data;
+ uint32_t corpse_size;
+ if (task_map_corpse_info(mach_task_self(), task, &corpse_data, &corpse_size) != KERN_SUCCESS) {
+ FAIL("Could not read corpse data");
+ }
+ kcdata_iter_t autopsyData = kcdata_iter((void*)corpse_data, corpse_size);
+ if (!kcdata_iter_valid(autopsyData)) {
+ FAIL("Corpse Data Invalid");
+ }
+ kcdata_iter_t exitReasonData = kcdata_iter_find_type(autopsyData, EXIT_REASON_SNAPSHOT);
+ if (!kcdata_iter_valid(exitReasonData)) {
+ FAIL("Could not find exit data");
+ }
+ struct exit_reason_snapshot *ers = (struct exit_reason_snapshot *)kcdata_iter_payload(exitReasonData);
+
+ if ( ers->ers_namespace != OS_REASON_DYLD ) {
+ FAIL("eri_namespace (%d) != OS_REASON_DYLD", ers->ers_namespace);
+ }
+ if ( ers->ers_code != DYLD_EXIT_REASON_OTHER ) {
+ FAIL("eri_code (%lld) != DYLD_EXIT_REASON_OTHER", ers->ers_code);
+ }
+ kcdata_iter_t iter = kcdata_iter((void*)corpse_data, corpse_size);
+
+ KCDATA_ITER_FOREACH(iter) {
+ if (kcdata_iter_type(iter) == KCDATA_TYPE_NESTED_KCDATA) {
+ kcdata_iter_t nestedIter = kcdata_iter(kcdata_iter_payload(iter), kcdata_iter_size(iter));
+ if ( kcdata_iter_type(nestedIter) != KCDATA_BUFFER_BEGIN_OS_REASON ){
+ return;
+ }
+ kcdata_iter_t payloadIter = kcdata_iter_find_type(nestedIter, EXIT_REASON_USER_PAYLOAD);
+ if ( !kcdata_iter_valid(payloadIter) ) {
+ FAIL("invalid kcdata payload iterator from payload data");
+ }
+ const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter);
+
+ if ( dyldInfo->version != 1 ) {
+ FAIL("dyld payload is not version 1");
+ }
+ PASS("Success");
+ }
+ }
+ FAIL("Did not find EXIT_REASON_USER_PAYLOAD");
+ });
+ process.launch();
+ dispatch_main();
+ }
+ return 0;
+}
+
#include <dlfcn.h>
#include <mach-o/dyld.h>
+#include "test_support.h"
-int main(int argc, const char* argv[])
-{
- printf("[BEGIN] NSAddImage-loaded\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// verify value is returned for image already loaded
const struct mach_header* mh = NSAddImage("/usr/lib/libSystem.B.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
if ( mh == NULL )
- printf("[FAIL] NSAddImage-loaded\n");
+ FAIL("Could not find mh for libSystem.B.dylib");
// verify existing dylib is not loaded if it is not already loaded
mh = NSAddImage("/usr/lib/libz.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
if ( mh != NULL )
- printf("[FAIL] NSAddImage-loaded\n");
-
- printf("[PASS] NSAddImage-loaded\n");
+ FAIL("Found mh for unloaded dylib libz.dylib");
- return 0;
+ PASS("Success");
}
#include <dlfcn.h>
#include <mach-o/dyld.h>
-extern struct mach_header __dso_handle;
+#include "test_support.h"
-int main(int argc, const char* argv[])
-{
- printf("[BEGIN] NSAddressOfSymbol-basic\n");
+extern struct mach_header __dso_handle;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
if ( sym == NULL ) {
- printf("[FAIL] NSAddressOfSymbol-basic can't find main\n");
- return 0;
+ FAIL("can't find main");
}
void* mainAddr = NSAddressOfSymbol(sym);
if ( mainAddr != &main ) {
- printf("[FAIL] NSAddressOfSymbol-basic address returned %p is not &main=%p\n", mainAddr, &main);
- return 0;
+ FAIL("address returned %p is not &main=%p", mainAddr, &main);
}
// verify NULL works
if ( NSAddressOfSymbol(NULL) != NULL ) {
- printf("[FAIL] NSAddressOfSymbol-basic NULL not handle\n");
- return 0;
+ FAIL("NULL not handle");
}
- printf("[PASS] NSAddressOfSymbol-basic\n");
- return 0;
+ PASS("Success");
}
#include <dlfcn.h>
#include <mach-o/dyld.h>
+#include "test_support.h"
-int main(int argc, const char* argv[])
-{
- printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
const char* path = argv[1];
- NSObjectFileImage ofi;
- if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
- printf("[FAIL] NSCreateObjectFileImageFromFile failed\n");
- return 0;
- }
-
- NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
- if ( mod == NULL ) {
- printf("[FAIL] NSLinkModule failed\n");
- return 0;
- }
+ NSObjectFileImage ofi;
+ if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
+ FAIL("NSCreateObjectFileImageFromFile failed");
+ }
+
+ NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
+ if ( mod == NULL ) {
+ FAIL("NSLinkModule failed");
+ }
- NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
- if ( sym == NULL ) {
- printf("[FAIL] NSLookupSymbolInModule failed\n");
- return 0;
- }
+ NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
+ if ( sym == NULL ) {
+ FAIL("NSLookupSymbolInModule failed");
+ }
- void* func = NSAddressOfSymbol(sym);
- if ( func == NULL ) {
- printf("[FAIL] NSAddressOfSymbol failed\n");
- return 0;
- }
+ void* func = NSAddressOfSymbol(sym);
+ if ( func == NULL ) {
+ FAIL("NSAddressOfSymbol failed");
+ }
Dl_info info;
if ( dladdr(func, &info) == 0 ) {
- printf("[FAIL] dladdr(&p, xx) failed");
- return 0;
+ FAIL("dladdr(&p, xx) fail");
}
- //printf("_fooInBundle found in %s\n", info.dli_fname);
+ LOG("_fooInBundle found in %s", info.dli_fname);
- if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
- printf("[FAIL] NSUnLinkModule failed\n");
- return 0;
- }
+ if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+ FAIL("NSUnLinkModule failed");
+ }
if ( dladdr(func, &info) != 0 ) {
- printf("[FAIL] dladdr(&p, xx) found but should not have\n");
- return 0;
+ FAIL("dladdr(&p, xx) found but should not have");
}
if ( !NSDestroyObjectFileImage(ofi) ) {
- printf("[FAIL] NSDestroyObjectFileImage failed\n");
- return 0;
- }
+ FAIL("NSDestroyObjectFileImage failed");
+ }
- printf("[PASS] NSCreateObjectFileImageFromFile-basic\n");
- return 0;
+ PASS("Success");
}
#include <mach-o/dyld.h>
#include <vector>
-int main(int argc, const char* argv[])
-{
- printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n");
+#include "test_support.h"
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
const char* path = argv[1];
std::vector<NSObjectFileImage> ofis;
for (unsigned i = 0; i != 32; ++i) {
- NSObjectFileImage ofi;
- if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
- printf("[FAIL] NSCreateObjectFileImageFromFile failed\n");
- return 0;
- }
- ofis.push_back(ofi);
- }
-
- for (unsigned i = 0; i != 32; ++i) {
- NSObjectFileImage ofi = ofis[i];
- NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
- if ( mod == NULL ) {
- printf("[FAIL] NSLinkModule failed\n");
- return 0;
- }
-
- NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
- if ( sym == NULL ) {
- printf("[FAIL] NSLookupSymbolInModule failed\n");
- return 0;
- }
-
- void* func = NSAddressOfSymbol(sym);
- if ( func == NULL ) {
- printf("[FAIL] NSAddressOfSymbol failed\n");
- return 0;
- }
-
- Dl_info info;
- if ( dladdr(func, &info) == 0 ) {
- printf("[FAIL] dladdr(&p, xx) failed");
- return 0;
- }
- //printf("_fooInBundle found in %s\n", info.dli_fname);
-
- if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
- printf("[FAIL] NSUnLinkModule failed\n");
- return 0;
- }
-
- if ( dladdr(func, &info) != 0 ) {
- printf("[FAIL] dladdr(&p, xx) found but should not have\n");
- return 0;
- }
- }
-
- for (unsigned i = 0; i != 32; ++i) {
- NSObjectFileImage ofi = ofis[i];
- if ( !NSDestroyObjectFileImage(ofi) ) {
- printf("[FAIL] NSDestroyObjectFileImage failed\n");
- return 0;
- }
- }
-
- printf("[PASS] NSCreateObjectFileImageFromFile-basic\n");
- return 0;
+ NSObjectFileImage ofi;
+ if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
+ FAIL("NSCreateObjectFileImageFromFile failed");
+ }
+ ofis.push_back(ofi);
+ }
+
+ for(unsigned i = 0; i != 32; ++i) {
+ NSObjectFileImage ofi = ofis[i];
+ NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
+ if ( mod == NULL ) {
+ FAIL("NSLinkModule failed");
+ }
+
+ NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
+ if ( sym == NULL ) {
+ FAIL("NSLookupSymbolInModule failed");
+ }
+
+ void* func = NSAddressOfSymbol(sym);
+ if ( func == NULL ) {
+ FAIL("NSAddressOfSymbol failed");
+ }
+
+ Dl_info info;
+ if ( dladdr(func, &info) == 0 ) {
+ FAIL("dladdr(&p, xx) fail");
+ }
+ LOG("_fooInBundle found in %s", info.dli_fname);
+
+ if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+ FAIL("NSUnLinkModule failed");
+ }
+
+ if ( dladdr(func, &info) != 0 ) {
+ FAIL("dladdr(&p, xx) found but should not have");
+ }
+ }
+
+ for (unsigned i = 0; i != 32; ++i) {
+ NSObjectFileImage ofi = ofis[i];
+ if ( !NSDestroyObjectFileImage(ofi) ) {
+ FAIL("NSDestroyObjectFileImage failed");
+ }
+ }
+
+ PASS("Success");
}
#include <dlfcn.h>
#include <mach-o/dyld.h>
+#include "test_support.h"
static void checkBundle(const char* path, bool unlinkBeforeDestroy)
{
int fd = open(path, O_RDONLY, 0);
if ( fd == -1 ) {
- printf("[FAIL] open(%s) failed", path);
- exit(0);
+ FAIL("open(%s) failed", path);
}
struct stat stat_buf;
if ( fstat(fd, &stat_buf) == -1) {
- printf("[FAIL] fstat() failed\n");
- exit(0);
+ FAIL("fstat() failed");
}
void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
if ( loadAddress == ((void*)(-1)) ) {
- printf("[FAIL] mmap() failed\n");
- exit(0);
+ FAIL("mmap() failed");
}
close(fd);
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) {
- printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n");
- exit(0);
+ FAIL("NSCreateObjectFileImageFromMemory failed");
}
NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
if ( mod == NULL ) {
- printf("[FAIL] NSLinkModule failed\n");
- exit(0);
+ FAIL("NSLinkModule failed");
}
if ( !unlinkBeforeDestroy ) {
// API lets you destroy ofi and NSModule lives on
if ( !NSDestroyObjectFileImage(ofi) ) {
- printf("[FAIL] NSDestroyObjectFileImage failed\n");
- exit(0);
+ FAIL("NSDestroyObjectFileImage failed");
}
}
NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
if ( sym == NULL ) {
- printf("[FAIL] NSLookupSymbolInModule failed\n");
- exit(0);
+ FAIL("NSLookupSymbolInModule failed");
}
void* func = NSAddressOfSymbol(sym);
if ( func == NULL ) {
- printf("[FAIL] NSAddressOfSymbol failed\n");
- exit(0);
+ FAIL("NSAddressOfSymbol failed");
}
Dl_info info;
if ( dladdr(func, &info) == 0 ) {
- printf("[FAIL] dladdr(&p, xx) failed\n");
- exit(0);
+ FAIL("dladdr(&p, xx) failed");
}
- //printf("_fooInBundle found in %s\n", info.dli_fname);
+ LOG("_fooInBundle found in %s", info.dli_fname);
if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
- printf("[FAIL] NSUnLinkModule failed\n");
- exit(0);
+ FAIL("NSUnLinkModule failed");
}
if ( dladdr(func, &info) != 0 ) {
- printf("[FAIL] dladdr(&p, xx) found but should not have\n");
- exit(0);
+ FAIL("dladdr(&p, xx) found but should not have");
}
if ( unlinkBeforeDestroy ) {
if ( !NSDestroyObjectFileImage(ofi) ) {
- printf("[FAIL] NSDestroyObjectFileImage failed\n");
- exit(0);
+ FAIL("NSDestroyObjectFileImage failed");
}
}
}
-int main(int argc, const char* argv[])
-{
- printf("[BEGIN] NSCreateObjectFileImageFromMemory-basic\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
checkBundle(argv[1], true);
checkBundle(argv[1], false);
for (unsigned i = 0; i != 255; ++i)
checkBundle(argv[1], false);
- printf("[PASS] NSCreateObjectFileImageFromMemory-basic\n");
- return 0;
+ PASS("Success");
}
#include <dlfcn.h>
#include <mach-o/dyld.h>
-extern struct mach_header __dso_handle;
+#include "test_support.h"
-int main(int argc, const char* argv[])
-{
- printf("[BEGIN] NSLookupSymbolInImage-basic\n");
+extern struct mach_header __dso_handle;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// verify known symbol works
NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
if ( sym == NULL ) {
- printf("[FAIL] NSLookupSymbolInImage-basic _main\n");
- return 0;
+ FAIL("Did not find symnbol _main");
}
// verify mode where NSLookupSymbolInImage() returns NULL if symbol not found
sym = NSLookupSymbolInImage(&__dso_handle, "_42hhg", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
if ( sym != NULL ) {
- printf("[FAIL] NSLookupSymbolInImage-basic _42hhg\n");
- return 0;
+ FAIL("Did not find symnbol _42hhg");
}
// Note: NSLookupSymbolInImage is documented to abort if symbol not found and NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR not used,
// but dyld 2 just returned NULL, so no need to test that.
- printf("[PASS] NSLookupSymbolInImage-basic\n");
- return 0;
+ PASS("Success");
}
#import <Foundation/NSObject.h>
+#include "test_support.h"
+
// All the libraries have a copy of NSString
@interface NSString : NSObject
@end
return (Class)&OBJC_CLASS_$_NSString;
}
-extern int printf(const char*, ...);
-
extern id objc_getClass(const char *name);
// Get the NSString from liblinked1.dylib
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;
// 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);
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"
// 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
*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");
}
// BUILD_ONLY: MacOSX
-// BUILD: $CC missing.m -dynamiclib -o $TEMP_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc -Wl,-fixup_chains
+// BUILD: $CC missing.m -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc -Wl,-fixup_chains
// BUILD: $CC lib1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc -Wl,-fixup_chains
-// BUILD: $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $TEMP_DIR/libmissing.dylib -Wl,-fixup_chains
-// BUILD: $CC main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak-chained.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $TEMP_DIR/libmissing.dylib -Wl,-fixup_chains -lc++
+// BUILD: $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains
+// BUILD: $CXX main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak-chained.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains -lc++
+
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libmissing.dylib
// RUN: ./_dyld_for_each_objc_class-missing-weak-chained.exe
#import <Foundation/Foundation.h>
+#include "test_support.h"
+
// All the libraries have a copy of DyldClass
@interface DyldClass : NSObject
@end
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
static bool gotDyldClassMain = false;
static bool gotDyldClassLinked1 = false;
-int main() {
- printf("[BEGIN] _dyld_for_each_objc_class-missing-weak-chained\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// This API is only available with dyld3 and shared caches. If we have dyld2 then don't do anything
const char* testDyldMode = getenv("TEST_DYLD_MODE");
assert(testDyldMode);
sawClass = true;
});
if (sawClass) {
- printf("[FAIL] _dyld_for_each_objc_class: dyld2 shouldn't see any classes\n");
- return 0;
+ FAIL("dyld2 shouldn't see any classes");
}
- printf("[PASS] _dyld_for_each_objc_class (dyld2 or no shared cache)\n");
- return 0;
+ PASS("Success");
}
// Make sure libmissing.dylib is actually missing
if (&getMissingDyldClass != nil) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: libmissing needs to be missing\n");
- return 0;
+ FAIL("libmissing needs to be missing");
}
// DyldClass in liblinked1 should exist as its superclass is just NSObject
if (getLinked1DyldClass() == nil) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked1 DyldClass should exist\n");
- return 0;
+ FAIL("liblinked1 DyldClass should exist");
}
// DyldLinkedClass in liblinked1 should exist as its superclass is just NSObject
if (getLinked1DyldLinkedClass() == nil) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked1 DyldLinkedClass should exist\n");
- return 0;
+ FAIL("liblinked1 DyldLinkedClass should exist");
}
// DyldLinkedClass in liblinked2 should exist as its superclass is just NSObject
if (getLinked2DyldLinkedClass() == nil) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked2 DyldLinkedClass should exist\n");
- return 0;
+ FAIL("liblinked2 DyldLinkedClass should exist");
}
// Check that DyldMainClass comes main.exe as that is its only definition
id runtimeDyldMainClass = objc_getClass("DyldMainClass");
if (runtimeDyldMainClass != getMainDyldMainClass()) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldMainClass should have come from main.exe\n");
- return 0;
+ FAIL("DyldMainClass should have come from main.exe");
}
// Check that DyldClass comes liblinked1 as it should be missing from liblinked2
id runtimeDyldClass = objc_getClass("DyldClass");
if (runtimeDyldClass != getLinked1DyldClass()) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from liblinked1\n");
- return 0;
+ FAIL("DyldClass should have come from liblinked1");
}
// Check that DyldLinkedClass comes from liblinked2
#if 0
id runtimeDyldLinkedClass = objc_getClass("DyldLinkedClass");
if (runtimeDyldLinkedClass != getLinked2DyldLinkedClass()) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldLinkedClass should have come from liblinked2\n");
- return 0;
+ FAIL("DyldLinkedClass should have come from liblinked2");
}
#endif
// We should walk these in the order liblinked, main exe
if (!gotDyldClassLinked1) {
if (classPtr != getLinked1DyldClass()) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from liblinked1\n");
- *stop = true;
- return;
+ FAIL("DyldClass should have come from liblinked1");
}
if (!isLoaded) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass isLoaded should have been set on liblinked1\n");
- *stop = true;
- return;
+ FAIL("DyldClass isLoaded should have been set on liblinked1");
}
gotDyldClassLinked1 = true;
return;
}
if (!gotDyldClassMain) {
if (classPtr != getMainDyldClass()) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from main exe\n");
- *stop = true;
- return;
+ FAIL("DyldClass should have come from main exe");
}
if (!isLoaded) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass isLoaded should have been set on main exe\n");
- *stop = true;
- return;
+ FAIL("DyldClass isLoaded should have been set on main exe");
}
gotDyldClassMain = true;
return;
}
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: Unexpected DyldClass\n");
- return;
+ FAIL("Unexpected DyldClass");
});
if (!gotDyldClassLinked1) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have seen DyldClass in liblinked1\n");
- return 0;
+ FAIL("_dyld_for_each_objc_class should have seen DyldClass in liblinked1");
}
if (!gotDyldClassMain) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have seen DyldClass in main.exe\n");
- return 0;
+ FAIL("_dyld_for_each_objc_class should have seen DyldClass in main.exe");
}
// Visit again, and return liblinked1's DyldClass
*stop = true;
});
if (dyldClassImpl != getLinked1DyldClass()) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have returned DyldClass from liblinked1\n");
- return 0;
+ FAIL("_dyld_for_each_objc_class should have returned DyldClass from liblinked1");
}
// Visit again, and return liblinked1's DyldClass
*stop = true;
});
if (dyldClassImpl != getMainDyldClass()) {
- printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have returned DyldClass from main.exe\n");
- return 0;
+ FAIL("_dyld_for_each_objc_class should have returned DyldClass from main.exe");
}
- printf("[PASS] _dyld_for_each_objc_class-missing-weak-chained\n");
-
- return 0;
+ PASS("Success");
}
-// 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
#import <Foundation/Foundation.h>
+#include "test_support.h"
+
// All the libraries have a copy of DyldClass
@interface DyldClass : NSObject
@end
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
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);
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
#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
// 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
*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
*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");
}
#import <Foundation/Foundation.h>
+#include "test_support.h"
+
// All the libraries have a copy of DyldClass
@interface DyldClass : NSObject
@end
return (Class)&OBJC_CLASS_$_DyldMainClass;
}
-extern int printf(const char*, ...);
-
extern id objc_getClass(const char *name);
// Get the DyldClass from liblinked1.dylib
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);
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"
// 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
*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
*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");
}
#include <string.h>
#include <dlfcn.h>
+#include "test_support.h"
+
// All the libraries have a copy of DyldProtocol
@protocol DyldProtocol
@end
return (void*)@protocol(DyldMainProtocol);
}
-extern int printf(const char*, ...);
-
extern id objc_getProtocol(const char *name);
static bool gotDyldProtocolMain = 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);
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"
// 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;
});
*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
*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");
}
#include <mach-o/dyld.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] _dyld_get_image_slide-test\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
int count = _dyld_image_count();
for (int i=0; i < count; ++i) {
const struct mach_header* mh = _dyld_get_image_header(i);
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;
}
}
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");
}
#import <Foundation/Foundation.h>
+#include "test_support.h"
+
@interface DyldClass : NSObject
@end
}
@end
-extern int printf(const char*, ...);
-
extern id objc_getClass(const char *name);
-int main() {
- printf("[BEGIN] _dyld_get_objc_selector-chained\n");
-
- // dyldClassFoo
- const char* sel = _dyld_get_objc_selector("dyldClassFoo");
- if (sel) {
- if ((SEL)sel != @selector(dyldClassFoo)) {
- printf("[FAIL] _dyld_get_objc_selector-chained: dyldClassFoo is wrong\n");
- return 0;
- }
- }
-
- // dyldMainClassFoo
- sel = _dyld_get_objc_selector("dyldMainClassFoo");
- if (sel) {
- if ((SEL)sel != @selector(dyldMainClassFoo)) {
- printf("[FAIL] _dyld_get_objc_selector-chained: dyldMainClassFoo is wrong\n");
- return 0;
- }
- }
-
- // dyldMainClassFoo2
- sel = _dyld_get_objc_selector("dyldMainClassFoo2");
- if (sel) {
- if ((SEL)sel != @selector(dyldMainClassFoo2)) {
- printf("[FAIL] _dyld_get_objc_selector-chained: dyldMainClassFoo2 is wrong\n");
- return 0;
- }
- }
-
- printf("[PASS] _dyld_get_objc_selector-chained\n");
-
- return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ // dyldClassFoo
+ const char* sel = _dyld_get_objc_selector("dyldClassFoo");
+ if (sel) {
+ if ((SEL)sel != @selector(dyldClassFoo)) {
+ FAIL("dyldClassFoo is wrong");
+ }
+ }
+
+ // dyldMainClassFoo
+ sel = _dyld_get_objc_selector("dyldMainClassFoo");
+ if (sel) {
+ if ((SEL)sel != @selector(dyldMainClassFoo)) {
+ FAIL("dyldMainClassFoo is wrong");
+ }
+ }
+
+ // dyldMainClassFoo2
+ sel = _dyld_get_objc_selector("dyldMainClassFoo2");
+ if (sel) {
+ if ((SEL)sel != @selector(dyldMainClassFoo2)) {
+ FAIL("dyldMainClassFoo2 is wrong");
+ }
+ }
+
+ PASS("Success");
}
#include <mach-o/dyld.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] _dyld_get_objc_selector-shared-cache\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
size_t cacheLen;
uintptr_t cacheStart = (uintptr_t)_dyld_get_shared_cache_range(&cacheLen);
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");
}
#import <Foundation/Foundation.h>
+#include "test_support.h"
+
@interface DyldClass : NSObject
@end
@implementation DyldClass
--(void) dyldClassFoo {
-
-}
-+(void) dyldClassFoo {
-
-}
+-(void) dyldClassFoo {}
++(void) dyldClassFoo {}
@end
@interface DyldMainClass : NSObject
@end
@implementation DyldMainClass
--(void) dyldMainClassFoo {
-
-}
--(void) dyldMainClassFoo2 {
-
-}
+-(void) dyldMainClassFoo {}
+-(void) dyldMainClassFoo2 {}
@end
-extern int printf(const char*, ...);
-
extern id objc_getClass(const char *name);
-int main() {
- printf("[BEGIN] _dyld_get_objc_selector\n");
-
- // dyldClassFoo
- const char* sel = _dyld_get_objc_selector("dyldClassFoo");
- if (sel) {
- if ((SEL)sel != @selector(dyldClassFoo)) {
- printf("[FAIL] _dyld_get_objc_selector: dyldClassFoo is wrong\n");
- return 0;
- }
- }
-
- // dyldMainClassFoo
- sel = _dyld_get_objc_selector("dyldMainClassFoo");
- if (sel) {
- if ((SEL)sel != @selector(dyldMainClassFoo)) {
- printf("[FAIL] _dyld_get_objc_selector: dyldMainClassFoo is wrong\n");
- return 0;
- }
- }
-
- // dyldMainClassFoo2
- sel = _dyld_get_objc_selector("dyldMainClassFoo2");
- if (sel) {
- if ((SEL)sel != @selector(dyldMainClassFoo2)) {
- printf("[FAIL] _dyld_get_objc_selector: dyldMainClassFoo2 is wrong\n");
- return 0;
- }
- }
-
- printf("[PASS] _dyld_get_objc_selector\n");
-
- return 0;
-}
\ No newline at end of file
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ // dyldClassFoo
+ const char* sel = _dyld_get_objc_selector("dyldClassFoo");
+ if (sel) {
+ if ((SEL)sel != @selector(dyldClassFoo)) {
+ FAIL("dyldClassFoo is wrong");
+ }
+ }
+
+ // dyldMainClassFoo
+ sel = _dyld_get_objc_selector("dyldMainClassFoo");
+ if (sel) {
+ if ((SEL)sel != @selector(dyldMainClassFoo)) {
+ FAIL("dyldMainClassFoo is wrong");
+ }
+ }
+
+ // dyldMainClassFoo2
+ sel = _dyld_get_objc_selector("dyldMainClassFoo2");
+ if (sel) {
+ if ((SEL)sel != @selector(dyldMainClassFoo2)) {
+ FAIL("dyldMainClassFoo2 is wrong");
+ }
+ }
+
+ PASS("_dyld_get_objc_selector");
+
+ return 0;
+}
#include <uuid/uuid.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
+
extern void* __dso_handle;
extern int foo1();
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];
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");
}
#include <pthread.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
static void* work(void* mh)
{
// 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);
-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");
}
#include <ptrauth.h>
#endif
+#include "test_support.h"
+
static const void* stripPointer(const void* ptr)
{
#if __has_feature(ptrauth_calls)
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");
}
-#include <stdio.h>
-#include <stdlib.h>
#include <dlfcn.h>
+#include "test_support.h"
+
__attribute__((constructor))
-void bar() {
+void bar(int argc, const char* argv[], const char* envp[], const char* apple[]) {
void* handle = dlopen(RUN_DIR "/libbaz.dylib", RTLD_FIRST);
if ( handle == NULL ) {
- printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libbaz.dylib", dlerror());
- exit(0);
+ FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libbaz.dylib", dlerror());
}
}
#include <unordered_set>
+#include "test_support.h"
+
extern "C" void foo();
extern mach_header __dso_handle;
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);
}
-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
// 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");
}
#include <stdlib.h>
#include <dlfcn.h>
+#include "test_support.h"
+
__attribute__((constructor))
-void bar() {
+void bar(int argc, const char* argv[], const char* envp[], const char* apple[]) {
void* handle = dlopen(RUN_DIR "/libbaz.dylib", RTLD_FIRST);
if ( handle == NULL ) {
- printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libbaz.dylib", dlerror());
+ FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libbaz.dylib", dlerror());
exit(0);
}
}
#include <unordered_set>
+#include "test_support.h"
+
extern "C" void foo();
extern mach_header __dso_handle;
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
// 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");
}
#include <unordered_set>
+#include "test_support.h"
+
extern mach_header __dso_handle;
static std::unordered_set<const mach_header*> sCurrentImages;
static void notify(const mach_header* mh, intptr_t vmaddr_slide)
{
- //fprintf(stderr, "mh=%p\n", mh);
+ LOG("mh=%p", mh);
if ( sCurrentImages.count(mh) != 0 ) {
- printf("[FAIL] _dyld_register_func_for_add_image: notified twice about %p\n", mh);
- exit(0);
+ FAIL("notified twice about %p", mh);
}
sCurrentImages.insert(mh);
}
-int main()
-{
- printf("[BEGIN] _dyld_register_func_for_add_image\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
_dyld_register_func_for_add_image(¬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");
}
#include <mach-o/dyld_priv.h>
#include <dlfcn.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] _dyld_shared_cache_is_locally_built\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// We can't reliably test the result of this function, but it shouldn't crash
_dyld_shared_cache_is_locally_built();
- printf("[PASS] _dyld_shared_cache_is_locally_built\n");
-
- return 0;
+ PASS("SUCCESS");
}
#include <mach/mach.h>
#include <mach/mach_host.h>
+#include "test_support.h"
+
void tryPath(const char* prog, const char* path)
{
void* handle = dlopen(path, RTLD_LAZY);
#if HARDENED
if ( handle != NULL ) {
- printf("[FAIL] %s dlopen(%s) unexpectedly succeeded\n", prog, path);
+ FAIL("dlopen(%s) unexpectedly succeeded", path);
exit(0);
}
#else
if ( handle == NULL ) {
- printf("[FAIL] %s dlopen(%s) - %s\n", prog, path, dlerror());
+ FAIL("dlopen(%s) - %s", path, dlerror());
exit(0);
}
#endif
}
-int main(int arg, const char* argv[])
-{
- printf("[BEGIN] %s\n", argv[0]);
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// verify leaf name leads to dylib in /usr/lib/
void* handle = dlopen("libc.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("[FAIL] %s dlopen - %s\n", argv[0], dlerror());
- return 0;
+ FAIL("dlopen - %s", dlerror());
}
// verify file system relative paths: hardened should fail
tryPath(argv[0], "libmy.dylib");
tryPath(argv[0], "./libmy.dylib");
tryPath(argv[0], "../amfi-hardened-dlopen-leaf/libmy.dylib");
-
- printf("[PASS] %s\n", argv[0]);
-
- return 0;
+ PASS("Succcess");
}
#include <stdio.h>
+#include "test_support.h"
+
// Note this is weak so that we have a bind
__attribute__((weak))
void* p = 0;
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");
}
#include <dlfcn.h>
#include <assert.h>
+#include "test_support.h"
+
extern char tzname[]; // a char array in libSystem.dylib
}
#endif
-int main()
-{
- printf("[BEGIN] bind-rebase\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
verifyRebases();
verifyBinds();
verifyLongChains();
#endif
- printf("[PASS] bind-rebase\n");
- return 0;
+ PASS("Success");
}
#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");
+}
--- /dev/null
+// BUILD_ONLY: MacOSX
+// BUILD_MIN_OS: 10.5
+
+// BUILD: $CC main.c -o $BUILD_DIR/crt-vars10.5-libSystem.exe
+
+// RUN: ./crt-vars10.5-libSystem.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <crt_externs.h>
+#include <mach-o/ldsyms.h>
+
+#include "test_support.h"
+
+// This struct is passed as fifth parameter to libSystem.dylib's initializer so it record
+// the address of crt global variables.
+struct ProgramVars
+{
+ const void* mh;
+ int* NXArgcPtr;
+ char*** NXArgvPtr;
+ char*** environPtr;
+ char** __prognamePtr;
+};
+
+
+// global variables defeined in crt1.o
+extern char** NXArgv;
+extern int NXArgc;
+extern char** environ;
+extern char* __progname;
+
+
+static const struct ProgramVars* sVars;
+
+void __attribute__((constructor))
+myInit(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars)
+{
+ sVars = vars;
+}
+
+
+int main(int argc, const char* argv[])
+{
+ if ( _NSGetArgv() != &NXArgv ) {
+ FAIL("crt-libSystem: _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]);
+ }
+
+ if ( _NSGetArgc() != &NXArgc ) {
+ FAIL("crt-libSystem: _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]);
+ }
+
+ if ( _NSGetEnviron() != &environ ) {
+ FAIL("crt-libSystem: _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]);
+ }
+
+ if ( _NSGetProgname() != &__progname ) {
+ FAIL("crt-libSystem: _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]);
+ }
+
+ if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) {
+ FAIL("crt-libSystem: _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]);
+ }
+
+ if ( sVars->NXArgvPtr != &NXArgv ) {
+ FAIL("crt-libSystem: sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]);
+ }
+
+ if ( sVars->NXArgcPtr != &NXArgc ) {
+ FAIL("crt-libSystem: sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]);
+ }
+
+ if ( sVars->environPtr != &environ ) {
+ FAIL("crt-libSystem: sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]);
+ }
+
+ if ( sVars->__prognamePtr != &__progname ) {
+ FAIL("crt-libSystem: sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]);
+ }
+
+ if ( sVars->mh != &_mh_execute_header ) {
+ FAIL("crt-libSystem: sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]);
+ }
+
+ PASS("Success");
+}
+
--- /dev/null
+// BUILD_ONLY: MacOSX
+// BUILD_MIN_OS: 10.6
+
+// BUILD: $CC main.c -o $BUILD_DIR/crt-vars10.6-libSystem.exe
+
+// RUN: ./crt-vars10.6-libSystem.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <crt_externs.h>
+#include <mach-o/ldsyms.h>
+
+#include "test_support.h"
+
+// This struct is passed as fifth parameter to libSystem.dylib's initializer so it record
+// the address of crt global variables.
+struct ProgramVars
+{
+ const void* mh;
+ int* NXArgcPtr;
+ char*** NXArgvPtr;
+ char*** environPtr;
+ char** __prognamePtr;
+};
+
+
+// global variables defeined in crt1.o
+extern char** NXArgv;
+extern int NXArgc;
+extern char** environ;
+extern char* __progname;
+
+
+static const struct ProgramVars* sVars;
+
+void __attribute__((constructor))
+myInit(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars)
+{
+ sVars = vars;
+}
+
+
+int main(int argc, const char* argv[])
+{
+ if ( _NSGetArgv() != &NXArgv ) {
+ FAIL("crt-libSystem: _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]);
+ }
+
+ if ( _NSGetArgc() != &NXArgc ) {
+ FAIL("crt-libSystem: _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]);
+ }
+
+ if ( _NSGetEnviron() != &environ ) {
+ FAIL("crt-libSystem: _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]);
+ }
+
+ if ( _NSGetProgname() != &__progname ) {
+ FAIL("crt-libSystem: _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]);
+ }
+
+ if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) {
+ FAIL("crt-libSystem: _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]);
+ }
+
+ if ( sVars->NXArgvPtr != &NXArgv ) {
+ FAIL("crt-libSystem: sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]);
+ }
+
+ if ( sVars->NXArgcPtr != &NXArgc ) {
+ FAIL("crt-libSystem: sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]);
+ }
+
+ if ( sVars->environPtr != &environ ) {
+ FAIL("crt-libSystem: sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]);
+ }
+
+ if ( sVars->__prognamePtr != &__progname ) {
+ FAIL("crt-libSystem: sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]);
+ }
+
+ if ( sVars->mh != &_mh_execute_header ) {
+ FAIL("crt-libSystem: sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]);
+ }
+
+ PASS("Success");
+}
+
#include <crt_externs.h>
#include <mach-o/ldsyms.h>
+#include "test_support.h"
+
// This struct is passed as fifth parameter to libSystem.dylib's initializer so it record
// the address of crt global variables.
struct ProgramVars
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");
}
#include <stdio.h>
+#include "test_support.h"
+
extern int foo;
-int main()
-{
- printf("[BEGIN] cwd-relative-load\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
if ( foo == 42 )
- printf("[PASS] cwd-relative-load\n");
+ PASS("cwd-relative-load");
else
- printf("[FAIL] cwd-relative-load, wrong value\n");
-
- return 0;
+ FAIL("cwd-relative-load, wrong value");
}
// BUILD: $CC main-no-syms.c -o $BUILD_DIR/dladdr-stripped.exe
-// BUILD: strip $BUILD_DIR/dladdr-stripped.exe
+// BUILD: $STRIP $BUILD_DIR/dladdr-stripped.exe
// RUN: ./dladdr-stripped.exe
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
-
+#include "test_support.h"
///
/// verify dladdr() returns NULL for a symbol name in a fully stripped
/// main executable (and not _mh_execute_header+nnn).
///
-int main()
-{
- printf("[BEGIN] dladdr-stripped\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
Dl_info info;
if ( dladdr(&main, &info) == 0 ) {
- printf("[FAIL] dladdr(&main, xx) failed\n");
- return 0;
+ FAIL("dladdr(&main, xx) failed");
}
if ( info.dli_sname != NULL ){
- printf("[FAIL] dladdr() returned: \"%s\" instead of NULL\n", info.dli_sname);
- return 0;
+ FAIL("%s\" instead of NULL", info.dli_sname);
}
- printf("[PASS] dladdr-stripped\n");
- return 0;
+ PASS("Succes");
}
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
+
extern char** environ;
#if __has_feature(ptrauth_calls)
{
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");
}
}
{
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");
}
}
{
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");
}
}
{
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");
}
}
{
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");
}
}
{
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");
}
}
{
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();
verifymydata();
verifyNULL();
- printf("[PASS] dladdr-basic\n");
- return 0;
-}
+ PASS("Success");}
#include <ptrauth.h>
#endif
+#include "test_support.h"
+
extern void* __dso_handle;
{
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");
}
}
{
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");
}
}
{
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");
}
}
{
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");
}
}
#include <ptrauth.h>
#endif
+#include "test_support.h"
+
extern void* __dso_handle;
extern void verifyDylib();
{
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");
}
}
{
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");
}
}
{
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");
}
}
{
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();
verifyDylib();
- printf("[PASS] dladdr-dylib\n");
- return 0;
+ PASS("Success");
}
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
// verify dlclose() runs static terminator
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;
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");
}
-// BUILD: mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2
// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/door1/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=3
// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/door2/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=17
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=10 $BUILD_DIR/door1/libbar.dylib
#include <dlfcn.h>
#include <stdlib.h>
+#include "test_support.h"
+
// Program dlopen()s libfoo.dylib which was linked against libbar.dylib
// Neither have valid paths and must be found via DYLD_LIBRARY_PATH
// This test direct and indirect loading.
-int main(int argc, const char* argv[])
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
const char* env = getenv("DYLD_LIBRARY_PATH");
if ( env == NULL ) {
- printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n");
- printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, env not set\n");
- return 0;
+ FAIL("env not set");
}
const char* valueStr = argv[1];
if ( valueStr == NULL ) {
- printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n");
- printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, arg1 value not set\n");
- return 0;
+ FAIL("arg1 value not set");
}
char* end;
long value = strtol(valueStr, &end, 0);
- printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH %s\n", env);
- void* handle = dlopen("/bogus/libfoo.dylib", RTLD_LAZY);
- if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
- return 0;
- }
-
+ void* handle = dlopen("/bogus/libfoo.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("dlerror(\"/bogus/libfoo.dylib\"): %s", dlerror());
+ }
+
typedef int (*FooProc)();
- FooProc sym = (FooProc)dlsym(handle, "foo");
- if ( sym == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
- return 0;
- }
+ FooProc sym = (FooProc)dlsym(handle, "foo");
+ if ( sym == NULL ) {
+ FAIL("dlerror(): %s", dlerror());
+ }
int result = (*sym)();
if ( result != value ) {
- printf("result=%d, expected %ld (str=%s)\n", result, value, valueStr);
- printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
- return 0;
- }
+ FAIL("result=%d, expected %ld (str=%s)", result, value, valueStr);
+ }
int r = dlclose(handle);
if ( r != 0 ) {
- printf("dlclose() returned %d\n", r);
- printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
- return 0;
- }
-
- void* handle2 = dlopen("/junk/libfoo.dylib", RTLD_LAZY);
- if ( handle2 == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env);
- return 0;
- }
-
-
+ FAIL("dlclose() returned %d", r);
+ }
- printf("[PASS] dlopen-DYLD_LIBRARY_PATH %s\n", env);
+ void* handle2 = dlopen("/junk/libfoo.dylib", RTLD_LAZY);
+ if ( handle2 == NULL ) {
+ FAIL("dlerror(\"/junk/libfoo.dylib\"): %s", dlerror());
+ }
- return 0;
+ PASS("Success");
}
#include <string.h>
#include <dlfcn.h>
+#include "test_support.h"
///
/// This tests the interaction of RTLD_LOCAL and weak-def coalescing.
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
///
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
///
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");
}
///
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");
}
#include <string.h>
#include <dlfcn.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] dlopen-RTLD_LOCAL-hides\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
///
/// This tests that RTLD_LOCAL prevents RTLD_DEFAULT from finding symbols, but can be found via handle
///
void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LOCAL);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libfoo.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
- return 0;
+ FAIL("dlopen(\"libfoo.dylib\", RTLD_LOCAL) failed but it should have worked: %s", dlerror());
}
void* sym = dlsym(handle, "foo");
if ( sym == NULL ) {
- printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror());
- return 0;
+ FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", dlerror());
}
void* sym2 = dlsym(RTLD_DEFAULT, "foo");
if ( sym2 != NULL ) {
- printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"foo\") succeeded but it should have failed\n");
- return 0;
+ FAIL("dlsym(RTLD_DEFAULT, \"foo\") succeeded but it should have failed");
}
-
///
/// This tests that RTLD_GLOBAL after RTLD_LOCAL allows RTLD_DEFAULT to find symbols
///
void* handle2 = dlopen(RUN_DIR "/libfoo.dylib", RTLD_GLOBAL);
if ( handle2 == NULL ) {
- printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libfoo.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
- return 0;
+ FAIL("dlopen(\"libfoo.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror());
}
void* sym3 = dlsym(RTLD_DEFAULT, "foo");
if ( sym3 == NULL ) {
- printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"foo\") failed after upgrading to RTLD_GLOBAL\n");
- return 0;
+ FAIL("dlsym(RTLD_DEFAULT, \"foo\") failed after upgrading to RTLD_GLOBAL");
}
-
///
/// This tests that RTLD_LOCAL after RTLD_GLOBAL does not block RTLD_DEFAULT from finding symbols
///
void* handle3 = dlopen(RUN_DIR "/libbar.dylib", RTLD_GLOBAL);
if ( handle3 == NULL ) {
- printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libbar.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
- return 0;
+ FAIL("dlopen(\"libbar.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror());
}
void* handle4 = dlopen(RUN_DIR "/libbar.dylib", RTLD_LOCAL);
if ( handle4 == NULL ) {
- printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libbar.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
- return 0;
+ FAIL("dlopen(\"libbar.dylib\", RTLD_LOCAL) failed but it should have worked: %s", dlerror());
}
void* sym4 = dlsym(RTLD_DEFAULT, "bar");
if ( sym4 == NULL ) {
- printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"bar\") failed but it should have worked: %s\n", dlerror());
- return 0;
+ FAIL("dlsym(RTLD_DEFAULT, \"bar\") failed but it should have worked: %s", dlerror());
}
- printf("[PASS] dlopen-RTLD_LOCAL-hides\n");
- return 0;
+ PASS("Success");
}
#include <string.h>
#include <dlfcn.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] dlopen-RTLD_NODELETE\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
///
/// This tests that RTLD_NODELETE on first dlopen() blocks dlclose() from unloading image
///
void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NODELETE);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-RTLD_NODELETE: dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s\n", dlerror());
- return 0;
+ FAIL("dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s", dlerror());
}
int* fooSym = (int*)dlsym(handle, "foo");
if ( fooSym == NULL ) {
- printf("[FAIL] dlopen-RTLD_NODELETE: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror());
- return 0;
+ FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", dlerror());
}
int fooValue = *fooSym;
dlclose(handle);
Dl_info info;
if ( dladdr(fooSym, &info) != 0 ) {
- printf("[FAIL] dlopen-RTLD_NODELETE: dladdr(fooSym, xx) succeeded as if libfoo.dylib was not unloaded\n");
- return 0;
+ FAIL("dladdr(fooSym, xx) succeeded as if libfoo.dylib was not unloaded");
}
// dereference foo pointer. If RTLD_NODELETE worked, this will not crash
if ( *fooSym != fooValue ) {
- printf("[FAIL] dlopen-RTLD_NODELETE: value at fooSym changed\n");
- return 0;
+ FAIL("value at fooSym changed");
}
///
///
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");
}
#include <stdio.h>
#include <dlfcn.h>
+#include "test_support.h"
+
bool doneInitB = false;
bool inInitB = false;
__attribute__((constructor))
-void initB()
+void initB(int argc, const char* argv[], const char* envp[], const char* apple[])
{
inInitB = true;
// "upward" link to libInitA.dylib
void* handle = dlopen(RUN_DIR "/libInitA.dylib", RTLD_NOLOAD);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: dlopen(libInitA.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror());
+ FAIL("dlopen(\"libInitA.dylib\", RTLD_NOLOAD) failed but it should have worked: %s", dlerror());
return;
}
inInitB = false;
#include <stdbool.h>
#include <dlfcn.h>
+#include "test_support.h"
extern bool initsInWrongOrder;
extern bool allInitsDone();
-int main()
-{
- printf("[BEGIN] dlopen-RTLD_NOLOAD-in-initializer\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
///
/// This tests that using RTLD_NOLOAD in an initializer does not trigger out of order initializers
///
if ( initsInWrongOrder )
- printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: wrong init order\n");
+ FAIL("wrong init order");
else if ( !allInitsDone() )
- printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: all initializers not run\n");
+ FAIL("all initializers not run");
else
- printf("[PASS] dlopen-RTLD_NOLOAD-in-initializer\n");
-
- return 0;
+ PASS("Success");
}
// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dlopen-RTLD_NOLOAD-basic.exe
-// BUILD: cd $BUILD_DIR && ln -s libfoo.dylib libfoo-sym.dylib
+// BUILD: $SYMLINK libfoo.dylib $BUILD_DIR/libfoo-sym.dylib
// RUN: ./dlopen-RTLD_NOLOAD-basic.exe
#include <string.h>
#include <dlfcn.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] dlopen-RTLD_NOLOAD-basic\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
///
/// This tests that RTLD_NOLOAD finds existing dylib statically linked
///
void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOLOAD);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libfoo.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror());
- return 0;
+ FAIL("dlopen(\"libfoo.dylib\", RTLD_NOLOAD) failed but it should have worked: %s", dlerror());
}
void* sym = dlsym(handle, "foo");
if ( sym == NULL ) {
- printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror());
- return 0;
+ FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", dlerror());
}
///
///
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");
}
///
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());
}
///
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");
}
// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib
-// BUILD: $CC bar.c -dynamiclib -o $TEMP_DIR/libbar-present.dylib -install_name $RUN_DIR/libbar.dylib -DHAS_SYMBOL=1
-// BUILD: $CC foo.c -dynamiclib -Os -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib $TEMP_DIR/libbar-present.dylib
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/libbar-present.dylib -install_name $RUN_DIR/libbar.dylib -DHAS_SYMBOL=1
+// BUILD: $CC foo.c -dynamiclib -Os -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib $BUILD_DIR/libbar-present.dylib
// BUILD: $CC main.c -o $BUILD_DIR/dlopen-RTLD_NOW.exe -DRUN_DIR="$RUN_DIR"
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libbar-present.dylib
+
// RUN: ./dlopen-RTLD_NOW.exe
#include <stdio.h>
#include <dlfcn.h>
#include <mach-o/getsect.h>
+#include "test_support.h"
+
#if __LP64__
extern struct mach_header_64 __dso_handle;
#else
extern struct mach_header __dso_handle;
#endif
-int main()
-{
- printf("[BEGIN] dlopen-RTLD_NOW\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
///
/// This tests that RTLD_NOW on dlopen() will return NULL because call from libfoo to libbar could not be bound
///
void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOW);
if ( handle != NULL ) {
- printf("[FAIL] dlopen-RTLD_NOW: dlopen(libfoo.dylib, RTLD_NOW) should have failed\n");
- return 0;
+ FAIL("dlopen(\"libfoo.dylib\", RTLD_NOW) should have failed");
}
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");
}
// BUILD_ONLY: MacOSX
-// BUILD: mkdir -p $BUILD_DIR/test1
// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/test1/libtest1.dylib -install_name @rpath/libtest1.dylib
// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test1.bundle -Wl,-rpath,@loader_path/test1/ $BUILD_DIR/test1/libtest1.dylib
-// BUILD: mkdir -p $BUILD_DIR/test2
// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/test2/libtest2.dylib -install_name @loader_path/test2/libtest2.dylib
// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test2.bundle $BUILD_DIR/test2/libtest2.dylib
-// BUILD: mkdir -p $BUILD_DIR/test3
// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/test3/libtest3.dylib -install_name @rpath/libtest3.dylib
// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test3.bundle -Wl,-rpath,$RUN_DIR/test3 $BUILD_DIR/test3/libtest3.dylib
-// BUILD: mkdir -p $BUILD_DIR/test4
// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/test4/libtest4.dylib -install_name @rpath/libtest4.dylib
// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test4.bundle -Wl,-rpath,@executable_path/test4/ $BUILD_DIR/test4/libtest4.dylib
-// BUILD: mkdir -p $BUILD_DIR/test5
// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/test5/libtest5.dylib -install_name @executable_path/test5/libtest5.dylib
// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test5.bundle $BUILD_DIR/test5/libtest5.dylib
-
-
// BUILD: $CC main.c -o $BUILD_DIR/dlopen-restricted.exe -DRUN_DIR="$RUN_DIR" -sectcreate __RESTRICT __restrict /dev/null
// RUN: ./dlopen-restricted.exe
#include <stdio.h>
#include <dlfcn.h>
+#include "test_support.h"
-int main(int argc, const char* argv[])
-{
- printf("[BEGIN] dlopen-restricted\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// test1: LC_RPATH not in main executable uses @loader_path
void* handle = dlopen(RUN_DIR "/test1.bundle", RTLD_LAZY);
if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-restricted test1.bundle\n");
- return 0;
+ FAIL("test1.bundle dlerror(): %s", dlerror());
}
// test2: @loader_path not in main executable
handle = dlopen(RUN_DIR "/test2.bundle", RTLD_LAZY);
if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-restricted test2.bundle\n");
- return 0;
+ FAIL("test2.bundle\n dlerror(): %s", dlerror());
}
// test3: LC_RPATH not in main executable uses absolute path
handle = dlopen(RUN_DIR "/test3.bundle", RTLD_LAZY);
if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-restricted test3.bundle\n");
- return 0;
+ FAIL("test3.bundle dlerror(): %s", dlerror());
}
// test4: [SHOULD FAIL] LC_RPATH not in main executable uses @executable_path
handle = dlopen(RUN_DIR "/test4.bundle", RTLD_LAZY);
if ( handle != NULL ) {
- printf("[FAIL] dlopen-restricted test4.bundle dlopen() should not work\n");
- return 0;
+ FAIL("test4.bundle dlopen() should not work");
}
// test5: [SHOULD FAIL] @executable_path in LC_LOAD_DYLIB
handle = dlopen(RUN_DIR "/test5.bundle", RTLD_LAZY);
if ( handle != NULL ) {
- printf("[FAIL] dlopen-restricted test5.bundle dlopen() should not work\n");
- return 0;
+ FAIL("test5.bundle dlopen() should not work");
}
-
-
- printf("[PASS] dlopen-restricted\n");
- return 0;
+ PASS("Success");
}
-// BUILD: cp bad.txt $BUILD_DIR/libnota.dylib
+// BUILD: $CP bad.txt $BUILD_DIR/libnota.dylib
// BUILD: $CC main.c -o $BUILD_DIR/dlopen-bad-file.exe -DRUN_DIR="$RUN_DIR"
// RUN: ./dlopen-bad-file.exe
#include <dlfcn.h>
#include <string.h>
+#include "test_support.h"
-
-int main()
-{
- printf("[BEGIN] dlopen-bad-file\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// try to dlopen() a text file
- void* handle = dlopen(RUN_DIR "/libnota.dylib", RTLD_FIRST);
- if ( handle != NULL ) {
- printf("[FAIL] dlopen-bad-file should have failed on non-mach-o file %s\n", RUN_DIR "/libnota.dylib");
- return 0;
- }
+ void* handle = dlopen(RUN_DIR "/libnota.dylib", RTLD_FIRST);
+ if ( handle != NULL ) {
+ FAIL("Should have failed on non-mach-o file %s", RUN_DIR "/libnota.dylib");
+ }
const char* message = dlerror();
if ( (strstr(message, "mach-o") == NULL) && (strstr(message, "too short") == NULL) ) {
- printf("dlerror: %s\n", message);
- printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'mach-o'\n");
- return 0;
- }
+ FAIL("dlerror() message '%s' did not contain 'mach-o'", message);
+ }
// try to dlopen() a directory
- handle = dlopen(RUN_DIR, RTLD_FIRST);
- if ( handle != NULL ) {
- printf("[FAIL] dlopen-bad-file should have failed on dir %s\n", RUN_DIR);
- return 0;
- }
+ handle = dlopen(RUN_DIR, RTLD_FIRST);
+ if ( handle != NULL ) {
+ FAIL("Should have failed on dir %s", RUN_DIR);
+ }
message = dlerror();
if ( strstr(message, "not a file") == NULL ) {
- printf("dlerror: %s\n", message);
- printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'not a file'\n");
- return 0;
- }
-
- printf("[PASS] dlopen-bad-file\n");
+ FAIL("dlerror() message '%s' did not contain 'not a file", message);
+ }
- return 0;
+ PASS("Success");
}
#include <stdio.h>
#include <dlfcn.h>
+#include "test_support.h"
static void tryImage(const char* path)
{
- printf("[BEGIN] dlopen-basic %s\n", path);
- void* handle = dlopen(path, RTLD_LAZY);
- if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-basic %s\n", path);
- return;
- }
-
- void* sym = dlsym(handle, "foo");
- if ( sym == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-basic %s\n", path);
- return;
- }
-
- int result = dlclose(handle);
- if ( result != 0 ) {
- printf("dlclose() returned %d, dlerror()=%s\n", result, dlerror());
- printf("[FAIL] dlopen-basic %s\n", path);
- return;
- }
-
- printf("[PASS] dlopen-basic %s\n", path);
+ void* handle = dlopen(path, RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("dlopen(\"%s\"), dlerror()=%s", path, dlerror());
+ }
+
+ void* sym = dlsym(handle, "foo");
+ if ( sym == NULL ) {
+ FAIL("dlsym(\"foo\") for \"%s\" returned NULL, dlerror()=%s", path, dlerror());
+ }
+
+ int result = dlclose(handle);
+ if ( result != 0 ) {
+ FAIL("dlclose(\"%s\") returned %d, dlerror()=%s", path, result, dlerror());
+ }
}
-int main()
-{
- tryImage(RUN_DIR "/test.bundle");
- tryImage(RUN_DIR "/test.dylib");
-
- return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ tryImage(RUN_DIR "/test.bundle");
+ tryImage(RUN_DIR "/test.dylib");
+ PASS("Success");
}
#include <stdio.h>
#include <dlfcn.h>
-// libfoo-static.dylib and libfoo-dynamic.dylib each have an empty (no disk size) DATA segment
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] dlopen-empty-data\n");
+// libfoo-static.dylib and libfoo-dynamic.dylib each have an empty (no disk size) DATA segment
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-empty-data: libfoo-dynamic.dylib could not be loaded: %s\n", dlerror());
- return 0;
+ FAIL("libfoo-dynamic.dylib could not be loaded: %s", dlerror());
}
- printf("[PASS] dlopen-empty-data\n");
- return 0;
+ PASS("Success");
}
-// BUILD: $CC c.c -dynamiclib -o $TEMP_DIR/libcextra.dylib -install_name $RUN_DIR/libc.dylib -DEXTRA_SYMBOL=1
+// BUILD: $CC c.c -dynamiclib -o $BUILD_DIR/libcextra.dylib -install_name $RUN_DIR/libc.dylib -DEXTRA_SYMBOL=1
// BUILD: $CC c.c -dynamiclib -o $BUILD_DIR/libc.dylib -install_name $RUN_DIR/libc.dylib
-// BUILD: $CC b.m -dynamiclib -o $BUILD_DIR/libb.dylib -install_name $RUN_DIR/libb.dylib $TEMP_DIR/libcextra.dylib -framework Foundation
+// BUILD: $CC b.m -dynamiclib -o $BUILD_DIR/libb.dylib -install_name $RUN_DIR/libb.dylib $BUILD_DIR/libcextra.dylib -framework Foundation
// BUILD: $CC a.c -dynamiclib -o $BUILD_DIR/liba.dylib -install_name $RUN_DIR/liba.dylib $BUILD_DIR/libb.dylib
// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-fail-cleanly.exe
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libcextra.dylib
+
+
// RUN: ./dlopen-fail-cleanly.exe
#include <stdio.h>
#include <dlfcn.h>
#include <mach-o/dyld.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] dlopen-fail-cleanly\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// dlopen dylib chain that should fail
void* handle = dlopen(RUN_DIR "/liba.dylib", RTLD_NOW);
if ( handle != NULL ) {
- printf("[FAIL] dlopen-fail-cleanly dlopen(liba.dylib) expected to fail but did not\n");
- return 0;
+ FAIL("dlopen(liba.dylib) expected to fail but did not");
}
// iterate loaded images and make sure no residue from failed dlopen
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");
}
#include <stdio.h>
#include <dlfcn.h>
+#include "test_support.h"
+
int gInitialisersCalled = 0;
-int main() {
- printf("[BEGIN] dlopen-flat\n");
- int result;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ int result;
- // Foo exports foo()
- void* fooHandle = 0;
- {
- fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
- if (!fooHandle) {
- printf("dlopen failed with error: %s\n", dlerror());
- return 1;
- }
- if (gInitialisersCalled != 1) {
- printf("gInitialisersCalled != 1\n");
- printf("[FAIL] dlopen-flat\n");
- return 1;
- }
- }
- // Now unload foo which should do something.
- result = dlclose(fooHandle);
- if (result != 0) {
- printf("dlclose() returned %c\n", result);
- printf("[FAIL] dlopen-flat\n");
- return 1;
- }
+ // Foo exports foo()
+ void* fooHandle = 0;
+ {
+ fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+ if (!fooHandle) {
+ FAIL("dlopen(\"" RUN_DIR "/libfoo.dylib\") failed with error: %s", dlerror());
+ }
+ if (gInitialisersCalled != 1) {
+ FAIL("gInitialisersCalled != 1");
+ }
+ }
+ // Now unload foo which should do something.
+ result = dlclose(fooHandle);
+ if (result != 0) {
+ FAIL("dlclose() returned %c", result);
+ }
- // Open foo again which should do something.
- {
- fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
- if (!fooHandle) {
- printf("dlopen failed with error: %s\n", dlerror());
- return 1;
- }
- if (gInitialisersCalled != 2) {
- printf("gInitialisersCalled != 2\n");
- printf("[FAIL] dlopen-flat\n");
- return 1;
- }
- }
+ // Open foo again which should do something.
+ {
+ fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+ if (!fooHandle) {
+ FAIL("dlopen failed with error: %s", dlerror());
+ }
+ if (gInitialisersCalled != 2) {
+ FAIL("gInitialisersCalled != 2");
+ }
+ }
- // Bar is going to resolve foo()
- void* barHandle = 0;
- {
- barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
- if (!barHandle) {
- printf("dlopen failed with error: %s\n", dlerror());
- return 1;
- }
- if (gInitialisersCalled != 3) {
- printf("gInitialisersCalled != 3\n");
- printf("[FAIL] dlopen-flat\n");
- return 1;
- }
- }
- // Now unload foo which shouldn't do anything.
- result = dlclose(fooHandle);
- if (result != 0) {
- printf("dlclose() returned %c\n", result);
- printf("[FAIL] dlopen-flat\n");
- return 1;
- }
+ // Bar is going to resolve foo()
+ void* barHandle = 0;
+ {
+ barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
+ if (!barHandle) {
+ FAIL("dlopen(\"" RUN_DIR "/libbar.dylib\" failed with error: %s", dlerror());
+ }
+ if (gInitialisersCalled != 3) {
+ FAIL("gInitialisersCalled != 3");
+ }
+ }
+ // Now unload foo which shouldn't do anything.
+ result = dlclose(fooHandle);
+ if (result != 0) {
+ FAIL("dlclose(\"" RUN_DIR "/libfoo.dylib\") returned %c", result);
+ }
- // Open foo again which shouldn't do anything.
- {
- fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
- if (!fooHandle) {
- printf("dlopen failed with error: %s\n", dlerror());
- return 1;
- }
- if (gInitialisersCalled != 3) {
- printf("gInitialisersCalled != 3\n");
- printf("[FAIL] dlopen-flat\n");
- return 1;
- }
- }
+ // Open foo again which shouldn't do anything.
+ {
+ fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+ if (!fooHandle) {
+ FAIL("dlopen(\"" RUN_DIR "/libfoo.dylib\" failed with error: %s", dlerror());
+ }
+ if (gInitialisersCalled != 3) {
+ FAIL("gInitialisersCalled != 3");
+ }
+ }
- printf("[PASS] dlopen-flat\n");
- return 0;
+ PASS("Success");
}
#include <stdio.h>
#include <dlfcn.h>
+#include "test_support.h"
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ // Verify dyld will fallback and look for framework in /System/Library/Frameworks/
+ void* handle = dlopen("/System/Library/BadPath/CoreFoundation.framework/CoreFoundation", RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("dlerror(): %s", dlerror());
+ }
-int main()
-{
- printf("[BEGIN] dlopen-framework-fallback\n");
+ // validate handle works to find symbols
+ void* sym = dlsym(handle, "CFRetain");
+ if ( sym == NULL ) {
+ FAIL("dlerror(): %s", dlerror());
+ }
- // Verify dyld will fallback and look for framework in /System/Library/Frameworks/
- void* handle = dlopen("/System/Library/BadPath/CoreFoundation.framework/CoreFoundation", RTLD_LAZY);
- if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-framework-fallback\n");
- return 0;
- }
-
- // validate handle works to find symbols
- void* sym = dlsym(handle, "CFRetain");
- if ( sym == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-framework-fallback\n");
- return 0;
- }
-
- printf("[PASS] dlopen-framework-fallback\n");
-
- return 0;
+ PASS("Success");
+
+ return 0;
}
#include <mach/mach.h>
#include <mach/mach_host.h>
+#include "test_support.h"
+
typedef bool (*BoolFunc)(void);
}
-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
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");
}
-
-
-#include <stdio.h>
#include <dlfcn.h>
-#include <stdlib.h>
-#include <dispatch/dispatch.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <pthread.h>
-#include <assert.h>
-#include <unistd.h>
+#include "test_support.h"
static void* work1(void* arg)
{
__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;
#include <dlfcn.h>
#include <stdlib.h>
+#include "test_support.h"
__attribute__((constructor))
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");
}
+#include "test_support.h"
static int inited = 0;
return inited;
}
-int bar() {
- return inited ? 0 : 1;
-}
\ No newline at end of file
+void bar() {
+ if (inited == 0) {
+ FAIL("libbar.dylib not initialized");
+ }
+}
#include <stdio.h>
#include <unistd.h>
-extern int bar();
+#include "test_support.h"
+
extern int bazInited();
static void* barHandle = NULL;
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
+}
#include <dlfcn.h>
#include <stdlib.h>
+#include "test_support.h"
-extern int foo();
-extern int bar();
-
-int main() {
- printf("[BEGIN] dlopen-in-init2\n");
- if ( foo() != 0 )
- return 0;
- if ( bar() != 0 )
- return 0;
- printf("[PASS] dlopen-in-init2\n");
- return 0;
+extern void foo();
+extern void bar();
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ foo();
+ bar();
+ PASS("Success");
}
#include <stdio.h>
#include <unistd.h>
+#include "test_support.h"
+
static void* bazHandle = NULL;
static void* bazSymbol = NULL;
static int barInited = 0;
static int bazInited = 0;
__attribute__((constructor))
-static void myinit()
-{
- barInited = 1;
- bazHandle = dlopen(RUN_DIR "/libbaz.dylib", 0);
- if ( bazHandle == NULL ) {
- printf("[FAIL] dlopen-in-init3, dlopen libbaz.dylib: %s\n", dlerror());
- return;
- }
- bazSymbol = dlsym(RTLD_DEFAULT, "bazIsInited");
+static void myinit(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ barInited = 1;
+ bazHandle = dlopen(RUN_DIR "/libbaz.dylib", 0);
+ if ( bazHandle == NULL ) {
+ FAIL("dlopen libbaz.dylib: %s", dlerror());
+ }
+ bazSymbol = dlsym(RTLD_DEFAULT, "bazIsInited");
if ( bazSymbol == NULL ) {
- printf("[FAIL] dlopen-in-init3, dlsym libbaz.dylib\n");
- return;
+ FAIL("dlsym libbaz.dylib");
}
bazInited = ((int(*)())bazSymbol)();
}
int bar() {
if ( barInited == 0 ) {
- printf("[FAIL] dlopen-in-init3, didn't init bar\n");
- return 1;
+ FAIL("Didn't init bar");
}
if ( bazHandle == NULL ) {
- return 1;
+ FAIL("bazHandle not inited");
}
if ( bazSymbol == NULL ) {
- return 1;
+ FAIL("bazSymbol not inited");
}
if ( bazInited == 0 ) {
- printf("[FAIL] dlopen-in-init3, didn't init bar\n");
- return 1;
+ FAIL("didn't init bar");
}
return 0;
-}
\ No newline at end of file
+}
-
-#include <stdio.h>
+#include "test_support.h"
extern int bar();
extern int bazIsInited();
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
+}
// RUN: ./dlopen-in-init3.exe
// This test uses dlopen to jump ahead in the initializer graph
-// main doesn't directly link any of the libraries here, but dlopen's libfoo which links libbar and libbar.
+// main doesn't directly link any of the libraries here, but dlopen's libfoo which links libbar and libbaz.
// We should run initializers in the order libbar, libbaz, libfoo.
// However, libbar has a static init with a dlopen of libbaz and so libbaz needs to be initialized by libbar instead of by libfoo
#include <dlfcn.h>
#include <stdlib.h>
-int main() {
- printf("[BEGIN] dlopen-in-init3\n");
- void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", 0);
- if ( fooHandle == NULL ) {
- printf("[FAIL] dlopen-in-init3, dlopen libfoo.dylib: %s\n", dlerror());
- return 0;
- }
- void* fooSymbol = dlsym(RTLD_DEFAULT, "foo");
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", 0);
+ if ( fooHandle == NULL ) {
+ FAIL("dlopen-in-init3, dlopen libfoo.dylib: %s", dlerror());
+ }
+ void* fooSymbol = dlsym(RTLD_DEFAULT, "foo");
if ( fooSymbol == NULL ) {
- printf("[FAIL] dlopen-in-init3, dlsym libfoo.dylib\n");
- return 0;
+ FAIL("dlsym libfoo.dylib");
+ }
+ if ( ((int(*)())fooSymbol)() != 0 ) {
+ FAIL("fooSymbol() should return 0");
}
- if ( ((int(*)())fooSymbol)() != 0 )
- return 0;
- printf("[PASS] dlopen-in-init3\n");
- return 0;
+ PASS("Success");
}
#include <dlfcn.h>
#include <mach-o/dyld.h>
+#include "test_support.h"
static void checkBundle(const char* path, bool unlinkBeforeDestroy)
{
int fd = open(path, O_RDONLY, 0);
if ( fd == -1 ) {
- printf("[FAIL] open(%s) failed", path);
- exit(0);
+ FAIL("open(%s) failed", path);
}
struct stat stat_buf;
if ( fstat(fd, &stat_buf) == -1) {
- printf("[FAIL] fstat() failed\n");
- exit(0);
+ FAIL("fstat() failed");
}
void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
if ( loadAddress == ((void*)(-1)) ) {
- printf("[FAIL] mmap() failed\n");
- exit(0);
+ FAIL("mmap() failed");
}
close(fd);
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) {
- printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n");
- exit(0);
+ FAIL("NSCreateObjectFileImageFromMemory failed");
}
NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE);
if ( mod == NULL ) {
- printf("[FAIL] NSLinkModule failed\n");
- exit(0);
+ FAIL("NSLinkModule failed");
}
if ( !unlinkBeforeDestroy ) {
// API lets you destroy ofi and NSModule lives on
if ( !NSDestroyObjectFileImage(ofi) ) {
- printf("[FAIL] NSDestroyObjectFileImage failed\n");
- exit(0);
+ FAIL("NSDestroyObjectFileImage failed");
}
}
NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle");
if ( sym == NULL ) {
- printf("[FAIL] NSLookupSymbolInModule failed\n");
- exit(0);
+ FAIL("NSLookupSymbolInModule failed");
}
void* func = NSAddressOfSymbol(sym);
if ( func == NULL ) {
- printf("[FAIL] NSAddressOfSymbol failed\n");
- exit(0);
+ FAIL("NSAddressOfSymbol failed");
}
Dl_info info;
if ( dladdr(func, &info) == 0 ) {
- printf("[FAIL] dladdr(&p, xx) failed\n");
- exit(0);
+ FAIL("dladdr(&p, xx) failed");
}
- //printf("_fooInBundle found in %s\n", info.dli_fname);
+ LOG("_fooInBundle found in %s", info.dli_fname);
if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
- printf("[FAIL] NSUnLinkModule failed\n");
- exit(0);
+ FAIL("NSUnLinkModule failed");
}
if ( dladdr(func, &info) != 0 ) {
- printf("[FAIL] dladdr(&p, xx) found but should not have\n");
- exit(0);
+ FAIL("dladdr(&p, xx) found but should not have");
}
if ( unlinkBeforeDestroy ) {
if ( !NSDestroyObjectFileImage(ofi) ) {
- printf("[FAIL] NSDestroyObjectFileImage failed\n");
- exit(0);
+ FAIL("NSDestroyObjectFileImage failed");
}
}
}
{
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);
// 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");
+}
#include <stdlib.h>
#include <string.h>
+#include "test_support.h"
+
static const char* expectedStrings[] = {
"a() from main",
"initC",
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;
}
#include <stdlib.h>
#include <dispatch/dispatch.h>
+#include "test_support.h"
+
// main deps on A
// main dlopens B which deps on C
// main dlopens D which deps on C
extern void a(const char*);
extern void setState(const char* from);
-int main()
-{
- printf("[BEGIN] dlopen-intertwined\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
a("main");
void* handle = dlopen(RUN_DIR "/libB.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
- exit(0);
+ FAIL("Error: %s", dlerror());
}
handle = dlopen(RUN_DIR "/libD.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
- exit(0);
+ FAIL("Error: %s", dlerror());
}
handle = dlopen(RUN_DIR "/libE.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
- exit(0);
+ FAIL("Error: %s", dlerror());
}
handle = dlopen(RUN_DIR "/libF.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-intertwined: %s\n", dlerror());
- exit(0);
+ FAIL("Error: %s", dlerror());
}
setState("DONE");
- printf("[PASS] dlopen-intertwined\n");
- return 0;
+ PASS("Success");
}
#include <string.h>
#include <stdlib.h>
+#include "test_support.h"
-
-int main()
-{
- printf("[BEGIN] dlopen-long-error-message\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
for (int i=0; i < 10; ++i) {
void* handle = dlopen("/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/libbogus.dylib", RTLD_FIRST);
if ( handle != NULL ) {
- printf("[FAIL] dlopen-long-error-message should have failed on non-existent file\n");
+ FAIL("Should have failed on non-existent file");
return 0;
}
free(strdup("hello there"));
}
- printf("[PASS] dlopen-long-error-message\n");
-
- return 0;
+ PASS("Success");
}
#include <stdio.h>
#include <dlfcn.h>
-int main()
-{
- printf("[BEGIN] dlopen-prebuilt-dlopen-closure\n");
-
- void* handle = dlopen("/usr/lib/libobjc-trampolines.dylib", RTLD_LAZY);
- if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-prebuilt-dlopen-closure\n");
- return 0;
- }
-
- printf("[PASS] dlopen-prebuilt-dlopen-closure\n");
-
- return 0;
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ void* handle = dlopen("/usr/lib/libobjc-trampolines.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("dlerror(): %s", dlerror());
+ }
+
+ PASS("Success");
}
int foo()
{
- return 10;
+ return 10;
}
#include <stdlib.h>
#include <dispatch/dispatch.h>
+#include "test_support.h"
-
-int main()
-{
- printf("[BEGIN] dlopen-read\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
__block bool allGood = true;
dispatch_apply(6, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
for (int i=0; i < 500; ++i) {
void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-read: %s\n", dlerror());
- exit(0);
+ FAIL("dlopen-read: %s", dlerror());
}
dlclose(handle);
}
});
- printf("[PASS] dlopen-read\n");
+ PASS("Success");
return 0;
}
// BUILD: $CC main.c -o $BUILD_DIR/dlopen-realpath.exe
-// BUILD: cd $BUILD_DIR && ln -s ./IOKit.framework/IOKit IOKit && ln -s /System/Library/Frameworks/IOKit.framework IOKit.framework
+// BUILD: $SYMLINK ./IOKit.framework/IOKit $BUILD_DIR/IOKit
+// BUILD: $SYMLINK /System/Library/Frameworks/IOKit.framework $BUILD_DIR/IOKit.framework
-// RUN: DYLD_FALLBACK_LIBRARY_PATH=/baz ./dlopen-realpath.exe
+//FIXME: Use something besides IOKit so we do not need to copy it into the chroot
+// R: DYLD_FALLBACK_LIBRARY_PATH=/baz ./dlopen-realpath.exe
#include <stdio.h>
#include <dlfcn.h>
+#include "test_support.h"
static void tryImage(const char* path)
{
- printf("[BEGIN] dlopen-realpath %s\n", path);
- void* handle = dlopen(path, RTLD_LAZY);
- if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-realpath %s\n", path);
- return;
- }
-
- int result = dlclose(handle);
- if ( result != 0 ) {
- printf("dlclose(%p): %s\n", handle, dlerror());
- printf("[FAIL] dlopen-realpath %s\n", path);
- return;
- }
-
- printf("[PASS] dlopen-realpath %s\n", path);
+ void* handle = dlopen(path, RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("dlerror(\"%s\"): %s", path, dlerror());
+ }
+
+ int result = dlclose(handle);
+ if ( result != 0 ) {
+ FAIL("dlclose(\"%s\"): %s", path, dlerror());
+ }
}
-int main()
-{
- tryImage("./IOKit.framework/IOKit");
- tryImage("./././IOKit/../IOKit.framework/IOKit");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ tryImage("./IOKit.framework/IOKit");
+ tryImage("./././IOKit/../IOKit.framework/IOKit");
tryImage("./IOKit");
// Also try libSystem which has an alias in the OS to /usr/lib/libSystem.B.dylib
tryImage("//usr/lib/libSystem.dylib");
tryImage("/usr/./lib/libSystem.dylib");
- return 0;
+ PASS("Success");
}
int bar()
{
- return 0;
+ return 0;
}
#include <stdlib.h>
#include <dispatch/dispatch.h>
+#include "test_support.h"
-
-int main()
-{
- printf("[BEGIN] dlopen-recurse\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// libfoo's initializer calls dlopen(). If that hangs, we have a locking bug
void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
dlclose(handle);
- printf("[PASS] dlopen-recurse\n");
- return 0;
+ PASS("Success");
}
int bar()
{
- return 0;
+ return 0;
}
-// BUILD: mkdir -p $BUILD_DIR/dir
// BUILD: $CC bar.c -dynamiclib -install_name @rpath/libbar.dylib -o $BUILD_DIR/dir/libbar.dylib
// BUILD: $CC test.c -dynamiclib -install_name $RUN_DIR/libtest.dylib -o $BUILD_DIR/libtest.dylib -rpath @loader_path/dir
// BUILD: $CC main.c -o $BUILD_DIR/dlopen-rpath-from-dylib.exe $BUILD_DIR/libtest.dylib
#include <dlfcn.h>
#include <stdbool.h>
+#include "test_support.h"
/// test that a call to dlopen() from a dylib uses the LC_RPATH from that dylib
-extern bool test_dlopen();
+extern void test_dlopen();
-int main()
-{
- printf("[BEGIN] dlopen-rpath-from-dylib\n");
-
- if ( test_dlopen() )
- printf("[PASS] dlopen-rpath-from-dylib\n");
- else
- printf("[FAIL] dlopen-rpath-from-dylib\n");
-
- return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ test_dlopen();
+ PASS("Succcess");
}
#include <dlfcn.h>
#include <stdbool.h>
-bool test_dlopen()
+#include "test_support.h"
+
+void test_dlopen()
{
void* handle = dlopen("@rpath/libbar.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-rpath-from-dylib: dlopen(@rpath/libbar.dylib) failed: %s\n", dlerror());
- return false;
+ FAIL("dlopen(\"@rpath/libbar.dylib\") failed: %s", dlerror());
}
-
dlclose(handle);
-
- return true;
}
-
int foo()
{
- return 10;
+ return 10;
}
-// 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
#include <stdio.h>
#include <dlfcn.h>
+#include "test_support.h"
/// test that a leaf name passed to dlopen() searches the rpath
-int main()
-{
- printf("[BEGIN] dlopen-rpath-implicit\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
void* handle = dlopen("libimplicitrpath.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("[FAIL] dlopen-rpath-implicit: dlopen(libimplicitrpath.dylib) failed: %s\n", dlerror());
- return 0;
+ FAIL("dlopen(\"libimplicitrpath.dylib\") failed: %s", dlerror());
}
dlclose(handle);
-
- printf("[PASS] dlopen-rpath-implicit\n");
-
- return 0;
+ PASS("Succcess");
}
-#include <stdio.h>
-#include <stdlib.h>
-
+#include "test_support.h"
__attribute__((constructor))
-void init()
+void init(int argc, const char* argv[], const char* envp[], const char* apple[])
{
- printf("[FAIL] dlopen-rpath-prev-override\n");
- exit(0);
+ FAIL("Bad dylib loaded");
}
int sub2()
{
- return 2;
+ return 2;
}
int foo()
{
- return 10;
+ return 10;
}
int sub1()
{
- return 1;
+ return 1;
}
-// BUILD: mkdir -p $BUILD_DIR/dir $BUILD_DIR/good $BUILD_DIR/bad
// BUILD: $CC good.c -dynamiclib -install_name @rpath/libtest.dylib -o $BUILD_DIR/good/libtest.dylib
// BUILD: $CC bad.c -dynamiclib -install_name @rpath/libtest.dylib -o $BUILD_DIR/bad/libtest.dylib
// BUILD: $CC dyn.c -dynamiclib -install_name @rpath/libdynamic.dylib -o $BUILD_DIR/dir/libdynamic.dylib $BUILD_DIR/good/libtest.dylib -rpath @loader_path/../bad
#include <stdio.h>
#include <dlfcn.h>
-int main()
-{
- printf("[BEGIN] dlopen-rpath-prev-override\n");
+#include "test_support.h"
- void* handle = dlopen(RUN_DIR "/dir/libdynamic.dylib", RTLD_LAZY);
- if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-rpath-prev-override\n");
- return 0;
- }
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ void* handle = dlopen(RUN_DIR "/dir/libdynamic.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("dlerror(): %s", dlerror());
+ }
- printf("[PASS] dlopen-rpath-prev-override\n");
- return 0;
+ PASS("Success");
}
int foo()
{
- return 10;
+ return 10;
}
-// BUILD: mkdir -p $BUILD_DIR/dir1 $BUILD_DIR/dir2
// BUILD: $CC sub1.c -dynamiclib -install_name @rpath/librpathstatic.dylib -o $BUILD_DIR/dir1/librpathstatic.dylib
// BUILD: $CC sub2.c -dynamiclib -install_name @rpath/libdynamic.dylib -o $BUILD_DIR/dir2/libdynamic.dylib $BUILD_DIR/dir1/librpathstatic.dylib
// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libstatic.dylib -o $BUILD_DIR/libstatic.dylib -rpath @loader_path/dir1 $BUILD_DIR/dir1/librpathstatic.dylib
#include <stdio.h>
#include <dlfcn.h>
-int main()
-{
- printf("[BEGIN] dlopen-rpath-prev\n");
+#include "test_support.h"
- void* handle = dlopen(RUN_DIR "/dir2/libdynamic.dylib", RTLD_LAZY);
- if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-rpath-prev\n");
- return 0;
- }
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ void* handle = dlopen(RUN_DIR "/dir2/libdynamic.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("dlerror(): %s", dlerror());
+ }
- printf("[PASS] dlopen-rpath-prev\n");
- return 0;
+ PASS("Success");
}
int sub1()
{
- return 1;
+ return 1;
}
int sub2()
{
- return 2;
+ return 2;
}
#include <stdio.h>
#include <dlfcn.h>
-int main() {
- printf("[BEGIN] dlopen-signing\n");
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
void* handle = dlopen("signed.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-signing (signed loading signed)\n");
- return 0;
+ FAIL("dlerror(): %s", dlerror());
} else {
int result = dlclose(handle);
if ( result != 0 ) {
- printf("dlclose() returned %c\n", result);
- printf("[FAIL] dlopen-signing (signed unloading signed)\n");
- return 0;
+ FAIL("dlclose() returned %c", result);
}
}
handle = dlopen("unsigned.dylib", RTLD_LAZY);
if ( handle != NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-signing (signed loading unsigned)\n");
- return 0;
+ FAIL("dlerror(): %s", dlerror());
} else {
int result = dlclose(handle);
if ( result != 0 ) {
- printf("dlclose() returned %c\n", result);
- printf("[FAIL] dlopen-signing (signed unloading signed)\n");
- return 0;
+ FAIL("dlclose() returned %c", result);
}
}
- printf("[PASS] dlopen-signing\n");
-
- return 0;
+ PASS("Success");
}
// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
-// BUILD: cd $BUILD_DIR && ln -s libfoo.dylib libfoo-symlink.dylib
+// BUILD: $SYMLINK libfoo.dylib $BUILD_DIR/libfoo-symlink.dylib
// BUILD: $CC main.c -o $BUILD_DIR/dlopen-symlink.exe -DRUN_DIR="$RUN_DIR"
// RUN: ./dlopen-symlink.exe
#include <mach-o/dyld.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] dlopen-symlink\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// call dlopen() with a path that is a symlink
void* handle = dlopen(RUN_DIR "/libfoo-symlink.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlopen-symlink\n");
- return 0;
+ FAIL("dlerror(): %s", dlerror());
}
// walk images to see if path was converted to real path
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");
}
#include <string.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
// verify RTLD_DEFAULT search order
-
-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");
}
#include <string.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
// verify RTLD_MAIN_ONLY search order
-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");
}
#include <string.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
// verify RTLD_NEXT search order
-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");
}
#include <string.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
// verify RTLD_SELF search order
-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");
}
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
// verify RTLD_DEFAULT search order
int mainSymbol = 4;
-int main()
-{
- printf("[BEGIN] dlsym-handle\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
if ( fooHandle == NULL ) {
- printf("[FAIL] dlsym-handle: libfoo.dylib could not be loaded, %s\n", dlerror());
- return 0;
+ FAIL("libfoo.dylib could not be loaded, %s", dlerror());
}
void* barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
if ( barHandle == NULL ) {
- printf("[FAIL] dlsym-handle: libbar.dylib could not be loaded, %s\n", dlerror());
- return 0;
+ FAIL("libbar.dylib could not be loaded, %s", dlerror());
}
// verify fooHandle does not find mainSymbol
if ( dlsym(fooHandle, "mainSymbol") != NULL ) {
- printf("[FAIL] dlsym-handle: mainSymbol was found with fooHandle\n");
- return 0;
+ FAIL("mainSymbol was found with fooHandle");
}
// verify fooHandle can find foo
if ( dlsym(fooHandle, "foo") == NULL ) {
- printf("[FAIL] dlsym-handle: foo not found with fooHandle\n");
- return 0;
+ FAIL("foo not found with fooHandle");
}
// verify fooHandle can find base
if ( dlsym(fooHandle, "base") == NULL ) {
- printf("[FAIL] dlsym-handle: base not found with fooHandle\n");
- return 0;
+ FAIL("base not found with fooHandle");
}
// verify fooHandle cannot find bar
if ( dlsym(fooHandle, "bar") != NULL ) {
- printf("[FAIL] dlsym-handle: bar found with fooHandle\n");
- return 0;
+ FAIL("bar found with fooHandle");
}
// verify barHandle can find bar
if ( dlsym(barHandle, "bar") == NULL ) {
- printf("[FAIL] dlsym-handle: bar not found with barHandle\n");
- return 0;
+ FAIL("bar not found with barHandle");
}
// verify barHandle can find base
if ( dlsym(barHandle, "base") == NULL ) {
- printf("[FAIL] dlsym-handle: base not found with barHandle\n");
- return 0;
+ FAIL("base not found with barHandle");
}
// verify barHandle cannot find foo
if ( dlsym(barHandle, "foo") != NULL ) {
- printf("[FAIL] dlsym-handle: foo found with barHandle\n");
- return 0;
+ FAIL("foo found with barHandle");
}
// verify renamed and re-exported symbols work
if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) {
- printf("[FAIL] dlsym-handle: strcmp not found\n");
- return 0;
+ FAIL("strcmp not found");
}
// verify bad handle errors
if ( dlsym((void*)0xdeadbeef, "malloc") != NULL ) {
- printf("[FAIL] dlsym-handle: malloc found with bad handle\n");
- return 0;
+ FAIL("malloc found with bad handle");
}
else {
const char* message = dlerror();
if ( strstr(message, "invalid") == NULL ) {
- printf("[FAIL] dlsym-handle: invalid handle error message missing 'invalid'\n");
- return 0;
+ FAIL(" invalid handle error message missing 'invalid'");
}
}
- printf("[PASS] dlsym-handle\n");
- return 0;
+ PASS("Success");
}
#include <string.h>
#include <mach-o/dyld-interposing.h>
+#include "test_support.h"
+
static bool inMalloc = false;
static bool forceSystemMalloc = false;
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;
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);
#include <stdlib.h>
#include <string.h>
-int main()
-{
- printf("[BEGIN] dlsym-in-interposed-malloc\n");
+#include "test_support.h"
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// malloc should have been called when dyld3's libdyld was initialized, but
// call it one more time anyway just to make sure its working
(void)malloc(1);
-
- //printf("%p %p %p %p\n", p1, p2, p3, p4);
- printf("[PASS] dlsym-in-interposed-malloc\n");
- return 0;
+ PASS("Success");
}
-// BUILD: mkdir -p $BUILD_DIR/sub1 $BUILD_DIR/sub2
// BUILD: $CC sub1.c -dynamiclib -install_name @rpath/libsub1.dylib -o $BUILD_DIR/sub1/libsub1.dylib
// BUILD: $CC sub2.c -dynamiclib -install_name @rpath/libsub2.dylib -o $BUILD_DIR/sub2/libsub2.dylib
// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -rpath @loader_path/sub1 -Wl,-reexport_library,$BUILD_DIR/sub1/libsub1.dylib -Wl,-reexport_library,$BUILD_DIR/sub2/libsub2.dylib
#include <stdio.h>
#include <dlfcn.h>
-int main()
-{
- printf("[BEGIN] dlsym-re-export\n");
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// RTLD_FIRST means dlsym() should only search libfoo.dylib (and any re-exports)
- void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST);
- if ( handle == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlsym-re-export\n");
- return 0;
- }
-
- void* sym1 = dlsym(handle, "sub1");
- if ( sym1 == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlsym-re-export\n");
- return 0;
- }
-
- void* sym2 = dlsym(handle, "sub2");
- if ( sym2 == NULL ) {
- printf("dlerror(): %s\n", dlerror());
- printf("[FAIL] dlsym-re-export\n");
- return 0;
- }
-
- printf("[PASS] dlsym-re-export\n");
- return 0;
+ void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST);
+ if ( handle == NULL ) {
+ FAIL("dlerror(): %s", dlerror());
+ }
+
+ void* sym1 = dlsym(handle, "sub1");
+ if ( sym1 == NULL ) {
+ FAIL("dlerror(): %s", dlerror());
+ }
+
+ void* sym2 = dlsym(handle, "sub2");
+ if ( sym2 == NULL ) {
+ FAIL("dlerror(): %s", dlerror());
+ }
+
+ PASS("Success");
}
// if we ever re-enable this on iOS we will need to add // BOOT_ARGS: dtrace_dof_mode=1
-// BUILD: /usr/sbin/dtrace -h -s main.d -o $TEMP_DIR/probes.h
-// BUILD: $CC main.c -I$TEMP_DIR -o $BUILD_DIR/dtrace.exe
+// BUILD: $DTRACE -h -s main.d -o $BUILD_DIR/probes.h
+// BUILD: $CC main.c -I$BUILD_DIR -o $BUILD_DIR/dtrace.exe $DEPENDS_ON $BUILD_DIR/probes.h
// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dtrace.exe
-// RUN: $SUDO dtrace -l -n 'dyld_testing*:dtrace.exe:main:callback' -c ./dtrace.exe
-
-
+// RUN: $SUDO /usr/sbin/dtrace -o /dev/null 2> /dev/null -n 'dyld_testing*:dtrace.exe:main:callback' -c $RUN_DIR/dtrace.exe
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sdt.h>
+#include "test_support.h"
+
#include "probes.h"
-int main()
-{
- printf("[BEGIN] dtrace\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
DYLD_TESTING_CALLBACK();
if (!DYLD_TESTING_CALLBACK_ENABLED())
- printf("[FAIL] dtrace: DYLD_TESTING_CALLBACK_ENABLED() returned false\n");
+ FAIL("DYLD_TESTING_CALLBACK_ENABLED() returned false");
else
- printf("[PASS] dtrace\n");
-
- return 0;
+ PASS("Success");
}
// 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
#include <string.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
+
bool gFoundLibrary = false;
const char* gLibraryName = NULL;
}
});
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");
}
// 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
#include <string.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
+
bool gFoundLibrary = false;
const char* gLibraryName = NULL;
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;
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");
}
+++ /dev/null
-
-// BUILD: $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib
-// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libMissingDylib.dylib -o $BUILD_DIR/libMissingDylib.dylib
-// BUILD: $CC emptyMain.c $BUILD_DIR/libMissingDylib.dylib -o $BUILD_DIR/prog_missing_dylib.exe
-// BUILD: $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib
-// BUILD: $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL
-// BUILD: $CC useSymbol.c $BUILD_DIR/libHasSymbols.dylib -o $BUILD_DIR/prog_missing_symbol.exe
-// BUILD: $CC main.c -o $BUILD_DIR/dyld_abort_tests.exe
-
-// NO_CRASH_LOG: prog_missing_dylib.exe
-// NO_CRASH_LOG: prog_missing_symbol.exe
-
-// RUN: ./dyld_abort_tests.exe
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <err.h>
-#include <System/sys/reason.h>
-#include <System/sys/proc_info.h>
-#include <System/kern/kern_cdata.h>
-#include <libproc.h>
-#include <mach-o/dyld_priv.h>
-
-
-static bool sSignalCaught = false;
-static bool sChildAbortInfoCorrect = false;
-static pid_t sChildPid = 0;
-static uint64_t sExpectedDyldReason = 0;
-static const char* sExpectedDylibPath = NULL;
-static const char* sExpectedSymbol = NULL;
-
-
-static void childDied(int sig)
-{
- sSignalCaught = true;
- //printf("sigchld for pid=%d\n", sChildPid);
-
- struct proc_exitreasoninfo info;
- bzero(&info, sizeof(info));
- uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE];
- bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE);
- info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
- info.eri_kcd_buf = (user_addr_t)packReasonData;
- //fprintf(stderr, "info=%p\n", &info);
- int procResult = proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE);
- if ( procResult != sizeof(struct proc_exitreasoninfo) ) {
- printf("bad return size from proc_pidinfo(), %d expected %lu\n", procResult, PROC_PIDEXITREASONINFO_SIZE);
- return;
- }
- if ( info.eri_namespace != OS_REASON_DYLD ) {
- printf("eri_namespace (%d) != OS_REASON_DYLD\n", info.eri_namespace);
- return;
- }
- if ( info.eri_code != sExpectedDyldReason ) {
- printf("eri_code (%llu) != %lld\n", sExpectedDyldReason, info.eri_code);
- return;
- }
- kcdata_iter_t iter = kcdata_iter(packReasonData, info.eri_reason_buf_size);
-
- if ( !kcdata_iter_valid(iter) ) {
- printf("invalid kcdata iterator from payload data\n");
- return;
- }
-
- if ( kcdata_iter_type(iter) != KCDATA_BUFFER_BEGIN_OS_REASON ){
- printf("first kcdata from payload data is not KCDATA_BUFFER_BEGIN_OS_REASON\n");
- return;
- }
-
- kcdata_iter_t payloadIter = kcdata_iter_find_type(iter, EXIT_REASON_USER_PAYLOAD);
- if ( !kcdata_iter_valid(payloadIter) ) {
- printf("invalid kcdata payload iterator from payload data\n");
- return;
- }
- const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter);
-
- if ( dyldInfo->version != 1 ) {
- printf("dyld payload is not version 1\n");
- return;
- }
-
- if ( (dyldInfo->flags & 1) == 0 ) {
- printf("dyld flags should have low bit set to me process terminated at launch\n");
- return;
- }
-
- if ( sExpectedDylibPath != NULL ) {
- if ( dyldInfo->targetDylibPathOffset != 0 ) {
- const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset;
- if ( strstr(targetDylib, sExpectedDylibPath) == NULL ) {
- printf("dylib path (%s) not what expected (%s)\n", targetDylib, sExpectedDylibPath);
- return;
- }
- }
- else {
- printf("dylib path (%s) not provided by dyld\n", sExpectedDylibPath);
- return;
- }
- }
-
- if ( sExpectedSymbol != NULL ) {
- if ( dyldInfo->targetDylibPathOffset != 0 ) {
- const char* missingSymbol = (char*)dyldInfo + dyldInfo->symbolOffset;
- if ( strcmp(sExpectedSymbol, missingSymbol) != 0 ) {
- printf("symbol (%s) not what expected (%s)\n", missingSymbol, sExpectedSymbol);
- return;
- }
- }
- else {
- printf("symbol (%s) not provided by dyld\n", sExpectedSymbol);
- return;
- }
- }
-
- sChildAbortInfoCorrect = true;
-}
-
-
-bool runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* expectedSymbol)
-{
- sSignalCaught = false;
- sChildAbortInfoCorrect = false;
- sExpectedDyldReason = dyldReason;
- sExpectedDylibPath = expectedDylibPath;
- sExpectedSymbol = expectedSymbol;
-
- // fork and exec child
- sChildPid = fork();
- if ( sChildPid < 0 )
- err(EXIT_FAILURE, "fork");
- if ( sChildPid == 0 ) {
- // child side
- char* childArgv[] = { (char*)prog, NULL };
- int result = execvp(prog, childArgv);
- err(EXIT_FAILURE, "exec(\"%s\",...)", prog);
- }
- for(int i=0; i < 10; ++i) {
- if ( sSignalCaught )
- break;
- sleep(1);
- }
-
- return sChildAbortInfoCorrect;
-}
-
-
-int main(int argc, const char* argv[])
-{
- bool someTestFailed = false;
- printf("[BEGIN] dyld_abort_payload\n");
-
- // set up signal handler for catching child terminations
- signal(SIGCHLD, childDied);
-
- // test launch program with missing library
- if ( !runTest("./prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL) ) {
- printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_DYLIB_MISSING\n");
- someTestFailed = true;
- }
-
- // test launch program with missing symbol
- if ( !runTest("./prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol") ) {
- printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_SYMBOL_MISSING\n");
- someTestFailed = true;
- }
-
- if ( !someTestFailed )
- printf("[PASS] dyld_abort_payload\n");
-
- return 0;
-}
-
--- /dev/null
+
+// BUILD: $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib
+// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libMissingDylib.dylib -o $BUILD_DIR/libMissingDylib.dylib
+// BUILD: $CC emptyMain.c $BUILD_DIR/libMissingDylib.dylib -o $BUILD_DIR/prog_missing_dylib.exe
+// BUILD: $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib
+// BUILD: $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL
+// BUILD: $CC useSymbol.c $BUILD_DIR/libHasSymbols.dylib -o $BUILD_DIR/prog_missing_symbol.exe
+// BUILD: $CXX main.cpp -o $BUILD_DIR/dyld_abort_tests.exe
+
+// NO_CRASH_LOG: prog_missing_dylib.exe
+// NO_CRASH_LOG: prog_missing_symbol.exe
+
+// RUN: ./dyld_abort_tests.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <err.h>
+#include <System/sys/reason.h>
+#include <System/sys/proc_info.h>
+#include <System/kern/kern_cdata.h>
+#include <libproc.h>
+#include <mach-o/dyld_priv.h>
+
+#include "test_support.h"
+
+
+void runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* expectedSymbol) {
+ _process process;
+ process.set_executable_path(prog);
+ process.set_crash_handler(^(task_t task) {
+ LOG("Crash for task=%u", task);
+ vm_address_t corpse_data;
+ uint32_t corpse_size;
+ if (task_map_corpse_info(mach_task_self(), task, &corpse_data, &corpse_size) != KERN_SUCCESS) {
+ FAIL("Could not read corpse data");
+ }
+ kcdata_iter_t autopsyData = kcdata_iter((void*)corpse_data, corpse_size);
+ if (!kcdata_iter_valid(autopsyData)) {
+ FAIL("Corpse Data Invalid");
+ }
+ kcdata_iter_t exitReasonData = kcdata_iter_find_type(autopsyData, EXIT_REASON_SNAPSHOT);
+ if (!kcdata_iter_valid(exitReasonData)) {
+ FAIL("Could not find exit data");
+ }
+ struct exit_reason_snapshot *ers = (struct exit_reason_snapshot *)kcdata_iter_payload(exitReasonData);
+
+ if ( ers->ers_namespace != OS_REASON_DYLD ) {
+ FAIL("eri_namespace (%d) != OS_REASON_DYLD", ers->ers_namespace);
+ }
+ if ( ers->ers_code != dyldReason ) {
+ FAIL("eri_code (%llu) != dyldReason (%lld)", ers->ers_code, dyldReason);
+ }
+ kcdata_iter_t iter = kcdata_iter((void*)corpse_data, corpse_size);
+
+ KCDATA_ITER_FOREACH(iter) {
+ if (kcdata_iter_type(iter) == KCDATA_TYPE_NESTED_KCDATA) {
+ kcdata_iter_t nestedIter = kcdata_iter(kcdata_iter_payload(iter), kcdata_iter_size(iter));
+ if ( kcdata_iter_type(nestedIter) != KCDATA_BUFFER_BEGIN_OS_REASON ){
+ return;
+ }
+ kcdata_iter_t payloadIter = kcdata_iter_find_type(nestedIter, EXIT_REASON_USER_PAYLOAD);
+ if ( !kcdata_iter_valid(payloadIter) ) {
+ FAIL("invalid kcdata payload iterator from payload data");
+ }
+ const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter);
+
+ if ( dyldInfo->version != 1 ) {
+ FAIL("dyld payload is not version 1");
+ }
+
+ if ( (dyldInfo->flags & 1) == 0 ) {
+ FAIL("dyld flags should have low bit set to indicate process terminated during launch");
+ }
+
+ if ( expectedDylibPath != NULL ) {
+ if ( dyldInfo->targetDylibPathOffset != 0 ) {
+ const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset;
+ if ( strstr(targetDylib, expectedDylibPath) == NULL ) {
+ FAIL("dylib path (%s) not what expected (%s)", targetDylib, expectedDylibPath);
+ }
+ } else {
+ FAIL("dylib path (%s) not provided by dyld", expectedDylibPath);
+ }
+ }
+
+ if ( expectedSymbol != NULL ) {
+ if ( dyldInfo->targetDylibPathOffset != 0 ) {
+ const char* missingSymbol = (char*)dyldInfo + dyldInfo->symbolOffset;
+ if ( strcmp(expectedSymbol, missingSymbol) != 0 ) {
+ FAIL("symbol (%s) not what expected (%s)", missingSymbol, expectedSymbol);
+ }
+ } else {
+ FAIL("symbol (%s) not provided by dyld", expectedSymbol);
+ }
+ }
+ PASS("Success");
+ }
+ }
+ FAIL("Did not find EXIT_REASON_USER_PAYLOAD");
+ });
+ process.launch();
+}
+
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ // test launch program with missing library
+ runTest("./prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL);
+// runTest("./prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol");
+ PASS("Success");
+}
+
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
+
bool isParent = true;
static void notifyBeforeFork(const struct mach_header* mh, intptr_t vmaddr_slide)
// 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
}
}
-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
+}
#include <string.h>
#include <mach-o/dyld_priv.h>
-extern struct mach_header __dso_handle;
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] dyld_get_image_versions\n");
+extern struct mach_header __dso_handle;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// should succeed
dyld_get_image_versions(&__dso_handle, ^(dyld_platform_t platform, uint32_t sdkVersion, uint32_t minOS) {
- printf("main binary: platform=%d, sdk=0x%08X, minOS-0x%08X\n", platform, sdkVersion, minOS);
+ LOG("main binary: platform=%d, sdk=0x%08X, minOS-0x%08X", platform, sdkVersion, minOS);
});
uint8_t badFile[4096];
// 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");
}
#include <string.h>
#include <mach-o/dyld_priv.h>
-extern struct mach_header __dso_handle;
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] dyld_get_sdk_version\n");
+extern struct mach_header __dso_handle;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// should succeed
if ( dyld_get_sdk_version(&__dso_handle) == 0 ) {
- printf("[FAIL] dyld_get_sdk_version: expected SDK\n");
- return 0;
+ FAIL("dyld_get_sdk_version: expected SDK");
}
// should fail
const char* text = "bad text";
if ( dyld_get_sdk_version((struct mach_header*)text) != 0 ) {
- printf("[FAIL] dyld_get_sdk_version: expected failure\n");
- return 0;
+ FAIL("dyld_get_sdk_version: expected failure");
}
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");
}
#include <string.h>
#include <mach-o/dyld_priv.h>
-int main(int argc, const char* argv[])
-{
- printf("[BEGIN] dyld_has_inserted_or_interposing_libraries\n");
+#include "test_support.h"
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
bool actual = dyld_has_inserted_or_interposing_libraries();
bool expected = (argc == 2) && (strcmp(argv[1], "true") == 0);
- const char* result = (actual == expected) ? "PASS" : "FAIL";
- printf("[%s] dyld_has_inserted_or_interposing_libraries\n", result);
+ if (actual != expected) {
+ FAIL("dyld_has_inserted_or_interposing_libraries");
+ }
- return 0;
+ PASS("Success");
}
#include <mach-o/dyld.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] dyld_image_path_containing_address-test\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
int count = _dyld_image_count();
for (int i=0; i < count; ++i) {
const struct mach_header* mh = _dyld_get_image_header(i);
const char* name1 = _dyld_get_image_name(i);
const char* name2 = dyld_image_path_containing_address(mh);
if ( strcmp(name1, name2) != 0 ) {
- printf("[FAIL] dyld_image_path_containing_address-test: %s != %s\n", name1, name2);
- return 0;
+ FAIL("%s != %s", name1, name2);
}
}
- printf("[PASS] dyld_image_path_containing_address-test\n");
- return 0;
+ PASS("Success");
}
#include <stdlib.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] dyld_need_closure\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// We only support trying to save to containerised paths, so anything not
// of that form should fail
if ( !dyld_need_closure("./foo.exe", "/tmp/Containers/Data/") ) {
- printf("[FAIL] dyld_closure: Should have needed a closure for containerised path\n");
- return 0;
+ FAIL("Should have needed a closure for containerised path");
}
if ( dyld_need_closure("./foo.exe", "/tmp/Containers/Data2/") ) {
- printf("[FAIL] dyld_closure: Should have rejected a closure for non-containerised path\n");
- return 0;
+ FAIL("Should have rejected a closure for non-containerised path");
}
- printf("[PASS] dyld_need_closure\n");
-
- return 0;
+ PASS("Success");
}
+#include <stdio.h>
#include <signal.h>
-#include <unistd.h>
-#include <mach/mach.h>
+#include <stdlib.h>
+#include <dispatch/dispatch.h>
__attribute__((section("__DATA,__allow_alt_plat"))) uint64_t dummy;
-int main()
-{
- (void)kill(getpid(), SIGSTOP);
- return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ dispatch_source_t exitSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, getppid(),
+ DISPATCH_PROC_EXIT, dispatch_get_main_queue());
+ dispatch_source_set_event_handler(exitSource, ^{
+ exit(0);
+ });
+ dispatch_resume(exitSource);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ kill(getppid(), SIGUSR1);
+ });
+ dispatch_main();
}
+++ /dev/null
-
-// BUILD: $CC linksWithCF.c -o $BUILD_DIR/linksWithCF.exe -framework CoreFoundation
-// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info.exe -ldarwintest
-// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info.exe
-
-// RUN: $SUDO ./dyld_process_info.exe
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <spawn.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-#include <sys/types.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <mach-o/dyld_priv.h>
-#include <mach-o/dyld_process_info.h>
-#include <Availability.h>
-
-#include "dyld_test.h"
-
-static void inspectProcess(task_t task, bool launchedSuspended, bool expectCF, bool forceIOSMac)
-{
- kern_return_t result;
- dyld_process_info info = _dyld_process_info_create(task, 0, &result);
- T_EXPECT_MACH_SUCCESS(result, "dyld_process_info() should succeed");
- T_ASSERT_NOTNULL(info, "dyld_process_info(task, 0) alwats return a value");
-
- dyld_process_state_info stateInfo;
- bzero(&stateInfo, sizeof(stateInfo));
- _dyld_process_info_get_state(info, &stateInfo);
- T_EXPECT_EQ_UINT((stateInfo.dyldState == dyld_process_state_not_started), launchedSuspended, "If launchSuspended then stateInfo.dyldState shoould be dyld_process_state_not_started");
- if ( !launchedSuspended ) {
- T_EXPECT_GE_UCHAR(stateInfo.dyldState, dyld_process_state_libSystem_initialized, "libSystem should be initalized by now");
- T_EXPECT_GT_UINT(stateInfo.imageCount, 0, "image count should be > 0");
- T_EXPECT_GT_UINT(stateInfo.initialImageCount, 0, "initial image count should be > 0");
- T_EXPECT_GE_UINT(stateInfo.imageCount, stateInfo.initialImageCount, "image count should be >= initial image count");
- }
-
- if (launchedSuspended) {
- T_EXPECT_EQ_UINT(_dyld_process_info_get_platform(info), 0, "_dyld_process_info_get_platform() should be 0 for launchSuspended processes");
- } else if (forceIOSMac) {
- T_EXPECT_EQ_UINT(_dyld_process_info_get_platform(info), PLATFORM_IOSMAC, "_dyld_process_info_get_platform() should be PLATFORM_IOSMAC");
- } else {
- T_EXPECT_EQ_UINT(_dyld_process_info_get_platform(info), dyld_get_active_platform(), "_dyld_process_info_get_platform() should be the same dyld_get_active_platform()");
- }
-
- __block bool foundDyld = false;
- __block bool foundMain = false;
- __block bool foundCF = false;
- _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
- if ( strstr(path, "/dyld") != NULL )
- foundDyld = true;
- if ( strstr(path, "/linksWithCF.exe") != NULL )
- foundMain = true;
- if ( strstr(path, "/dyld_process_info.exe") != NULL )
- foundMain = true;
- if ( strstr(path, "/CoreFoundation.framework/") != NULL )
- foundCF = true;
- });
- T_EXPECT_TRUE(foundDyld, "dyld should always be in the image list");
- T_EXPECT_TRUE(foundMain, "The main executable should always be in the image list");
- if (expectCF) {
- T_EXPECT_TRUE(foundCF, "CF should be in the image list");
- }
-
- _dyld_process_info_release(info);
-}
-
-static void launchTest(bool launchOtherArch, bool launchSuspended, bool forceIOSMac)
-{
- if (forceIOSMac) { setenv("DYLD_FORCE_PLATFORM", "6", 1); }
- pid_t pid = T_POSIXSPAWN_ASSERT(launchSuspended, launchOtherArch, INSTALL_PATH "/linksWithCF.exe");
- task_t task = T_TASK_FOR_PID_ASSERT(pid);
- if (forceIOSMac) { unsetenv("DYLD_FORCE_PLATFORM"); }
-
- // wait until process is up and has suspended itself
- struct task_basic_info info;
- do {
- unsigned count = TASK_BASIC_INFO_COUNT;
- kern_return_t kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count);
- usleep(10000);
- } while ( info.suspend_count == 0 );
-
- inspectProcess(task, launchSuspended, !launchSuspended, forceIOSMac);
- int r = kill(pid, SIGKILL);
- waitpid(pid, &r, 0);
-}
-
-T_DECL_DYLD(dyld_process_info, "Test basic dyld_process_info functionality", T_META_ASROOT(true)) {
- launchTest(false, false, false);
- launchTest(false, true, false);
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
- // FIXME: Reenable these ones i386 is turned back on for simulators
- //launchTest(true, false, false);
- //launchTest(true, true, false);
- launchTest(false, false, true);
- launchTest(false, true, true);
- //FIXME: This functionality is broken, but it is an edge case no one should ever hit
- //launchTest(true, true, true);
-#endif
- inspectProcess(mach_task_self(), false, false, false);
-}
--- /dev/null
+
+// BUILD: $CC linksWithCF.c -o $BUILD_DIR/linksWithCF.exe -framework CoreFoundation
+// BUILD: $CXX main.cpp -o $BUILD_DIR/dyld_process_info.exe -DRUN_DIR="$RUN_DIR"
+// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info.exe
+
+// RUN: $SUDO ./dyld_process_info.exe
+
+#include <Block.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/dyld_priv.h>
+#include <mach-o/dyld_process_info.h>
+#include <Availability.h>
+
+#include "test_support.h"
+
+
+static void inspectProcess(task_t task, bool launchedSuspended, bool expectCF, bool forceIOSMac)
+{
+ kern_return_t result;
+ dyld_process_info info = _dyld_process_info_create(task, 0, &result);
+ if (result != KERN_SUCCESS) {
+ FAIL("dyld_process_info() should succeed");
+ }
+ if (info == NULL) {
+ FAIL("dyld_process_info(task, 0) alwats return a value");
+ }
+
+ dyld_process_state_info stateInfo;
+ bzero(&stateInfo, sizeof(stateInfo));
+ _dyld_process_info_get_state(info, &stateInfo);
+ if ((stateInfo.dyldState == dyld_process_state_not_started) != launchedSuspended) {
+ FAIL("If launchSuspended then stateInfo.dyldState shoould be dyld_process_state_not_started");
+ }
+ if ( !launchedSuspended ) {
+ if (stateInfo.dyldState < dyld_process_state_libSystem_initialized) { FAIL("libSystem should be initalized by now"); }
+ if (stateInfo.imageCount == 0) { return FAIL("image count should be > 0"); }
+ if (stateInfo.initialImageCount == 0) { return FAIL("initial image count should be > 0"); }
+ if (stateInfo.imageCount < stateInfo.initialImageCount) { FAIL("image count should be >= initial image count"); }
+ }
+
+ dyld_platform_t remotePlatform = _dyld_process_info_get_platform(info);
+ dyld_platform_t localPlatform = dyld_get_active_platform();
+ if (launchedSuspended) {
+ if (remotePlatform != 0) {
+ FAIL("_dyld_process_info_get_platform() should be 0 for launchSuspended processes");
+ }
+ } else if (forceIOSMac && (remotePlatform != PLATFORM_IOSMAC)) {
+ FAIL("_dyld_process_info_get_platform(%u) should be PLATFORM_IOSMAC", remotePlatform);
+ } else if (!forceIOSMac && (remotePlatform != localPlatform)) {
+ FAIL("_dyld_process_info_get_platform(%u) should be the same dyld_get_active_platform(%u)",
+ remotePlatform, localPlatform);
+ }
+
+ __block bool foundDyld = false;
+ __block bool foundMain = false;
+ __block bool foundCF = false;
+ _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
+ if ( strstr(path, "/dyld") != NULL )
+ foundDyld = true;
+ if ( strstr(path, "/linksWithCF.exe") != NULL )
+ foundMain = true;
+ if ( strstr(path, "/dyld_process_info.exe") != NULL )
+ foundMain = true;
+ if ( strstr(path, "/CoreFoundation.framework/") != NULL )
+ foundCF = true;
+ });
+ if (!foundDyld) { FAIL("dyld should always be in the image list"); }
+ if (!foundMain) { FAIL("The main executable should always be in the image list"); }
+ if (expectCF && !foundCF) { FAIL("CF should be in the image list"); }
+
+ _dyld_process_info_release(info);
+}
+
+#if __x86_64__
+cpu_type_t otherArch[] = { CPU_TYPE_I386 };
+#elif __i386__
+cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
+#elif __arm64__
+cpu_type_t otherArch[] = { CPU_TYPE_ARM };
+#elif __arm__
+cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
+#endif
+
+
+static void launchTest(bool launchOtherArch, bool launchSuspended, bool forceIOSMac)
+{
+ LOG("launchTest %s", launchSuspended ? "suspended" : "unsuspended");
+ const char * program = RUN_DIR "/linksWithCF.exe";
+
+ _process process;
+ process.set_executable_path(RUN_DIR "/linksWithCF.exe");
+ process.set_launch_suspended(launchSuspended);
+ process.set_launch_async(true);
+ if (forceIOSMac) {
+ LOG("Launching native");
+ const char* env[] = { "TEST_OUTPUT=None", "DYLD_FORCE_PLATFORM=6", NULL};
+ process.set_env(env);
+ } else {
+ LOG("Launching iOSMac");
+ const char* env[] = { "TEST_OUTPUT=None", NULL};
+ process.set_env(env);
+ }
+ pid_t pid = process.launch();
+ LOG("launchTest pid: %d", pid);
+
+ task_t task;
+ if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS) {
+ FAIL("task_for_pid() failed");
+ }
+ LOG("launchTest task: %u", task);
+
+ // wait until process is up and has suspended itself
+ if (!launchSuspended) {
+ dispatch_queue_t queue = dispatch_queue_create("com.apple.test.dyld_process_info", NULL);
+ // We do this instead of using a dispatch_semaphore to prevent priority inversions
+ dispatch_block_t oneShotSemaphore = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
+ dispatch_source_t signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1,
+ 0, queue);
+ dispatch_source_set_event_handler(signalSource, ^{
+ LOG("Recieved signal");
+ oneShotSemaphore();
+ dispatch_source_cancel(signalSource);
+ });
+ dispatch_resume(signalSource);
+ dispatch_block_wait(oneShotSemaphore, DISPATCH_TIME_FOREVER);
+ }
+ LOG("task running");
+
+ inspectProcess(task, launchSuspended, !launchSuspended, forceIOSMac);
+}
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ signal(SIGUSR1, SIG_IGN);
+ TIMEOUT(120);
+ launchTest(false, false, false);
+ launchTest(false, true, false);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // FIXME: Reenable these ones i386 is turned back on for simulators
+ //launchTest(true, false, false);
+ //launchTest(true, true, false);
+ launchTest(false, false, true);
+ launchTest(false, true, true);
+ //FIXME: This functionality is broken, but it is an edge case no one should ever hit
+ //launchTest(true, true, true);
+#endif
+ dispatch_async( dispatch_get_main_queue(), ^{
+ inspectProcess(mach_task_self(), false, false, false);
+ PASS("Success");
+ });
+ dispatch_main();
+}
+++ /dev/null
-
-// BUILD: $CC target.c -o $BUILD_DIR/target.exe
-// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib
-// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info_notify.exe
-// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_notify.exe
-
-// RUN_TIMEOUT: 2400
-// RUN: $SUDO ./dyld_process_info_notify.exe $RUN_DIR/target.exe
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <spawn.h>
-#include <errno.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <mach-o/dyld_process_info.h>
-#include <dispatch/dispatch.h>
-#include <Availability.h>
-
-
-extern char** environ;
-
-#if __x86_64__
- cpu_type_t otherArch[] = { CPU_TYPE_I386 };
-#elif __i386__
- cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
-#elif __arm64__
- cpu_type_t otherArch[] = { CPU_TYPE_ARM };
-#elif __arm__
- cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
-#endif
-
-struct task_and_pid {
- pid_t pid;
- task_t task;
-};
-
-static struct task_and_pid launchTest(const char* testProgPath, const char* arg1, bool launchOtherArch, bool launchSuspended)
-{
- //fprintf(stderr, "launchTest() launchOtherArch=%d, launchSuspended=%d, arg=%s\n", launchOtherArch, launchSuspended, arg1);
- posix_spawnattr_t attr = 0;
- if ( posix_spawnattr_init(&attr) != 0 ) {
- printf("[FAIL] dyld_process_info_notify posix_spawnattr_init()\n");
- exit(0);
- }
- if ( launchSuspended ) {
- if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
- printf("[FAIL] dyld_process_info_notify POSIX_SPAWN_START_SUSPENDED\n");
- exit(0);
- }
- }
- if ( launchOtherArch ) {
- size_t copied;
- if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) {
- printf("[FAIL] dyld_process_info_notify posix_spawnattr_setbinpref_np()\n");
- exit(0);
- }
- }
-
- struct task_and_pid child;
- const char* argv[] = { testProgPath, arg1, NULL };
- int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ);
- if ( psResult != 0 ) {
- printf("[FAIL] dyld_process_info_notify posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
- exit(0);
- }
- if (posix_spawnattr_destroy(&attr) != 0) {
- printf("[FAIL] dyld_process_info_notify posix_spawnattr_destroy()\n");
- exit(0);
- }
- if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
- printf("[FAIL] dyld_process_info_notify task_for_pid()\n");
- (void)kill(child.pid, SIGKILL);
- exit(0);
- }
-
- return child;
-}
-
-static void killTest(struct task_and_pid tp) {
- int r = kill(tp.pid, SIGKILL);
- waitpid(tp.pid, &r, 0);
-}
-
-static void wait_util_task_suspended(task_t task)
-{
- struct task_basic_info info;
- do {
- unsigned count = TASK_BASIC_INFO_COUNT;
- kern_return_t kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count);
- //fprintf(stderr, "task_info() => %d, suspendCount=%d\n", kr, info.suspend_count);
- sleep(1);
- } while ( info.suspend_count == 0 );
-}
-
-
-static bool monitor(struct task_and_pid tp, bool disconnectEarly, bool attachLate)
-{
- kern_return_t kr;
- __block bool sawMainExecutable = false;
- __block bool sawlibSystem = false;
- __block bool gotTerminationNotice = false;
- __block bool gotEarlyNotice = false;
- __block bool gotMainNotice = false;
- __block bool gotMainNoticeBeforeAllInitialDylibs = false;
- __block bool gotFooNoticeBeforeMain = false;
-
- __block int libFooLoadCount = 0;
- __block int libFooUnloadCount = 0;
- dispatch_semaphore_t taskDone = dispatch_semaphore_create(0);
-
- dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
-
- unsigned count = 0;
- dyld_process_info_notify handle;
- do {
- handle = _dyld_process_info_notify(tp.task, serviceQueue,
- ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
- if ( strstr(path, "/target.exe") != NULL )
- sawMainExecutable = true;
- if ( strstr(path, "/libSystem") != NULL )
- sawlibSystem = true;
- if ( strstr(path, "/libfoo.dylib") != NULL ) {
- if ( !gotMainNotice )
- gotFooNoticeBeforeMain = true;
- if ( unload )
- ++libFooUnloadCount;
- else
- ++libFooLoadCount;
- if ( disconnectEarly ) {
- gotEarlyNotice = true;
- dispatch_semaphore_signal(taskDone);
- }
- }
- },
- ^{
- gotTerminationNotice = true;
- dispatch_semaphore_signal(taskDone);
- },
- &kr);
- ++count;
- if ( handle == NULL )
- fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d, count=%d\n", kr, count);
- } while ( (handle == NULL) && (count < 5) );
-
- if ( handle == NULL ) {
- return false;
- }
-
- if (!attachLate) {
- // If the process starts suspended register for main(),
- // otherwise skip since this test is a race between
- // process setup and notification registration
- _dyld_process_info_notify_main(handle, ^{
- //fprintf(stderr, "target entering main()\n");
- gotMainNotice = true;
- if ( !sawMainExecutable || !sawlibSystem )
- gotMainNoticeBeforeAllInitialDylibs = true;
- });
- } else {
- // if process suspends itself, wait until it has done so
- wait_util_task_suspended(tp.task);
- }
-
- // resume from initial suspend
- kill(tp.pid, SIGCONT);
-
- // block waiting for notification that target has exited
- bool gotSignal = (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0);
- _dyld_process_info_notify_release(handle);
-
-
- if ( !gotSignal ) {
- fprintf(stderr, "did not get exit signal\n");
- return false;
- }
-
- // Do not run any tests associated with startup unless the kernel suspended us
- // before main()
- if (!attachLate) {
- if ( !sawMainExecutable ) {
- fprintf(stderr, "did not get load notification of main executable\n");
- return false;
- }
-
- if ( !gotMainNotice ) {
- fprintf(stderr, "did not get notification of main()\n");
- return false;
- }
-
- if ( gotMainNoticeBeforeAllInitialDylibs ) {
- fprintf(stderr, "notification of main() arrived before all initial dylibs\n");
- return false;
- }
-
- if ( gotFooNoticeBeforeMain ) {
- fprintf(stderr, "notification of main() arrived after libfoo load notice\n");
- return false;
- }
-
- if ( !sawlibSystem ) {
- fprintf(stderr, "did not get load notification of libSystem\n");
- return false;
- }
- }
-
- if ( disconnectEarly ) {
- if ( libFooLoadCount != 1 ) {
- fprintf(stderr, "got %d load notifications about libFoo instead of 1\n", libFooLoadCount);
- return false;
- }
- if ( libFooUnloadCount != 0 ) {
- fprintf(stderr, "got %d unload notifications about libFoo instead of 1\n", libFooUnloadCount);
- return false;
- }
- }
- else {
- if ( libFooLoadCount != 3 ) {
- fprintf(stderr, "got %d load notifications about libFoo instead of 3\n", libFooLoadCount);
- return false;
- }
- if ( libFooUnloadCount != 3 ) {
- fprintf(stderr, "got %d unload notifications about libFoo instead of 3\n", libFooUnloadCount);
- return false;
- }
- }
-
- return true;
-}
-
-static void validateMaxNotifies(struct task_and_pid tp)
-{
- dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
- dyld_process_info_notify handles[10];
- // This loop goes through 10 iterations
- // i = 0..7 Should succeed
- // i = 8 Should fail, but trigger a release that frees up a slot
- // i = 9 Should succeed
- for (int i=0; i < 10; ++i) {
- kern_return_t kr;
- handles[i] = _dyld_process_info_notify(tp.task, serviceQueue,
- ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
- //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n",
- // unload, machHeader, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
- // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path);
- },
- ^{
- //fprintf(stderr, "target exited\n");
- },
- &kr);
- if ( handles[i] == NULL ) {
- if ( i == 8 ) {
- // expected failure, because only 8 simultaneous connections allowed
- // release one and try again
- _dyld_process_info_notify_release(handles[4]);
- handles[4] = NULL;
- }
- else {
- fprintf(stderr, "_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d\n", kr, i);
- killTest(tp);
- exit(0);
- }
- }
- }
- // release all
- for (int i=0; i < 10; ++i) {
- if ( handles[i] != NULL ) {
- _dyld_process_info_notify_release(handles[i]);
- }
- }
- dispatch_release(serviceQueue);
-}
-
-static bool testSelfAttach(void) {
- __block bool retval = false;
- kern_return_t kr = KERN_SUCCESS;
- dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
- dyld_process_info_notify handle = _dyld_process_info_notify(mach_task_self(), serviceQueue,
- ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
- if ( strstr(path, "/libfoo.dylib") != NULL ) {
- retval = true;
- }
- },
- ^{},
- &kr);
- if ( handle == NULL ) {
- fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d\n", kr);
- }
- void* h = dlopen("./libfoo.dylib", 0);
- dlclose(h);
- return retval;
-}
-
-int main(int argc, const char* argv[])
-{
- if ( argc < 2 ) {
- printf("[FAIL] dyld_process_info_notify missing argument\n");
- exit(0);
- }
- const char* testProgPath = argv[1];
-
- dispatch_async(dispatch_get_main_queue(), ^{
- struct task_and_pid child;
-
- // test 1) launch test program suspended in same arch as this program
- printf("[BEGIN] dyld_process_info_notify laucnh suspended (same arch)\n");
- child = launchTest(testProgPath, "", false, true);
- if ( ! monitor(child, false, false) ) {
- printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n");
- killTest(child);
- exit(0);
- }
- killTest(child);
- printf("[PASS] dyld_process_info_notify laucnh suspended (same arch)\n");
-
- // test 2) launch test program in same arch as this program where it sleeps itself
- printf("[BEGIN] dyld_process_info_notify laucnh suspend-in-main (same arch)\n");
- child = launchTest(testProgPath, "suspend-in-main", false, false);
- validateMaxNotifies(child);
- if ( ! monitor(child, false, true) ) {
- printf("[FAIL] dyld_process_info_notify launch suspend-in-main missed some notifications\n");
- killTest(child);
- exit(0);
- }
- killTest(child);
- printf("[PASS] dyld_process_info_notify laucnh suspend-in-main (same arch)\n");
-
-#if 0
- // test 3) launch test program suspended in opposite arch as this program
- printf("[BEGIN] dyld_process_info_notify laucnh suspended (other arch)\n");
- child = launchTest(testProgPath, "", true, true);
- if ( ! monitor(child, false, false) ) {
- printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n");
- killTest(child);
- exit(0);
- }
- killTest(child);
- printf("[PASS] dyld_process_info_notify laucnh suspended (other arch)\n");
-
- // test 4) launch test program in opposite arch as this program where it sleeps itself
- printf("[BEGIN] dyld_process_info_notify laucnh suspend-in-main (other arch)\n");
- child = launchTest(testProgPath, "suspend-in-main", true, false);
- if ( ! monitor(child, false, true) ) {
- printf("[FAIL] dyld_process_info_notify launch other arch suspend-in-main missed some notifications\n");
- killTest(child);
- exit(0);
- }
- killTest(child);
- printf("[PASS] dyld_process_info_notify laucnh suspend-in-main (other arch)\n");
-#endif
-
- // test 5) launch test program where we disconnect from it after first dlopen
- printf("[BEGIN] dyld_process_info_notify disconnect\n");
- child = launchTest(testProgPath, "", false, true);
- if ( ! monitor(child, true, false) ) {
- printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n");
- killTest(child);
- exit(0);
- }
- killTest(child);
- printf("[PASS] dyld_process_info_notify disconnect\n");
-
- // test 6) attempt to monitor the monitoring process
- printf("[BEGIN] dyld_process_info_notify self-attach\n");
- if (! testSelfAttach() ) {
- printf("[FAIL] dyld_process_info_notify self notification\n");
- }
- printf("[PASS] dyld_process_info_notify self-attach\n");
-
- exit(0);
- });
-
- dispatch_main();
-}
--- /dev/null
+
+// BUILD: $CC target.c -o $BUILD_DIR/target.exe -DRUN_DIR="$RUN_DIR"
+// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib
+// BUILD: $CXX main.cpp -o $BUILD_DIR/dyld_process_info_notify.exe -DRUN_DIR="$RUN_DIR"
+// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_notify.exe
+
+// RUN_TIMEOUT: 2400
+// XFAIL: $SUDO ./dyld_process_info_notify.exe $RUN_DIR/target.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/dyld_process_info.h>
+#include <dispatch/dispatch.h>
+#include <Availability.h>
+
+#include "test_support.h"
+
+extern char** environ;
+
+void launchTest(bool launchSuspended, bool disconnectEarly)
+{
+ LOG("launchTest (%s)", launchSuspended ? "suspended" : "unsuspened");
+ dispatch_queue_t queue = dispatch_queue_create("com.apple.dyld.test.dyld_process_info", NULL);
+ // We do this instead of using a dispatch_semaphore to prevent priority inversions
+ dispatch_block_t taskDone = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
+ dispatch_block_t taskStarted = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
+ pid_t pid;
+
+ task_t task;
+ char subTestNameBuffer[256];
+ char *subTestName = &subTestNameBuffer[0];
+ __block bool sawMainExecutable = false;
+ __block bool sawlibSystem = false;
+ __block bool gotTerminationNotice = false;
+ __block bool gotEarlyNotice = false;
+ __block bool gotMainNotice = false;
+ __block bool gotMainNoticeBeforeAllInitialDylibs = false;
+ __block bool gotFooNoticeBeforeMain = false;
+
+ __block int libFooLoadCount = 0;
+ __block int libFooUnloadCount = 0;
+ __block dyld_process_info_notify handle;
+
+ _process process;
+ process.set_executable(RUN_DIR "/target.exe");
+ const char* env[] = { "TEST_OUTPUT=None", NULL};
+ process.set_env(env);
+ process.set_launch_suspended(launchSuspended);
+ if (!launchSuspended) {
+ const char* args[] = {"suspend-in-main", NULL};
+ _process_config_set_args(process, args);
+ _process_config_set_stderr_handler(process, ^(int fd) {
+ dispatch_semaphore_signal(taskStarted);
+ });
+ _process_config_set_exit_handler(process, ^(pid_t pid) {
+ LOG("DIED (pid: %d)", pid);
+ });
+ }
+ pid = process.launch(queue);
+
+ if (!launchSuspended && dispatch_semaphore_wait(taskStarted, dispatch_time(DISPATCH_TIME_NOW, 5LL * NSEC_PER_SEC)) != 0) {
+ FAIL("Child launch timeout");
+ }
+#if 1
+ snprintf(&subTestNameBuffer[0], 256, "%s (arch: %d)", launchSuspended ? "launch suspended" : "launch suspend-in-main", currentArch);
+
+ if ( task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS ) {
+ FAIL("task_for_pid()");
+ }
+
+ kern_return_t kr;
+ unsigned count = 0;
+ do {
+ handle = _dyld_process_info_notify(task, queue,
+ ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
+ if ( strstr(path, "/target.exe") != NULL )
+ sawMainExecutable = true;
+ if ( strstr(path, "/libSystem") != NULL )
+ sawlibSystem = true;
+ if ( strstr(path, "/libfoo.dylib") != NULL ) {
+ if ( !gotMainNotice ) {
+ gotFooNoticeBeforeMain = true;
+ }
+ if ( unload ) {
+ ++libFooUnloadCount;
+ } else {
+ ++libFooLoadCount;
+ }
+ if ( disconnectEarly ) {
+ LOG("EARLY DISCONNECT");
+ gotEarlyNotice = true;
+ dispatch_semaphore_signal(taskDone);
+ }
+ }
+ },
+ ^{
+ LOG("TERMINATED (pid: %d)", pid);
+ gotTerminationNotice = true;
+ dispatch_semaphore_signal(taskDone);
+ },
+ &kr);
+ ++count;
+ if ( handle == NULL )
+ LOG("_dyld_process_info_notify() returned NULL, result=%d, count=%d", kr, count);
+ } while ( (handle == NULL) && (count < 5) );
+
+ if ( handle == NULL ) {
+ FAIL("%s: did not not get handle", subTestName);
+ }
+
+ if (launchSuspended) {
+ // If the process starts suspended register for main(),
+ // otherwise skip since this test is a race between
+ // process setup and notification registration
+ _dyld_process_info_notify_main(handle, ^{
+ LOG("target entering main()");
+ gotMainNotice = true;
+ if ( !sawMainExecutable || !sawlibSystem )
+ gotMainNoticeBeforeAllInitialDylibs = true;
+ });
+ kill(pid, SIGCONT);
+ LOG("Sent SIGCONT");
+ } else {
+ kill(pid, SIGUSR1);
+ LOG("Sent SIGUSR1");
+ }
+
+ // block waiting for notification that target has exited
+ if (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) != 0) {
+ FAIL("%s: did not get exit signal", subTestName);
+ }
+
+// dispatch_release(taskDone);
+// dispatch_release(queue);
+// _dyld_process_info_notify_release(handle);
+
+ // Do not run any tests associated with startup unless the kernel suspended us
+ // before main()
+ if (launchSuspended) {
+ if ( !sawMainExecutable ) {
+ FAIL("%s: did not get load notification of main executable", subTestName);
+ }
+
+ if ( !gotMainNotice ) {
+ FAIL("%s: did not get notification of main()", subTestName);
+ }
+
+ if ( gotMainNoticeBeforeAllInitialDylibs ) {
+ FAIL("%s: notification of main() arrived before all initial dylibs", subTestName);
+ }
+
+ if ( gotFooNoticeBeforeMain ) {
+ FAIL("%s: notification of main() arrived after libfoo load notice", subTestName);
+ }
+
+ if ( !sawlibSystem ) {
+ FAIL("%s: did not get load notification of libSystem", subTestName);
+ }
+ }
+
+ if ( disconnectEarly ) {
+ if ( libFooLoadCount != 1 ) {
+ FAIL("%s: got %d load notifications about libFoo instead of 1", subTestName, libFooLoadCount);
+ }
+ if ( libFooUnloadCount != 0 ) {
+ FAIL("%s: got %d unload notifications about libFoo instead of 1", subTestName, libFooUnloadCount);
+ }
+ }
+ else {
+ if ( libFooLoadCount != 3 ) {
+ FAIL("%s: got %d load notifications about libFoo instead of 3", subTestName, libFooLoadCount);
+ }
+ if ( libFooUnloadCount != 3 ) {
+ FAIL("%s: got %d unload notifications about libFoo instead of 3", subTestName, libFooUnloadCount);
+ }
+ }
+#endif
+}
+
+#if 0
+static void validateMaxNotifies(struct task_and_pid tp)
+{
+ dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
+ dyld_process_info_notify handles[10];
+ // This loop goes through 10 iterations
+ // i = 0..7 Should succeed
+ // i = 8 Should fail, but trigger a release that frees up a slot
+ // i = 9 Should succeed
+ for (int i=0; i < 10; ++i) {
+ kern_return_t kr;
+ handles[i] = _dyld_process_info_notify(tp.task, serviceQueue,
+ ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
+ LOG("unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s",
+ unload, machHeader, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
+ uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path);
+ },
+ ^{
+ LOG("target exited");
+ },
+ &kr);
+ if ( handles[i] == NULL ) {
+ if ( i == 8 ) {
+ // expected failure, because only 8 simultaneous connections allowed
+ // release one and try again
+ _dyld_process_info_notify_release(handles[4]);
+ handles[4] = NULL;
+ }
+ else {
+ LOG("_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d", kr, i);
+ killTest(tp);
+ exit(0);
+ }
+ }
+ }
+ // release all
+ for (int i=0; i < 10; ++i) {
+ if ( handles[i] != NULL ) {
+ _dyld_process_info_notify_release(handles[i]);
+ }
+ }
+ dispatch_release(serviceQueue);
+}
+#endif
+
+static void testSelfAttach(void) {
+ LOG("7");
+ __block bool dylibLoadNotified = false;
+ kern_return_t kr = KERN_SUCCESS;
+ dispatch_queue_t queue = dispatch_queue_create("com.apple.dyld.test.dyld_process_info.self-attach", NULL);
+ LOG("7.5");
+ dyld_process_info_notify handle = _dyld_process_info_notify(mach_task_self(), queue,
+ ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
+ if ( strstr(path, "/libfoo.dylib") != NULL ) {
+ dylibLoadNotified = true;
+ }
+ },
+ ^{},
+ &kr);
+ LOG("8");
+ if ( handle == NULL ) {
+ LOG("_dyld_process_info_notify() returned NULL, result=%d", kr);
+ }
+ LOG("8.5");
+ void* h = dlopen(RUN_DIR "/libfoo.dylib", 0);
+ LOG("8.75");
+ dlclose(h);
+ if (!dylibLoadNotified) {
+ FAIL("testSelfAttach");
+ }
+ LOG("9");
+}
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+
+ // test 1) launch test program suspended in same arch as this program
+ launchTest(true, false);
+
+ // test 2) launch test program in same arch as this program where it sleeps itself
+ launchTest(false, false);
+// validateMaxNotifies(child);
+
+ // test 3) launch test program where we disconnect from it after first dlopen
+ launchTest(true, true);
+// monitor("disconnect", child, true, false);
+
+ // test 4) attempt to monitor the monitoring process
+// testSelfAttach();
+ PASS("Success");
+
+}
#include <signal.h>
#include <unistd.h>
#include <mach/mach.h>
+#include <dispatch/dispatch.h>
-
-
-int main(int argc, const char* argv[])
-{
- if ( (argc > 1) && (strcmp(argv[1], "suspend-in-main") == 0) )
- (void)kill(getpid(), SIGSTOP);
-
+void performDylibOperations(void) {
for (int i=0; i < 3; ++i) {
- void* h = dlopen("./libfoo.dylib", 0);
+ void* h = dlopen(RUN_DIR "/libfoo.dylib", 0);
dlclose(h);
}
+ fprintf(stderr, "Done (pid: %d)\n", getpid());
+ exit(0);
+}
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ signal(SIGUSR1, SIG_IGN);
+ dispatch_source_t signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, getppid(),
+ DISPATCH_PROC_EXIT, dispatch_get_main_queue());
+ dispatch_source_set_event_handler(signalSource, ^{
+ exit(0);
+ });
+ dispatch_resume(signalSource);
+
+ if ( (argc > 1) && (strcmp(argv[1], "suspend-in-main") == 0) ) {
+ dispatch_source_t signalSourceSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL,
+ SIGUSR1, 0, dispatch_get_main_queue());
+ dispatch_source_set_event_handler(signalSourceSource, ^{
+ performDylibOperations();
+ });
+ dispatch_resume(signalSourceSource);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ fprintf(stderr, "Ready (pid: %d)\n", getpid());
+ });
+ } else {
+ performDylibOperations();
+ }
- return 0;
+ dispatch_main();
}
+++ /dev/null
-
-// BUILD: $CC target.c -o $BUILD_DIR/target.exe
-// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib -install_name $RUN_DIR/libfoo.dylib
-// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info_unload.exe
-// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_unload.exe
-
-// RUN: $SUDO ./dyld_process_info_unload.exe $RUN_DIR/target.exe
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <spawn.h>
-#include <errno.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <mach-o/dyld_process_info.h>
-
-
-extern char** environ;
-
-#if __x86_64__
- cpu_type_t otherArch[] = { CPU_TYPE_I386 };
-#elif __i386__
- cpu_type_t otherArch[] = { CPU_TYPE_X86_64 };
-#elif __arm64__
- cpu_type_t otherArch[] = { CPU_TYPE_ARM };
-#elif __arm__
- cpu_type_t otherArch[] = { CPU_TYPE_ARM64 };
-#endif
-
-struct task_and_pid {
- pid_t pid;
- task_t task;
-};
-
-static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended)
-{
- posix_spawnattr_t attr = 0;
- if ( posix_spawnattr_init(&attr) != 0 ) {
- printf("[FAIL] dyld_process_info_unload posix_spawnattr_init()\n");
- exit(0);
- }
- if ( launchSuspended ) {
- if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) {
- printf("[FAIL] dyld_process_info_unload POSIX_SPAWN_START_SUSPENDED\n");
- exit(0);
- }
- }
- if ( launchOtherArch ) {
- size_t copied;
- if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) {
- printf("[FAIL] dyld_process_info_unload posix_spawnattr_setbinpref_np()\n");
- exit(0);
- }
- }
-
- struct task_and_pid child;
- const char* argv[] = { testProgPath, NULL };
- int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ);
- if ( psResult != 0 ) {
- printf("[FAIL] dyld_process_info_unload posix_spawn(%s) failed, err=%d\n", testProgPath, psResult);
- exit(0);
- }
- if (posix_spawnattr_destroy(&attr) != 0) {
- printf("[FAIL] dyld_process_info_unload posix_spawnattr_destroy()\n");
- exit(0);
- }
-
- if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) {
- printf("[FAIL] dyld_process_info_unload task_for_pid()\n");
- kill(child.pid, SIGKILL);
- exit(0);
- }
-
- // wait until process is up and has suspended itself
- struct task_basic_info info;
- do {
- unsigned count = TASK_BASIC_INFO_COUNT;
- kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count);
- sleep(1);
- } while ( info.suspend_count == 0 );
-
- return child;
-}
-
-static void killTest(struct task_and_pid tp) {
- int r = kill(tp.pid, SIGKILL);
- waitpid(tp.pid, &r, 0);
-}
-
-static bool alwaysGetImages(struct task_and_pid tp, bool launchedSuspended)
-{
- int failCount = 0;
- for (int i=0; i < 100; ++i ) {
- kern_return_t result;
- dyld_process_info info = _dyld_process_info_create(tp.task, 0, &result);
- //fprintf(stderr, "info=%p, result=%08X\n", info, result);
- if ( i == 0 )
- (void)kill(tp.pid, SIGCONT);
- if ( info == NULL ) {
- failCount++;
- //fprintf(stderr, "info=%p, result=%08X\n", info, result);
- }
- else {
- usleep(100);
- _dyld_process_info_release(info);
- }
- }
- // ideally the fail count would be zero. But the target is dlopen/dlclosing in a tight loop, so there may never be a stable set of images.
- // The real bug driving this test case was _dyld_process_info_create() crashing when the the image list changed too fast.
- // The important thing is to not crash. Getting NULL back is ok.
- return true;
-}
-
-
-int main(int argc, const char* argv[])
-{
- printf("[BEGIN] dyld_process_info_unload\n");
-
- if ( argc < 2 ) {
- printf("[FAIL] dyld_process_info_unload missing argument\n");
- exit(0);
- }
- const char* testProgPath = argv[1];
- struct task_and_pid child;
-
- // launch test program suspended
- child = launchTest(testProgPath, false, true);
- if ( ! alwaysGetImages(child, true) ) {
- killTest(child);
- exit(0);
- }
- killTest(child);
-
- printf("[PASS] dyld_process_info_unload\n");
- return 0;
-}
--- /dev/null
+
+// BUILD: $CC target.c -o $BUILD_DIR/target.exe
+// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib -install_name $RUN_DIR/libfoo.dylib
+// BUILD: $CXX main.cpp -o $BUILD_DIR/dyld_process_info_unload.exe -DRUN_DIR="$RUN_DIR"
+// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_unload.exe
+
+// RUN: $SUDO ./dyld_process_info_unload.exe $RUN_DIR/target.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <mach-o/dyld_process_info.h>
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ _process process;
+ process.set_executable_path(RUN_DIR "/target.exe");
+ process.set_launch_suspended(true);
+ process.set_launch_async(true);
+ const char* env[] = { "TEST_OUTPUT=None", NULL};
+ process.set_env(env);
+ pid_t pid = process.launch();
+ task_t task;
+ if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS) {
+ FAIL("task_for_pid() failed");
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ int failCount = 0;
+ for (int i=0; i < 100; ++i ) {
+ kern_return_t result;
+ dyld_process_info info = _dyld_process_info_create(task, 0, &result);
+ LOG("info=%p, result=%08X", info, result);
+ if ( i == 0 )
+ (void)kill(pid, SIGCONT);
+ if ( info == NULL ) {
+ //FIXME: Compact info will never fail, make this a FAIL()
+ failCount++;
+ // ideally the fail count would be zero. But the target is dlopen/dlclosing in a tight loop, so there may never be a stable set of images.
+ // The real bug driving this test case was _dyld_process_info_create() crashing when the the image list changed too fast.
+ // The important thing is to not crash. Getting NULL back is ok.
+ LOG("info=%p, result=%08X", info, result);
+ }
+ else {
+ usleep(100);
+ _dyld_process_info_release(info);
+ }
+ }
+ PASS("Success");
+ });
+
+ dispatch_main();
+}
#include <string.h>
#include <dlfcn.h>
#include <unistd.h>
-#include <mach/mach.h>
+#include <dispatch/dispatch.h>
+#include "test_support.h"
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ signal(SIGUSR1, SIG_IGN);
+ dispatch_source_t signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, getppid(),
+ DISPATCH_PROC_EXIT, dispatch_get_main_queue());
+ dispatch_source_set_event_handler(signalSource, ^{
+ exit(0);
+ });
+ dispatch_resume(signalSource);
-int main(int argc, const char* argv[])
-{
- //fprintf(stderr, "target starting\n");
- usleep(1000);
- // load and unload in a loop
- for (int i=1; i < 10000; ++i) {
- void* h = dlopen("./libfoo.dylib", 0);
- usleep(100000/(i*100));
- dlclose(h);
- }
- //fprintf(stderr, "target done\n");
+ dispatch_async(dispatch_get_main_queue(), ^{
+ LOG("target starting");
+ usleep(1000);
+ // load and unload in a loop
+ for (int i=1; i < 10000; ++i) {
+ void* h = dlopen("./libfoo.dylib", 0);
+ usleep(100000/(i*100));
+ dlclose(h);
+ }
+ LOG("target done");
+ });
- return 0;
+ dispatch_main();
}
-// BUILD: mkdir -p $BUILD_DIR/override
-// BUILD: mkdir -p $BUILD_DIR/re-export-override
// BUILD: $CC myzlib.c -dynamiclib -o $BUILD_DIR/override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0
// BUILD: $CC main.c -o $BUILD_DIR/dyld_shared_cache_some_image_overridden.exe -lz
// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dyld_shared_cache_some_image_overridden.exe
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
+
// The test here is to override libz.1.dylib which is in the dyld cache with our own implementation.
// We then ensure that dyld_shared_cache_some_image_overridden returns the correct value to match whether we took a root
extern const char* zlibVersion();
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// If we aren't using a shared cache, eg, have DYLD_SHARED_REGION=avoid, then just assume we work
uuid_t currentCacheUUID;
if ( !_dyld_get_shared_cache_uuid(currentCacheUUID) ) {
- printf("[BEGIN] dyld_shared_cache_some_image_overridden\n");
if (dyld_shared_cache_some_image_overridden())
- printf("[FAIL] dyld_shared_cache_some_image_overridden\n");
+ FAIL("Overriden but no shared cache ");
else
- printf("[PASS] dyld_shared_cache_some_image_overridden\n");
- return 0;
+ PASS("No shared cache");
}
#if NO_LZ
- // This run doesn't link lz so instead dlopen's it
+ // This run doesn't link lz so instead dlopen's it
bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL);
- printf("[BEGIN] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os");
-
void* handle = dlopen("/usr/lib/libz.1.dylib", RTLD_NOLOAD);
if ( handle != NULL ) {
// Uh oh. Someone else has started linking libz so we can't use it as our root any more
- printf("[FAIL] dyld_shared_cache_some_image_overridden, libz is hard linked now. Update test to use a new dylib\n");
- return 0;
+ FAIL("libz is hard linked now. Update test to use a new dylib");
}
bool launchedWithOverriddenBinary = dyld_shared_cache_some_image_overridden();
// 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;
}
#include <string.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
+
extern struct mach_header __dso_handle;
-int main()
-{
- printf("[BEGIN] dyld_version_spi\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
dyld_platform_t active = dyld_get_active_platform();
dyld_platform_t base = dyld_get_base_platform(active);
dyld_build_version_t absoluteMin = { .platform = base, .version = 0 };
#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");
}
// BUILD_ONLY: MacOSX
// BUILD_MIN_OS: 10.5
-// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib -nostdlib -ldylib1.o
-// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -nostdlib -ldylib1.o
-// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR -nostdlib -lSystem -lcrt1.10.5.o
+// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib
// RUN: ./dylib-re-export.exe
#include <stdio.h>
+#include "test_support.h"
+
extern int bar();
-int main()
-{
- printf("[BEGIN] dylib-re-export-old-format\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
if ( bar() == 42 )
- printf("[PASS] dylib-re-export-old-format\n");
+ PASS("Success");
else
- printf("[FAIL] dylib-re-export-old-format, wrong value\n");
-
- return 0;
+ FAIL("Wrong value");
}
#include <stdio.h>
+#include "test_support.h"
+
extern int bar();
-int main()
-{
- printf("[BEGIN] dylib-re-export\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
if ( bar() == 42 )
- printf("[PASS] dylib-re-export\n");
+ PASS("Success");
else
- printf("[FAIL] dylib-re-export, wrong value\n");
+ FAIL("Wrong value");
return 0;
}
--- /dev/null
+
+
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
+// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-link.exe
+
+// RUN: ./dylib-static-link.exe
+
+
+#include <stdio.h>
+
+#include "test_support.h"
+
+extern int foo;
+
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ if ( foo == 42 )
+ PASS("Success");
+ else
+ FAIL("Wrong value");
+}
+
+
+++ /dev/null
-#include <stdio.h>
-#include <signal.h>
-
-int main()
-{
- printf("[BEGIN] dylib-static-link missing\n");
- printf("[FAIL] dylib-static-link missing, program should not have launched\n");
-
- return 0;
-}
-
-
+++ /dev/null
-
-
-// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
-// BUILD: $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-present.exe
-// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib
-// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-missing.exe
-
-// RUN: ./dylib-static-present.exe
-// RUN: NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe
-
-
-#include <stdio.h>
-
-extern int foo;
-
-
-int main()
-{
- printf("[BEGIN] dylib-static-link present\n");
- if ( foo == 42 )
- printf("[PASS] dylib-static-link present\n");
- else
- printf("[FAIL] dylib-static-link present, wrong value\n");
-
- return 0;
-}
-
-
-#include <stddef.h>
-#include <stdio.h>
+#include "test_support.h"
extern int foo __attribute__((weak_import));
-int main()
-{
- printf("[BEGIN] dylib-static-weak-link missing\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// dylib won't be found at runtime, so &foo should be NULL
if ( &foo == NULL )
- printf("[PASS] dylib-static-weak-link missing\n");
+ PASS("Success");
else
- printf("[FAIL] dylib-static-weak-link missing, &foo != NULL\n");
-
- return 0;
+ FAIL("&foo != NULL");
}
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
// BUILD: $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-weak-present.exe
-// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib
-// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib
+// BUILD: $CC missing.c $BUILD_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe
+
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libfoo2.dylib
// RUN: ./dylib-static-weak-present.exe
// RUN: ./dylib-static-weak-missing.exe
#include <stddef.h>
#include <stdio.h>
+#include "test_support.h"
+
extern int foo __attribute__((weak_import));
-int main()
-{
- printf("[BEGIN] dylib-static-weak-link present\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// dylib will be found at runtime, so &foo should never be NULL
if ( &foo != NULL ) {
if ( foo == 42 )
- printf("[PASS] dylib-static-weak-link present\n");
+ PASS("Success");
else
- printf("[FAIL] dylib-static-weak-link present, wrong value\n");
+ FAIL("Wrong value");
}
else {
- printf("[FAIL] dylib-static-weak-link present, &foo == NULL\n");
+ FAIL("&foo == NULL");
}
-
- return 0;
}
-// BUILD: mkdir -p $TEMP_DIR/Foo.framework $BUILD_DIR/FallbackFrameworks/Foo.framework
-// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=1
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=1
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/FallbackFrameworks/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=42
-// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FALLBACK_FRAMEWORK_PATH.exe $TEMP_DIR/Foo.framework/Foo
+// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FALLBACK_FRAMEWORK_PATH.exe $BUILD_DIR/Foo.framework/Foo
// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_FALLBACK_FRAMEWORK_PATH.exe
+// BUILD: $SKIP_INSTALL $BUILD_DIR/Foo.framework/Foo
+
// RUN: DYLD_FALLBACK_FRAMEWORK_PATH=$RUN_DIR/FallbackFrameworks/ ./env-DYLD_FALLBACK_FRAMEWORK_PATH.exe
#include <stdio.h>
#include <stdlib.h>
-extern int foo();
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
+extern int foo();
- if ( foo() == 42 )
- printf("[PASS] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ if ( foo() == 42 )
+ PASS("Success");
else
- printf("[FAIL] env-DYLD_FALLBACK_FRAMEWORK_PATH\n");
-
- return 0;
+ FAIL("foo() was not 42");
}
-// BUILD: mkdir -p $BUILD_DIR/fallback
-// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=1
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=1
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/fallback/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=42
-// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FALLBACK_LIBRARY_PATH.exe $TEMP_DIR/libfoo.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FALLBACK_LIBRARY_PATH.exe $BUILD_DIR/libfoo.dylib
// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_FALLBACK_LIBRARY_PATH.exe
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libfoo.dylib
+
// RUN: DYLD_FALLBACK_LIBRARY_PATH=$RUN_DIR/fallback/ ./env-DYLD_FALLBACK_LIBRARY_PATH.exe
#include <stdio.h>
-extern int foo();
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] env-DYLD_FALLBACK_LIBRARY_PATH\n");
+extern int foo();
- if ( foo() == 42 )
- printf("[PASS] env-DYLD_FALLBACK_LIBRARY_PATH\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ if ( foo() == 42 )
+ PASS("Success");
else
- printf("[FAIL] env-DYLD_FALLBACK_LIBRARY_PATH\n");
-
- return 0;
+ FAIL("libfoo.dylib incorrectly used fallback");
}
// BUILD_ONLY: MacOSX
-// BUILD: $CC main.c -o $BUILD_DIR/DYLD_FORCE_PLATFORM.exe -DENABLE_ALT_PLATFORMS=1 -ldarwintest
-// BUILD: $CC main.c -o $BUILD_DIR/DYLD_FORCE_PLATFORM_FAIL.exe -ldarwintest
-// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/DYLD_FORCE_PLATFORM.exe
-// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/DYLD_FORCE_PLATFORM_FAIL.exe
+// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FORCE_PLATFORM.exe -DENABLE_ALT_PLATFORMS=1
+// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FORCE_PLATFORM-fail.exe
+// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/env-DYLD_FORCE_PLATFORM.exe
+// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/env-DYLD_FORCE_PLATFORM-fail.exe
-// RUN: ./DYLD_FORCE_PLATFORM.exe
-// RUN: ./DYLD_FORCE_PLATFORM_FAIL.exe
+// RUN: DYLD_FORCE_PLATFORM=6 ./env-DYLD_FORCE_PLATFORM.exe
+// RUN: DYLD_FORCE_PLATFORM=6 ./env-DYLD_FORCE_PLATFORM-fail.exe
#include <mach-o/dyld_priv.h>
-#include "dyld_test.h"
+#include <dlfcn.h>
+
+#include "test_support.h"
#if ENABLE_ALT_PLATFORMS
__attribute__((section("__DATA,__allow_alt_plat"))) uint64_t dummy;
-T_DECL_DYLD(DYLD_FORCE_PLATFORM, "Test that DYLD_FORCE_PLATFORM works correctly", T_META_ENVVAR("DYLD_FORCE_PLATFORM=6")) {
- dyld_build_version_t ios12 = { .platform = PLATFORM_IOSMAC, .version = 0x000c0000 };
- T_EXPECT_EQ_UINT(dyld_get_active_platform(), PLATFORM_IOSMAC, "dyld_get_active_platform() should return PLATFORM_IOSMAC");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ dyld_build_version_t ios12 = { .platform = PLATFORM_IOS, .version = 0x000c0000 };
+ if (dyld_get_active_platform() != PLATFORM_MACCATALYST) { FAIL("dyld_get_active_platform() should return PLATFORM_MACCATALYST"); }
// libswiftUIKit.dylib exists in /System/iOSSupport/usr/lib/swift
// We should be able to dlopen it only if we are correctly prepending the /System/iOSSupport root path
- T_EXPECT_TRUE(dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib"), "Should be able to dlopen libswiftUIKit but %s", dlerror());
#if 0
- //FIXME: This has to be disabled until we can fix rdar://47156760
- T_EXPECT_TRUE(dyld_program_minos_at_least(ios12), "DYLD_FORCE_PLATFORM should synthesize an iOS min version greater than 12.0");
- T_EXPECT_TRUE(dyld_program_sdk_at_least(ios12), "DYLD_FORCE_PLATFORM should synthesize an iOS sdk versio greater than 12.0");
+ // FIXME: We don't want to bring in such large dylib graphs. We can repurpose root testing support for this
+ if (!dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib")) { FAIL("Should be able to dlopen libswiftUIKit but %s", dlerror()); }
#endif
+ if (!dyld_program_minos_at_least(ios12)) { FAIL("DYLD_FORCE_PLATFORM should synthesize an iOS min version greater than 12.0"); }
+ if (!dyld_program_sdk_at_least(ios12)) { FAIL("DYLD_FORCE_PLATFORM should synthesize an iOS sdk versio greater than 12.0"); }
+ PASS("Success");
}
#else
-T_DECL_DYLD(DYLD_FORCE_PLATFORM_FAIL, "Test that DYLD_FORCE_PLATFORM fails correctly", T_META_ENVVAR("DYLD_FORCE_PLATFORM=6")) {
- T_EXPECT_EQ_UINT(dyld_get_active_platform(), PLATFORM_MACOS, "dyld_get_active_platform() should return PLATFORM_IOSMAC");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ if (dyld_get_active_platform() != PLATFORM_MACOS) { FAIL("dyld_get_active_platform() should return PLATFORM_MACOS"); }
// libswiftUIKit.dylib exists in /System/iOSSupport/usr/lib/swift
// We should not be able to dlopen this as we don't expect to find it in a macOS location. If it starts
// being in a macOS location then we should update this test
- T_EXPECT_FALSE(dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib"), "Should not be able to dlopen libswiftUIKit");
+ if(dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib")) { FAIL("Should not be able to dlopen libswiftUIKit"); }
+ PASS("Success");
}
#endif
-// BUILD: mkdir -p $BUILD_DIR/Frameworks/Foo.framework $BUILD_DIR/Frameworks-alt/Foo.framework
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks/Foo.framework/Foo -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=1
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks-alt/Foo.framework/Foo -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=42
// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FRAMEWORK_PATH.exe $BUILD_DIR/Frameworks/Foo.framework/Foo
#include <stdio.h>
#include <stdlib.h>
+#include "test_support.h"
+
extern int foo();
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
int expected = (getenv("DYLD_FRAMEWORK_PATH") != NULL) ? 42 : 1;
- printf("[BEGIN] env-DYLD_FRAMEWORK_PATH, expect %d\n", expected);
-
if ( foo() == expected )
- printf("[PASS] env-DYLD_FRAMEWORK_PATH\n");
+ PASS("Success");
else
- printf("[FAIL] env-DYLD_FRAMEWORK_PATH\n");
-
- return 0;
+ FAIL("Incorrect libfoo.dylib loaded");
}
-// BUILD: mkdir -p $BUILD_DIR/Bar.framework
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=1
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo_other.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=42
// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/Bar.framework/Bar -install_name $RUN_DIR/Bar.framework/Bar -DVALUE=1
#include <string.h>
#include <dlfcn.h>
+#include "test_support.h"
+
extern int foo();
extern int bar();
typedef int (*IntProc)();
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
const char* suffix = getenv("DYLD_IMAGE_SUFFIX");
if ( suffix == NULL )
suffix = "";
- printf("[BEGIN] env-DYLD_IMAGE_SUFFIX-%s\n", suffix);
const int expectedFoo = (strstr(suffix, "_other") != NULL) ? 42 : 1;
const int expectedBar = (strstr(suffix, "_alt") != NULL) ? 42 : 1;;
#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)();
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");
}
-// BUILD: mkdir -p $BUILD_DIR/override
-// BUILD: mkdir -p $BUILD_DIR/re-export-override
// BUILD: $CC myzlib.c -dynamiclib -o $BUILD_DIR/override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -framework CoreFoundation
// BUILD: $CC reexported-myzlib.c -dynamiclib -o $BUILD_DIR/re-export-override/reexported.dylib -compatibility_version 1.0 -framework CoreFoundation -install_name $RUN_DIR/re-export-override/reexported.dylib
// BUILD: $CC reexporter.c -dynamiclib -o $BUILD_DIR/re-export-override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -Wl,-reexport_library,$BUILD_DIR/re-export-override/reexported.dylib -Wl,-debug_variant
#include <stdbool.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
+
// The test here is to override libz.1.dylib which is in the dyld cache with our own implementation.
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL) && !_dyld_shared_cache_optimized();
- printf("[BEGIN] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
-
bool usingMyDylib = (strcmp(zlibVersion(), "my") == 0);
if ( usingMyDylib == expectMyDylib )
- printf("[PASS] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
+ PASS("Succes");
else
- printf("[FAIL] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
-
- return 0;
+ FAIL("Expected %s, got %s", expectMyDylib ? "my" : "os", expectMyDylib ? "os" : "my");
}
-// BUILD: mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/door1/libfoo.dylib -DVALUE=1
// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door2/libfoo.dylib -install_name $RUN_DIR/door2/libfoo.dylib -DVALUE=42
// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_LIBRARY_PATH.exe $BUILD_DIR/door1/libfoo.dylib
#include <stdio.h>
#include <stdlib.h>
+#include "test_support.h"
+
extern int foo();
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
int expected = (getenv("DYLD_LIBRARY_PATH") != NULL) ? 42 : 1;
- printf("[BEGIN] env-DYLD_LIBRARY_PATH, expect %d\n", expected);
-
if ( foo() == expected )
- printf("[PASS] env-DYLD_LIBRARY_PATH\n");
+ PASS("Success");
else
- printf("[FAIL] env-DYLD_LIBRARY_PATH\n");
-
- return 0;
+ FAIL("Wrong dylib loaded");
}
// 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
// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH.exe $BUILD_DIR/Foo.framework/Foo
// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH-missing.exe -Wl,-dyld_env,DYLD_VERSIONED_FRAMEWORK_PATH=@loader_path/alt12 $BUILD_DIR/Foo2.framework/Foo2
-// BUILD: cd $BUILD_DIR/alt11/Foo.framework && ln -sf Versions/A/Foo
-// BUILD: rm -rf $BUILD_DIR/Foo2.framework
+// BUILD: $SYMLINK Versions/A/Foo $BUILD_DIR/alt11/Foo.framework/Foo $DEPENDS_ON $BUILD_DIR/alt11/Foo.framework/Versions/A/Foo
// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH.exe
// RUN: ./env-DYLD_VERSIONED_FRAMEWORK_PATH.exe 10
#include <string.h>
#include <stdlib.h> // for atoi()
+#include <mach-o/dyld_priv.h>
+
#include "test_support.h"
extern int foo();
int main(int argc, const char* argv[])
{
- if ( argc > 2 ) {
- bool found = false;
- uint32_t count = _dyld_image_count();
- for(uint32_t i=0; i < count; ++i) {
- const char* name = _dyld_get_image_name(i);
+ if ( argc > 2 ) {
+ bool found = false;
+ uint32_t count = _dyld_image_count();
+ for(uint32_t i=0; i < count; ++i) {
+ const char* name = _dyld_get_image_name(i);
if ( strstr(name, argv[2]) != NULL ) {
- found = true;
+ found = true;
}
- }
- if ( !found ) {
- FAIL("Dylib has wrong path");
- return EXIT_SUCCESS;
- }
- }
+ }
+ if ( !found ) {
+ FAIL("Dylib has wrong path");
+ return EXIT_SUCCESS;
+ }
+ }
- int expectedResult = atoi(argv[1]);
- int actualResult = foo();
+ int expectedResult = atoi(argv[1]);
+ int actualResult = foo();
if ( actualResult != expectedResult ) {
- FAIL("Using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
+ FAIL("Using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
} else {
- PASS("Success");
+ PASS("Success");
}
- return 0;
+ return 0;
}
// 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
// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe -DUSE_DLOPEN -DRUN_DIR="$RUN_DIR" -DDYLIB_NAME="libfoo.dylib"
// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dlopen.exe -DUSE_DLOPEN -DRUN_DIR="$RUN_DIR" -DDYLIB_NAME="libfoo2.dylib"
-// BUILD: rm -rf $BUILD_DIR/libfoo2.dylib
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libfoo2.dylib
// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH.exe
// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-11.exe
// FIXME: Forcibly disable testing with closures since macOS does not use them and they are currently broken
// RUN: DYLD_USE_CLOSURES=0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing.exe 12
-// RUN: DYLD_USE_CLOSURES=0 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 10
-// RUN: DYLD_USE_CLOSURES=0 DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt11 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 11
-// RUN: DYLD_USE_CLOSURES=0 DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt9 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 10
-// RUN: DYLD_USE_CLOSURES=0 DYLD_VERSIONED_LIBRARY_PATH="/AppleInternal/CoreOS/tests/dyld/env-DYLD_VERSIONED_LIBRARY_PATH/alt12" ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dlopen.exe 12
+// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 10
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt11 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 11
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt9 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 10
+// RUN: DYLD_VERSIONED_LIBRARY_PATH="/AppleInternal/CoreOS/tests/dyld/env-DYLD_VERSIONED_LIBRARY_PATH/alt12" ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dlopen.exe 12
#include <stdio.h> // fprintf(), NULL
#include <stdlib.h> // exit(), EXIT_SUCCESS
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) {
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;
}
+
#include <stdlib.h>
#include <string.h>
+#include "test_support.h"
+
extern int myAbs1;
int* ptr = &myAbs1;
-int main()
-{
- printf("[BEGIN] flat-namespace-absolute-symbol\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
if ( ptr != 0 ) {
- printf("[FAIL] absolute symbol not bound to zero with flat lookup\n");
- return 0;
+ FAIL("Absolute symbol not bound to zero with flat lookup");
}
- printf("[PASS] flat-namespace-absolute-symbol\n");
- return 0;
+ PASS("Success");
}
#include <stdlib.h>
#include <string.h>
-int main()
-{
- printf("[BEGIN] flat-namespace\n");
+#include "test_support.h"
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// check that the malloc in libfoo.dylib was used by looking at the content the allocated buffer
// <rdar://problem/31921090> strncmp is tricky for flat namespace because it is re-exporte and renamed
char* p1 = malloc(10);
if ( strncmp(p1, "##########", 10) != 0 ) {
- printf("[FAIL] malloc() from main executable not interposed\n");
- return 0;
+ FAIL("malloc() from main executable not interposed");
}
- printf("[PASS] flat-namespace\n");
- return 0;
+ PASS("Success");
}
#include <mach-o/dyld_images.h>
#include <uuid/uuid.h>
+#include "test_support.h"
+
extern const struct mach_header __dso_handle;
static void printUUIDs(const struct dyld_all_image_infos* infos)
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;
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");
}
#include <dlfcn.h>
#include <stdlib.h>
-extern bool foo(bool* ptr);
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] init-term-segments\n");
+extern bool foo(bool* ptr);
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
void* h = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOW);
if (h == NULL) {
- printf("[FAIL] init-term-segments - dlerror = %s\n", dlerror());
- return 0;
+ FAIL("dlerror = %s", dlerror());
}
void* fooSym = dlsym(RTLD_DEFAULT, "foo");
if ( fooSym == NULL ) {
- printf("[FAIL] init-term-segments - dlsym failure\n");
- return 0;
+ FAIL("dlsym failure");
}
bool ranTerm = false;
bool ranInit = ((__typeof(&foo))fooSym)(&ranTerm);
if (!ranInit) {
- printf("[FAIL] init-term-segments - didn't run init\n");
- return 0;
+ FAIL("didn't run init");
}
if ( dlclose(h) != 0 ) {
- printf("[FAIL] init-term-segments - didn't dlclose\n");
- return 0;
+ FAIL("didn't dlclose");
}
if (!ranTerm) {
- printf("[FAIL] init-term-segments - didn't run term\n");
- return 0;
+ FAIL("didn't run term");
}
- printf("[PASS] init-term-segments\n");
- return 0;
+ PASS("Success");
}
#include <stdlib.h>
#include <string.h>
+#include "test_support.h"
+
extern void* myalloc1(size_t);
extern void* myalloc2(size_t);
-int main()
-{
- printf("[BEGIN] interpose-malloc\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
char* p1 = malloc(10);
if ( strncmp(p1+10, "##########", 10) != 0 ) {
- printf("[FAIL] interpose-malloc malloc() from main executable not interposed\n");
- return 0;
+ FAIL("malloc() from main executable not interposed");
}
void* p2 = myalloc1(6);
if ( strncmp(p2+6, "######", 6) != 0 ) {
- printf("[FAIL] interpose-malloc myalloc1() from libfoo.dylib not interposed\n");
- return 0;
+ FAIL("myalloc1() from libfoo.dylib not interposed");
}
void* p3 = myalloc2(10);
if ( strncmp(p3+10, "##########", 10) != 0 ) {
- printf("[FAIL] interpose-malloc myalloc2() from libfoo.dylib not interposed\n");
- return 0;
+ FAIL("myalloc2() from libfoo.dylib not interposed");
}
void* p4 = strdup("hello");
if ( strncmp(p4+6, "#######", 6) != 0 ) {
- printf("[FAIL] interpose-malloc malloc() from strdup not interposed\n");
- return 0;
+ FAIL("malloc() from strdup not interposed");
}
- //printf("%p %p %p %p\n", p1, p2, p3, p4);
- printf("[PASS] interpose-malloc\n");
- return 0;
+ LOG("%p %p %p %p", p1, p2, p3, p4);
+ PASS("Success");
}
#include <stdio.h>
#include <stdlib.h>
+#include "test_support.h"
+
extern int foo();
int (*pFoo)() = &foo;
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
#if INTERPOSED
- printf("[BEGIN] interpose-resolver\n");
if ( foo() != 11 )
- printf("[FAIL] interpose-resolver: foo() != 11\n");
+ FAIL("foo() != 11");
else if ( (*pFoo)() != 11 )
- printf("[FAIL] interpose-resolver: *pFoo() != 11\n");
+ FAIL("*pFoo() != 11");
else
- printf("[PASS] interpose-resolver\n");
+ PASS("Success");
#else
- printf("[BEGIN] resolver-only\n");
if ( foo() != 10 )
- printf("[FAIL] resolver-only: foo() != 10\n");
+ FAIL(" foo() != 10");
else if ( (*pFoo)() != 10 )
- printf("[FAIL] resolver-only: *pFoo() != 10\n");
+ FAIL(" *pFoo() != 10");
else
- printf("[PASS] resolver-only\n");
+ PASS("Success");
#endif
- return 0;
}
// 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
#include <string.h>
#include <dlfcn.h>
+#include "test_support.h"
+
// Note, libinterposer.dylib interposes interposableFoo
extern int interposableFoo();
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");
}
// 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
#include <stdlib.h>
#include <string.h>
+#include "test_support.h"
extern int foo1();
extern int foo2();
#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");
}
#include <stdio.h>
+#include "test_support.h"
+
extern int foo1();
extern int foo2();
extern int foo3();
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");
}
// 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
#include <stdbool.h>
#include <mach-o/getsect.h>
+#include "test_support.h"
+
#if __LP64__
extern struct mach_header_64 __dso_handle;
#else
#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;
if ( argc < 0 )
slipperySymbol();
}
-
- printf("[PASS] " TESTNAME "\n");
-
- return 0;
+ PASS("%s", TESTNAME);
}
+++ /dev/null
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <mach/mach.h>
-#include <mach/machine.h>
-#include <err.h>
-#include <System/sys/reason.h>
-#include <System/sys/proc_info.h>
-#include <System/kern/kern_cdata.h>
-#include <libproc.h>
-#include <mach-o/dyld_priv.h>
-
-#if __arm64e__
- // arm64e uses chained binds which does not support lazy binding
- #define SUPPORTS_LAZY_BINDING 0
-#else
- #define SUPPORTS_LAZY_BINDING 1
-#endif
-
-
-static bool sSignalCaught = false;
-static bool sChildAbortInDyld = false;
-static pid_t sChildPid = 0;
-
-
-static void childDied(int sig)
-{
- sSignalCaught = true;
- //printf("sigchld for pid=%d\n", sChildPid);
-
- struct proc_exitreasoninfo info;
- bzero(&info, sizeof(info));
- uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE];
- bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE);
- info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
- info.eri_kcd_buf = (user_addr_t)packReasonData;
- //fprintf(stderr, "info=%p\n", &info);
- int procResult = proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE);
- if ( procResult != sizeof(struct proc_exitreasoninfo) ) {
- printf("bad return size from proc_pidinfo(), %d expected %lu\n", procResult, PROC_PIDEXITREASONINFO_SIZE);
- return;
- }
- if ( info.eri_namespace != OS_REASON_DYLD ) {
- printf("eri_namespace (%d) != OS_REASON_DYLD\n", info.eri_namespace);
- return;
- }
-
- sChildAbortInDyld = true;
-}
-
-
-bool runTest(const char* prog)
-{
- sSignalCaught = false;
- sChildAbortInDyld = false;
-
- // fork and exec child
- sChildPid = fork();
- if ( sChildPid < 0 )
- err(EXIT_FAILURE, "fork");
- if ( sChildPid == 0 ) {
- // child side
- char* childArgv[] = { (char*)prog, NULL };
- int result = execvp(prog, childArgv);
- err(EXIT_FAILURE, "exec(\"%s\",...)", prog);
- }
- for(int i=0; i < 10; ++i) {
- if ( sSignalCaught )
- break;
- sleep(1);
- }
-
- return sChildAbortInDyld;
-}
-
-
-int main(int argc, const char* argv[])
-{
- printf("[BEGIN] lazy-symbol-missing and called\n");
-
-#if SUPPORTS_LAZY_BINDING
- // set up signal handler for catching child terminations
- signal(SIGCHLD, childDied);
-
- // test launch program with missing library
- runTest(RUN_DIR "/lazy-symbol-missing-called.exe");
-
- if ( sSignalCaught && sChildAbortInDyld )
- printf("[PASS] lazy-symbol-missing and called\n");
- else
- printf("[FAIL] lazy-symbol-missing and called\n");
-#else
- printf("[PASS] lazy-symbol-missing and called\n");
-#endif
- return 0;
-}
-
--- /dev/null
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/machine.h>
+#include <err.h>
+#include <System/sys/reason.h>
+#include <System/sys/proc_info.h>
+#include <System/kern/kern_cdata.h>
+#include <libproc.h>
+#include <mach-o/dyld_priv.h>
+
+#if __arm64e__
+ // arm64e uses chained binds which does not support lazy binding
+ #define SUPPORTS_LAZY_BINDING 0
+#else
+ #define SUPPORTS_LAZY_BINDING 1
+#endif
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+#if SUPPORTS_LAZY_BINDING
+ _process process;
+ process.set_executable_path(RUN_DIR "/lazy-symbol-missing-called.exe");
+ const char* env[] = { "TEST_OUTPUT=None", NULL};
+ process.set_env(env);
+ process.set_exit_handler(^(pid_t pid) {
+ LOG("Child exited pid=%d", pid);
+
+ struct proc_exitreasoninfo info;
+ bzero(&info, sizeof(info));
+ uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE];
+ bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE);
+ info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
+ info.eri_kcd_buf = (user_addr_t)packReasonData;
+ LOG("info=%p", &info);
+ int procResult = proc_pidinfo(pid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE);
+ if ( procResult != sizeof(struct proc_exitreasoninfo) ) {
+ FAIL("bad return size from proc_pidinfo(), %d expected %lu", procResult, PROC_PIDEXITREASONINFO_SIZE);
+ }
+ if ( info.eri_namespace != OS_REASON_DYLD ) {
+ FAIL("eri_namespace (%d) != OS_REASON_DYLD", info.eri_namespace);
+ }
+ PASS("Success");
+ });
+ (void)process.launch();
+#endif
+ PASS("Success");
+}
+
+++ /dev/null
-// BUILD_ONLY: MacOSX
-
-// BUILD: $CC main.c -o $BUILD_DIR/rebuild-dyld-cache.exe
-
-// RUN: ./rebuild-dyld-cache.exe
-
-
-#include <stdio.h>
-#include <dlfcn.h>
-#include <unistd.h>
-#include <signal.h>
-#include <spawn.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-#include <sys/types.h>
-
-extern char** environ;
-
-int main()
-{
- printf("[BEGIN] macOS-cache-rebuild\n");
-
- const char* argv[] = { "/usr/bin/update_dyld_shared_cache", "-cache_dir", "/tmp/", NULL };
- pid_t child;
- int psResult = posix_spawn(&child, "/usr/bin/update_dyld_shared_cache", NULL, NULL, (char**)argv, environ);
- if ( psResult != 0 ) {
- printf("[FAIL] macOS-cache-rebuild: posix_spawn failed, err=%d\n", psResult);
- return 0;
- }
-
- int childStatus;
- (void)wait4(child, &childStatus, 0, NULL);
- if (WIFEXITED(childStatus) == 0)
- printf("[FAIL] macOS-cache-rebuild: update_dyld_shared_cache did not exit\n");
- else if (WEXITSTATUS(childStatus) != 0)
- printf("[FAIL] macOS-cache-rebuild: update_dyld_shared_cache failed\n");
- else
- printf("[PASS] macOS-cache-rebuild\n");
-
- return 0;
-}
-
--- /dev/null
+// BUILD_ONLY: MacOSX
+
+// BUILD: $CXX main.cpp -o $BUILD_DIR/rebuild-dyld-cache.exe
+
+// FIXME: This test will not make sense when remove update_dyld_shared_cache, and the functionality will be subsummed by root testing
+// ./rebuild-dyld-cache.exe
+
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+
+#include "test_support.h"
+
+extern char** environ;
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ _process process;
+ process.set_executable_path("/usr/bin/update_dyld_shared_cache");
+ const char* env[] = { "TEST_OUTPUT=None", NULL};
+ process.set_env(env);
+ const char* args[] = { "-cache_dir", "/tmp/", NULL };
+ process.set_args(args);
+ process.set_exit_handler(^(pid_t pid) {
+ int childStatus;
+ (void)wait4(pid, &childStatus, 0, NULL);
+ if (WIFEXITED(childStatus) == 0)
+ FAIL("update_dyld_shared_cache did not exit");
+ else if (WEXITSTATUS(childStatus) != 0)
+ FAIL("update_dyld_shared_cache failed");
+ else
+ PASS("Success");
+ });
+
+ process.launch();
+}
+
-// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $TEMP_DIR/libbar.dylib
+// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/missing/libbar.dylib
// BUILD: $CC bar-empty.c -dynamiclib -install_name $BUILD_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
-// BUILD: $CC main.c $TEMP_DIR/libbar.dylib -o $BUILD_DIR/missing-weak-def.exe
+// BUILD: $CC main.c $BUILD_DIR/missing/libbar.dylib -o $BUILD_DIR/missing-weak-def.exe
+
+// BUILD: $SKIP_INSTALL $BUILD_DIR/missing/libbar.dylib
// RUN: ./missing-weak-def.exe
#include <stdio.h>
#include <stdlib.h>
+#include "test_support.h"
+
__attribute__((weak))
__attribute__((weak_import))
int bar();
-int main()
-{
- printf("[BEGIN] missing-weak-def\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
if (&bar) {
- printf("[FAIL] missing-weak-def\n");
- return 0;
+ FAIL("bar from libbar not bound");
}
- printf("[PASS] missing-weak-def\n");
- return 0;
+ PASS("Success");
}
#include <mach-o/dyld_priv.h>
#include <dlfcn.h>
+#include "test_support.h"
// This program links with AppKit which in dyld3 mode stress tests building closures when there is no dyld shared cache
-int main()
-{
- printf("[BEGIN] no-shared-cache\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
size_t cacheLen;
const void* cacheStart = _dyld_get_shared_cache_range(&cacheLen);
if ( cacheStart != NULL ) {
- printf("[FAIL] no-shared-cache: _dyld_get_shared_cache_range() returned %p even though we are not using a dyld cache\n", cacheStart);
- return 0;
+ FAIL("_dyld_get_shared_cache_range() returned %p even though we are not using a dyld cache", cacheStart);
}
- printf("[PASS] no-shared-cache\n");
- return 0;
+ PASS("Success");
}
// RUN: ./operator-new.exe
-#include <stdio.h>
#include <new>
-
+#import "test_support.h"
//
// This test case verifies that calling operator new[] in libstdc++.dylib
::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");
}
#include <mach-o/dyld_priv.h>
#include <mach-o/getsect.h>
+#include "test_support.h"
+
extern bool isReadOnly(const void* addr);
extern bool testLib();
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;
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);
// 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");
}
// 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
#include <stdio.h>
#include <dlfcn.h>
+#include "test_support.h"
+
// Two ways to find libfoo.dylb: @rpath or DYLD_LIBRARY_PATH (set via LC_DYLD_ENVIRONMENT)
// These should work for non-restrictured programs.
// These should fail for restricted programs.
#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;
#include <stdio.h>
+#include "test_support.h"
+
extern int foo;
-int main()
-{
- printf("[BEGIN] rpath-absolute.exe\n");
- printf("[PASS] rpath-absolute.exe\n");
- return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ PASS("Success");
}
--- /dev/null
+int foo = 42;
--- /dev/null
+
+// BOOT_ARGS: dyld_flags=2
+
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/librel.dylib -install_name @rpath/librel.dylib
+// BUILD: $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-executable.exe -rpath @executable_path
+// BUILD: $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-executable-slash.exe -rpath @executable_path/
+// BUILD: $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-loader.exe -rpath @loader_path
+// BUILD: $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-loader-slash.exe -rpath @loader_path/
+
+// RUN: ./rpath-executable.exe
+// RUN: ./rpath-executable-slash.exe
+// RUN: ./rpath-loader.exe
+// RUN: ./rpath-loader-slash.exe
+
+// main prog links with librel.dylib. There are four variants of how LC_RPATH is set up.
+
+#include <stdio.h>
+
+#include "test_support.h"
+
+extern char* __progname;
+
+
+int main(int argc, const char* argv[])
+{
+ PASS("%s", __progname);
+}
+
+
// BOOT_ARGS: dyld_flags=2
-// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libmissing.dylib -install_name @rpath/libmissing.dylib
-// BUILD: $CC foo.c -dynamiclib -Wl,-weak_library,$TEMP_DIR/libmissing.dylib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -rpath $RUN_DIR
+// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name @rpath/libmissing.dylib
+// BUILD: $CC foo.c -dynamiclib -Wl,-weak_library,$BUILD_DIR/libmissing.dylib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -rpath $RUN_DIR
// BUILD: $CC main.c -o $BUILD_DIR/rpath-weak-missing.exe -DRUN_DIR="$RUN_DIR"
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libmissing.dylib
+
// RUN: ./rpath-weak-missing.exe
// RUN: DYLD_AMFI_FAKE=0 ./rpath-weak-missing.exe
#include <stdio.h>
#include <dlfcn.h>
+#include "test_support.h"
int main()
{
- printf("[BEGIN] rpath-weak-missing\n");
-
void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
if ( handle == NULL ) {
- printf("[FAIL] rpath-weak-missing dlopen(\"%s/libfoo.dylib\") - %s\n", RUN_DIR, dlerror());
- return 0;
+ FAIL("rpath-weak-missing dlopen(\"%s/libfoo.dylib\") - %s", RUN_DIR, dlerror());
}
- printf("[PASS] rpath-weak-missing\n");
- return 0;
+ PASS("rpath-weak-missing");
}
#include <ptrauth.h>
#endif
-
+#include "test_support.h"
struct dyld_cache_header
{
}
-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;
++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
++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
++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) {
++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");
}
#include <mach-o/dyld_priv.h>
#include <dlfcn.h>
+#include "test_support.h"
-int main()
-{
- printf("[BEGIN] shared_cache_optimized\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
// tests run on internal installs which use un-optimzed dyld cache
if ( _dyld_shared_cache_optimized() )
- printf("[FAIL] shared_cache_optimized unexpectedly returned true\n");
+ FAIL("unexpectedly returned true");
else
- printf("[PASS] shared_cache_optimized\n");
-
- return 0;
+ PASS("Success");
}
#include <ptrauth.h>
#endif
+#include "test_support.h"
+
static const void *stripPointer(const void *ptr) {
#if __has_feature(ptrauth_calls)
return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
}
-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");
}
#include <stddef.h>
-#include <stdio.h>
#include <stdbool.h>
+#include "test_support.h"
+
static bool mainCalled = false;
static bool libCalled = false;
static bool libCalledBeforeMain = false;
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");
}
#include <stddef.h>
#include <stdio.h>
+#include "test_support.h"
extern void libDynamicTerminated();
static __attribute__((destructor))
void myTerm()
{
- //fprintf(stderr, "foo static terminator\n");
+ LOG("foo static terminator");
libDynamicTerminated();
}
#include <dlfcn.h>
#include <mach-o/dyld_priv.h>
+#include "test_support.h"
// verify all static terminators run in proper order
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;
}
#include <stdio.h>
#include <stdlib.h>
+#include "test_support.h"
+
extern int foo();
extern int fooPlusOne();
-int main()
-{
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
#if TEN
if ( foo() != 10 )
- printf("[FAIL] symbol-resolver-basic: foo() != 10\n");
+ FAIL("foo() != 10");
else if ( fooPlusOne() != 11 )
- printf("[FAIL] symbol-resolver-basic: fooPlusOne() != 11\n");
+ FAIL("fooPlusOne() != 11");
else
- printf("[PASS] symbol-resolver-basic\n");
+ PASS("Success");
#else
if ( foo() != 0 )
- printf("[FAIL] symbol-resolver-basic: foo() != 0\n");
+ FAIL("foo() != 0");
else if ( fooPlusOne() != 1 )
- printf("[FAIL] symbol-resolver-basic: fooPlusOne() != 1\n");
+ FAIL("fooPlusOne() != 1");
else
- printf("[PASS] symbol-resolver-basic\n");
+ PASS("Success");
#endif
return 0;
#include <stdlib.h>
#include <pthread.h>
+#include "test_support.h"
+
// We create an A and a B.
// While destroying B we create a C
// Given that tlv_finalize has "destroy in reverse order of construction", we
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;
}
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;
}
// 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;
}
// 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;
}
// 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()
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;
}
#include <stdlib.h>
#include <pthread.h>
+#include "test_support.h"
+
// We create an A and a B.
// While destroying B we create a C
// Given that tlv_finalize has "destroy in reverse order of construction", we
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;
}
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;
}
// 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;
}
// 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;
}
// 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)
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;
#include <stdio.h>
#include <dlfcn.h>
+#include "test_support.h"
-
-
-
-int main()
-{
- printf("[BEGIN] thread-local-cleanup\n");
-
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
for (int i=0; i < 1000; ++i) {
void* handle = dlopen(RUN_DIR "/libtlv.dylib", RTLD_FIRST);
if ( handle == NULL ) {
- printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror());
- return 0;
+ FAIL("dlopen error: iteration %d %s", i, dlerror());
}
int result = dlclose(handle);
if ( result != 0 ) {
- printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror());
- return 0;
+ FAIL("dlclose error: iteration %d %s", i, dlerror());
}
}
-
- printf("[PASS] thread-local-cleanup\n");
- return 0;
+ PASS("Success");
}
#include <TargetConditionals.h>
+#include "test_support.h"
+
static pthread_t sMainThread;
static pthread_t sWorker1;
static pthread_t sWorker2;
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);
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();
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;
// 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;
#include <assert.h>
#include <unistd.h>
+#include "test_support.h"
extern __thread int a;
extern __thread int b;
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);
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);
// 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");
}
// BUILD_ONLY: MacOSX
// BUILD: $CC main.c -o $BUILD_DIR/unix-conformance.exe -D_XOPEN_SOURCE=600
-// BUILD: $CC main.c -o $TEMP_DIR/scratch.exe -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112
+// BUILD: $CC main.c -o $BUILD_DIR/scratch.exe -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112
+
+// BUILD: $SKIP_INSTALL $BUILD_DIR/scratch.exe
// RUN: ./unix-conformance.exe
#include <string.h>
#include <dlfcn.h>
-int main()
-{
- printf("[BEGIN] unix-conformance.dtest\n");
+#include "test_support.h"
- printf("[PASS] unix-conformance.dtest\n");
- return 0;
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ PASS("Success");
}
#include <stdio.h>
#include <stdlib.h>
+#include "test_support.h"
+
extern void foo();
// We have our own copy of operator new. Make sure that we call our version, but that foo calls the one from the inserted bar dylib
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
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");
}
#include <stdlib.h>
#include <dlfcn.h>
+#include "test_support.h"
+
extern int foo();
extern void* fooPtr();
int main()
{
- printf("[BEGIN] weak-coalesce-unload\n");
-
// dlopen foo1 which defines "foo"
void* handle1 = dlopen(RUN_DIR "/libfoo1.dylib", RTLD_FIRST);
if ( handle1 == NULL ) {
- printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo1.dylib", dlerror());
- return 0;
+ FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo1.dylib", dlerror());
}
const void* symFoo1 = dlsym(handle1, "foo");
if ( symFoo1 == NULL ) {
- printf("[FAIL] dlsym(handle1, foo) failed\n");
- return 0;
+ FAIL("dlsym(handle1, foo) failed");
}
const void* symFooPtr1 = dlsym(handle1, "fooPtr");
if ( symFooPtr1 == NULL ) {
- printf("[FAIL] dlsym(handle1, fooPtr) failed\n");
- return 0;
+ FAIL("dlsym(handle1, fooPtr) failed");
}
void* fooptr1 = ((__typeof(&fooPtr))symFooPtr1)();
int close1 = dlclose(handle1);
if ( close1 != 0 ) {
- printf("[FAIL] dlclose(handle1) failed with: %s\n", dlerror());
- return 0;
+ FAIL("dlclose(handle1) failed with: %s", dlerror());
}
// Now dlopen foo2 and get the value it finds for foo
void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST);
if ( handle2 == NULL ) {
- printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo2.dylib", dlerror());
- return 0;
+ FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo2.dylib", dlerror());
}
const void* symFoo2 = dlsym(handle2, "foo");
if ( symFoo2 == NULL ) {
- printf("[FAIL] dlsym(handle2, foo) failed\n");
- return 0;
+ FAIL("dlsym(handle2, foo) failed");
}
const void* symFooPtr2 = dlsym(handle2, "fooPtr");
if ( symFooPtr2 == NULL ) {
- printf("[FAIL] dlsym(handle2, fooPtr) failed\n");
- return 0;
+ FAIL("dlsym(handle2, fooPtr) failed");
}
void* fooptr2 = ((__typeof(&fooPtr))symFooPtr2)();
// Don't close foo2, but instead open foo3
void* handle3 = dlopen(RUN_DIR "/libfoo3.dylib", RTLD_FIRST);
if ( handle3 == NULL ) {
- printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo3.dylib", dlerror());
- return 0;
+ FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo3.dylib", dlerror());
}
const void* symFoo3 = dlsym(handle3, "foo");
if ( symFoo3 == NULL ) {
- printf("[FAIL] dlsym(handle3, foo) failed\n");
- return 0;
+ FAIL("dlsym(handle3, foo) failed");
}
const void* symFooPtr3 = dlsym(handle3, "fooPtr");
if ( symFooPtr3 == NULL ) {
- printf("[FAIL] dlsym(handle3, fooPtr) failed\n");
- return 0;
+ FAIL("dlsym(handle3, fooPtr) failed");
}
void* fooptr3 = ((__typeof(&fooPtr))symFooPtr3)();
// No-one should point to libfoo1.dylib
if ( symFoo1 == symFoo2 ) {
- printf("[FAIL] foo1 == foo2\n");
- return 0;
+ FAIL("foo1 == foo2");
}
if ( symFoo1 == symFoo3 ) {
- printf("[FAIL] foo1 == foo3\n");
- return 0;
+ FAIL("foo1 == foo3");
}
// foo2 and foo3 should be different
if ( symFoo2 == symFoo3 ) {
- printf("[FAIL] foo2 != foo3\n");
- return 0;
+ FAIL("foo2 != foo3");
}
// But their coalesced values should be the same
if ( fooptr1 == fooptr2 ) {
- printf("[FAIL] fooptr1 == fooptr2\n");
- return 0;
+ FAIL("fooptr1 == fooptr2");
}
if ( fooptr2 != fooptr3 ) {
- printf("[FAIL] fooptr2 != fooptr3\n");
- return 0;
+ FAIL("fooptr2 != fooptr3");
}
// foo should return the value from foo2, not the value from foo3
// Also calling this would explode if we somehow pointed at foo1
if ( ((__typeof(&foo))fooptr2)() != 2 ) {
- printf("[FAIL] foo2 != 2\n");
- return 0;
+ FAIL("foo2 != 2");
}
if ( ((__typeof(&foo))fooptr3)() != 2 ) {
- printf("[FAIL] foo3 != 2\n");
- return 0;
+ FAIL("foo3 != 2");
}
- printf("[PASS] weak-coalesce-unload\n");
- return 0;
+ PASS("weak-coalesce-unload");
}
+++ /dev/null
-##
-# 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
-
#include <stdio.h>
#include <stdbool.h>
+#include "test_support.h"
#include "base.h"
static bool wasProblem = false;
void baseVerifyCoal1(const char* where, int* addr)
{
- //fprintf(stderr, "baseVerifyCoal1(%s, %p)\n", where, addr);
- ++checkInCountCoal1;
- if ( coal1Where == NULL ) {
- coal1Where = where;
- coal1Addr = addr;
- }
- else {
- if ( addr != coal1Addr ) {
- fprintf(stderr, "coal1 resolved to different locations. %p in %s and %p in %s\n",
- coal1Addr, coal1Where, addr, where);
- wasProblem = true;
- }
- }
+ LOG("baseVerifyCoal1(%s, %p)", where, addr);
+ ++checkInCountCoal1;
+ if ( coal1Where == NULL ) {
+ coal1Where = where;
+ coal1Addr = addr;
+ }
+ else {
+ if ( addr != coal1Addr ) {
+ LOG("coal1 resolved to different locations. %p in %s and %p in %s",
+ coal1Addr, coal1Where, addr, where);
+ wasProblem = true;
+ }
+ }
}
void baseVerifyCoal2(const char* where, int* addr)
{
- //fprintf(stderr, "baseVerifyCoal2(%s, %p)\n", where, addr);
- ++checkInCountCoal2;
- if ( coal2Where == NULL ) {
- coal2Where = where;
- coal2Addr = addr;
- }
- else {
- if ( addr != coal2Addr ) {
- fprintf(stderr, "coal2 resolved to different locations. %p in %s and %p in %s\n",
- coal2Addr, coal2Where, addr, where);
- wasProblem = true;
- }
- }
+ LOG("baseVerifyCoal2(%s, %p)", where, addr);
+ ++checkInCountCoal2;
+ if ( coal2Where == NULL ) {
+ coal2Where = where;
+ coal2Addr = addr;
+ }
+ else {
+ if ( addr != coal2Addr ) {
+ LOG("coal2 resolved to different locations. %p in %s and %p in %s",
+ coal2Addr, coal2Where, addr, where);
+ wasProblem = true;
+ }
+ }
}
void baseCheck()
{
- if ( wasProblem )
- printf("[FAIL] weak-coalesce: was problem\n");
+ if ( wasProblem )
+ FAIL("was problem");
else if ( checkInCountCoal1 != 4 )
- printf("[FAIL] weak-coalesce: checkInCountCoal1 != 4\n");
+ FAIL("checkInCountCoal1 != 4");
else if ( checkInCountCoal2 != 4 )
- printf("[FAIL] weak-coalesce: checkInCountCoal2 != 2\n");
- else
- printf("[PASS] weak-coalesce\n");
+ FAIL("checkInCountCoal2 != 2");
+ else
+ PASS("Success");
}
* @APPLE_LICENSE_HEADER_END@
*/
#include <stdio.h>
+
+#include "test_support.h"
+
#include "base.h"
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);
}
#include <stdio.h>
+#include "test_support.h"
+
#include "base.h"
int coal1 = 2; // note: this is not weak and therefore should win
int __attribute__((weak)) coal2 = 2;
static __attribute__((constructor))
-void myinit()
+void myinit(int argc, const char* argv[], const char* envp[], const char* apple[])
{
- //fprintf(stderr, "myinit() in foo1.c\n");
- baseVerifyCoal1("in foo2", &coal1);
- baseVerifyCoal2("in foo2", &coal2);
+ LOG("myinit() in foo1.c");
+ baseVerifyCoal1("in foo2", &coal1);
+ baseVerifyCoal2("in foo2", &coal2);
}
-#include <stdio.h>
+#include "test_support.h"
#include "base.h"
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);
}
#include <stdio.h>
#include <stdlib.h>
+#include "test_support.h"
#include "base.h"
-int main()
-{
- printf("[BEGIN] weak-coalesce\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ baseVerifyCoal1("in main", &coal1);
+ baseVerifyCoal2("in main", &coal2);
- baseVerifyCoal1("in main", &coal1);
- baseVerifyCoal2("in main", &coal2);
-
- baseCheck();
- return 0;
+ baseCheck();
}
--- /dev/null
+
+__attribute__((weak))
+int weakTestValue = 42;
+
+int bar() {
+ return weakTestValue;
+}
\ No newline at end of file
--- /dev/null
+
+__attribute__((weak))
+int weakTestValue = 1;
+
+int foo() {
+ return weakTestValue;
+}
\ No newline at end of file
--- /dev/null
+// BUILD_ONLY: MacOSX
+// BUILD_MIN_OS: 10.5
+// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC main.c -o $BUILD_DIR/weak-def-bind-old-format.exe $BUILD_DIR/libfoo.dylib $BUILD_DIR/libbar.dylib -L$BUILD_DIR
+
+// RUN: ./weak-def-bind-old-format.exe
+
+
+#include <stdio.h>
+
+#include "test_support.h"
+
+extern int foo();
+extern int bar();
+
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+ if ( foo() != 42 ) {
+ FAIL("weak-def-bind-old-format, wrong value");
+ }
+ if ( bar() != 42 ) {
+ FAIL("weak-def-bind-old-format, wrong value");
+ }
+
+ PASS("weak-def-bind-old-format");
+}
+
+
-// BUILD: $CC bar.c -dynamiclib -o $TEMP_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib
-// BUILD: $CC foo.c -dynamiclib -L$TEMP_DIR -weak-lbar -Wl,-reexported_symbols_list,symbols.txt -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib
+// BUILD: $CC foo.c -dynamiclib -L$BUILD_DIR -weak-lbar -Wl,-reexported_symbols_list,$SRC_DIR/symbols.txt -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libbar.dylib
+
// RUN: ./dylib-re-export.exe
#include <stdio.h>
+#include "test_support.h"
+
__attribute__((weak_import))
extern int bar();
-int main()
-{
- printf("[BEGIN] dylib-re-export\n");
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
if ( &bar == 0 )
- printf("[PASS] dylib-re-export\n");
+ PASS("SUCCESS");
else
- printf("[FAIL] dylib-re-export, wrong value\n");
+ FAIL("wrong value");
return 0;
}