dyld-750.5.tar.gz
[apple/dyld.git] / chroot_util.cpp
1 /*
2 * Copyright (c) 2019 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <cassert>
26 #include <cstdio>
27 #include <cstring>
28
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <libgen.h>
32 #include <unistd.h>
33 #include <sys/attr.h>
34 #include <sys/mman.h>
35 #include <sys/stat.h>
36 #include <sys/param.h>
37 #include <mach-o/fat.h>
38 #include <mach-o/loader.h>
39 #include <copyfile.h>
40
41 #include <set>
42 #include <string>
43 #include <vector>
44 #include <functional>
45 #include <filesystem>
46
47 #include "StringUtils.h"
48 #include "MachOFile.h"
49
50 std::set<std::string> scanForDependencies(const std::string& path) {
51 __block std::set<std::string> result;
52 struct stat stat_buf;
53 int fd = open(path.c_str(), O_RDONLY, 0);
54 if (fd == -1) {
55 return result;
56 }
57
58 if (fstat(fd, &stat_buf) == -1) {
59 close(fd);
60 return result;
61 }
62
63 const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
64 if (buffer == MAP_FAILED) {
65 close(fd);
66 return result;
67 }
68
69 auto scanner = ^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
70 if (isWeak) { return; } // We explicily avoid LC_LOAD_WEAK_DYLIB since we are trying to build a minimal chroot
71 if (loadPath[0] != '/') { return; } // Only include absolute dependencies
72 result.insert(loadPath);
73 };
74 Diagnostics diag;
75 if ( dyld3::FatFile::isFatFile(buffer) ) {
76 const dyld3::FatFile* ff = (dyld3::FatFile*)buffer;
77 ff->forEachSlice(diag, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
78 const dyld3::MachOFile* mf = (dyld3::MachOFile*)sliceStart;
79 mf->forEachDependentDylib(scanner);
80 });
81 } else {
82 const dyld3::MachOFile* mf = (dyld3::MachOFile*)buffer;
83 if (mf->isMachO(diag, stat_buf.st_size)) {
84 mf->forEachDependentDylib(scanner);
85 }
86 }
87 close(fd);
88 return result;
89 }
90
91 std::string withoutPrefixPath(const std::string& path, const std::string& prefix ) {
92 std::string result = path;
93 size_t pos = result.find(prefix);
94 result.erase(pos, prefix.length());
95 return result;
96 }
97
98 void add_symlinks_to_dylib(const std::string path) {
99 static std::set<std::string> alreadyMatched;
100 size_t pos = path.rfind(".framework/Versions/");
101 auto prefixPath = path.substr(0, pos);
102 if (alreadyMatched.find(prefixPath) != alreadyMatched.end()) { return; }
103
104 if (pos == std::string::npos) { return; }
105 // fprintf(stderr, "PATH: %s\n", path.c_str());
106 size_t versionStart = pos+20;
107 size_t versionEnd = versionStart;
108 while (path[versionEnd] != '/') {
109 ++versionEnd;
110 }
111 size_t frameworkNameBegin = pos;
112 while (path[frameworkNameBegin-1] != '/') {
113 --frameworkNameBegin;
114 }
115 auto frameworkName = path.substr(frameworkNameBegin, pos-frameworkNameBegin);
116 auto version = path.substr(versionStart, versionEnd-versionStart);
117 std::string mainLinkPath = prefixPath + ".framework/" + frameworkName;
118 std::string mainLinkTarget = "Versions/Current/" + frameworkName;
119 std::string versionLinkPath = prefixPath + ".framework/Versions/Current";
120 std::string versionLinkTarget = version;;
121 alreadyMatched.insert(prefixPath);
122 if (!std::filesystem::exists(versionLinkPath)) {
123 std::filesystem::create_symlink(version, versionLinkPath);
124 }
125 if (!std::filesystem::exists(mainLinkPath)) {
126 std::filesystem::create_symlink(mainLinkTarget, mainLinkPath);
127 }
128 }
129
130 void add_symlink(const std::string& target, const std::string& path) {
131 if (!std::filesystem::exists(path)) {
132 std::filesystem::create_symlink(target, path);
133 }
134 }
135
136 void buildChroot(const std::string& chroot, const std::string& fallback, const std::vector<std::string>& binaries) {
137 auto chrootPath = std::filesystem::path(chroot);
138 auto fallbackPath = std::filesystem::path(fallback);
139
140 for (const auto& binary : binaries) {
141 if (std::filesystem::exists(chroot + binary)) { continue; }
142 std::filesystem::create_directories(std::filesystem::path(chroot + binary).parent_path());
143 std::filesystem::copy(fallback + binary, chroot + binary);
144 }
145 bool foundNewEntries = true;
146 std::set<std::string> scannedFiles;
147 std::string devfsPath = chroot + "/dev";
148 while (foundNewEntries) {
149 foundNewEntries = false;
150 for(auto file = std::filesystem::recursive_directory_iterator(chroot);
151 file != std::filesystem::recursive_directory_iterator();
152 ++file ) {
153 auto filePath = file->path().string();
154 if (filePath == devfsPath) {
155 file.disable_recursion_pending();
156 continue;
157 }
158 if (scannedFiles.find(filePath) != scannedFiles.end()) { continue; }
159 scannedFiles.insert(filePath);
160 auto candidates = scanForDependencies(filePath);
161 for (const auto& candidate : candidates) {
162 if (std::filesystem::exists(chroot + candidate)) { continue; }
163 if (!std::filesystem::exists(fallback + candidate)) { continue; }
164 std::filesystem::create_directories(std::filesystem::path(chroot + candidate).parent_path());
165 std::filesystem::copy(fallback + candidate, chroot + candidate);
166 add_symlinks_to_dylib(chroot + candidate);
167 foundNewEntries = true;
168 }
169 }
170 }
171 add_symlink("libSystem.B.dylib", chroot + "/usr/lib/libSystem.dylib");
172 add_symlink("libSystem.dylib", chroot + "/usr/lib/libc.dylib");
173 }
174
175 int main(int argc, const char * argv[]) {
176 std::vector<std::string> binaries;
177 std::vector<std::string> overlays;
178 std::string fallback;
179 std::string chroot;
180 for (int i = 1; i < argc; ++i) {
181 const char* arg = argv[i];
182 if (arg[0] == '-') {
183 if (strcmp(arg, "-chroot") == 0) {
184 chroot = argv[++i];
185 } else if (strcmp(arg, "-fallback") == 0) {
186 fallback = argv[++i];
187 } else if (strcmp(arg, "-add_file") == 0) {
188 binaries.push_back(argv[++i]);
189 } else {
190 fprintf(stderr, "unknown option: %s\n", arg);
191 exit(-1);
192 }
193 } else {
194 fprintf(stderr, "unknown option: %s\n", arg);
195 exit(-1);
196 }
197 }
198
199 if (chroot.length() == 0) {
200 fprintf(stderr, "No -chroot <dir>\n");
201 exit(-1);
202 }
203 if (fallback.length() == 0) {
204 fprintf(stderr, "No -fallback <dir>\n");
205 exit(-1);
206 }
207 buildChroot(chroot, fallback, binaries);
208 // insert code here...
209 return 0;
210 }