]>
git.saurik.com Git - apple/dyld.git/blob - chroot_util.cpp
2 * Copyright (c) 2019 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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
22 * @APPLE_LICENSE_HEADER_END@
36 #include <sys/param.h>
37 #include <mach-o/fat.h>
38 #include <mach-o/loader.h>
47 #include "StringUtils.h"
48 #include "MachOFile.h"
50 std::set
<std::string
> scanForDependencies(const std::string
& path
) {
51 __block
std::set
<std::string
> result
;
53 int fd
= open(path
.c_str(), O_RDONLY
, 0);
58 if (fstat(fd
, &stat_buf
) == -1) {
63 const void* buffer
= mmap(NULL
, (size_t)stat_buf
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
64 if (buffer
== MAP_FAILED
) {
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
);
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
);
82 const dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)buffer
;
83 if (mf
->isMachO(diag
, stat_buf
.st_size
)) {
84 mf
->forEachDependentDylib(scanner
);
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());
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; }
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
] != '/') {
111 size_t frameworkNameBegin
= pos
;
112 while (path
[frameworkNameBegin
-1] != '/') {
113 --frameworkNameBegin
;
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
);
125 if (!std::filesystem::exists(mainLinkPath
)) {
126 std::filesystem::create_symlink(mainLinkTarget
, mainLinkPath
);
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
);
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
);
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
);
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();
153 auto filePath
= file
->path().string();
154 if (filePath
== devfsPath
) {
155 file
.disable_recursion_pending();
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;
171 add_symlink("libSystem.B.dylib", chroot
+ "/usr/lib/libSystem.dylib");
172 add_symlink("libSystem.dylib", chroot
+ "/usr/lib/libc.dylib");
175 int main(int argc
, const char * argv
[]) {
176 std::vector
<std::string
> binaries
;
177 std::vector
<std::string
> overlays
;
178 std::string fallback
;
180 for (int i
= 1; i
< argc
; ++i
) {
181 const char* arg
= argv
[i
];
183 if (strcmp(arg
, "-chroot") == 0) {
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
]);
190 fprintf(stderr
, "unknown option: %s\n", arg
);
194 fprintf(stderr
, "unknown option: %s\n", arg
);
199 if (chroot
.length() == 0) {
200 fprintf(stderr
, "No -chroot <dir>\n");
203 if (fallback
.length() == 0) {
204 fprintf(stderr
, "No -fallback <dir>\n");
207 buildChroot(chroot
, fallback
, binaries
);
208 // insert code here...