1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 * Copyright (c) 2014 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
26 #include "mega-dylib-utils.h"
27 #include "MachOFileAbstraction.hpp"
31 #include <sys/errno.h>
32 #include <sys/fcntl.h>
33 #include <sys/param.h>
34 #include <mach-o/loader.h>
35 #include <mach-o/fat.h>
36 #include <mach-o/dyld.h>
38 #include <Availability.h>
45 #include <unordered_map>
46 #include <unordered_set>
48 #include <System/sys/csr.h>
50 #include "dyld_cache_config.h"
52 #include "OptimizerBranches.h"
54 #include "CacheFileAbstraction.hpp"
56 #include "mega-dylib-utils.h"
60 //#include <rootless.h>
61 extern "C" int rootless_check_trusted(const char *path
);
62 extern "C" int rootless_check_trusted_fd(int fd
) __attribute__((weak_import
));
64 static bool rootlessEnabled
;
65 static dispatch_once_t onceToken
;
67 bool isProtectedBySIP(const std::string
& path
, int fd
)
69 bool isProtected
= false;
70 // Check to make sure file system protections are on at all
71 dispatch_once(&onceToken
, ^{
72 rootlessEnabled
= csr_check(CSR_ALLOW_UNRESTRICTED_FS
);
76 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
77 if ( (fd
!= -1) && (&rootless_check_trusted_fd
!= NULL
) )
78 isProtected
= (rootless_check_trusted_fd(fd
) == 0);
81 if ( &rootless_check_trusted
!= NULL
)
82 isProtected
= (rootless_check_trusted(path
.c_str()) == 0);
89 char buffer
[PATH_MAX
];
90 uint32_t bufsize
= PATH_MAX
;
91 int result
= _NSGetExecutablePath(buffer
, &bufsize
);
93 std::string path
= buffer
;
94 size_t pos
= path
.rfind('/');
95 if ( pos
!= std::string::npos
)
96 return path
.substr(0,pos
+1);
98 warning("tool directory not found");
102 std::string
baspath(const std::string
& path
)
104 std::string::size_type slash_pos
= path
.rfind("/");
105 if (slash_pos
!= std::string::npos
) {
107 return path
.substr(slash_pos
);
113 std::string
dirpath(const std::string
& path
)
115 std::string::size_type slash_pos
= path
.rfind("/");
116 if (slash_pos
!= std::string::npos
) {
118 return path
.substr(0, slash_pos
);
120 char cwd
[MAXPATHLEN
];
121 (void)getcwd(cwd
, MAXPATHLEN
);
126 std::string
normalize_absolute_file_path(const std::string
&path
) {
127 std::vector
<std::string
> components
;
128 std::vector
<std::string
> processed_components
;
129 std::stringstream
ss(path
);
133 while (std::getline(ss
, item
, '/')) {
134 components
.push_back(item
);
137 if (components
[0] == ".") {
141 for (auto& component
: components
) {
142 if (component
.empty() || component
== ".")
144 else if (component
== ".." && processed_components
.size())
145 processed_components
.pop_back();
147 processed_components
.push_back(component
);
150 for (auto & component
: processed_components
) {
151 retval
= retval
+ "/" + component
;
159 FileCache::FileCache(void) {
160 cache_queue
= dispatch_queue_create("com.apple.dyld.cache.cache", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL
, QOS_CLASS_USER_INITIATED
, 0));
164 void FileCache::preflightCache(const std::unordered_set
<std::string
>& paths
) {
165 for (auto &path
: paths
) {
166 preflightCache(path
);
170 void FileCache::preflightCache(const std::string
& path
)
172 cacheBuilderDispatchAsync(cache_queue
, [=] {
173 std::string normalizedPath
= normalize_absolute_file_path(path
);
174 if (entries
.count(normalizedPath
) == 0) {
175 entries
[normalizedPath
] = fill(normalizedPath
);
180 std::tuple
<uint8_t *, struct stat
, bool> FileCache::cacheLoad(const std::string path
) {
182 std::tuple
<uint8_t*, struct stat
, bool> retval
;
183 std::string normalizedPath
= normalize_absolute_file_path(path
);
184 cacheBuilderDispatchSync(cache_queue
, [=, &found
, &retval
] {
185 auto entry
= entries
.find(normalizedPath
);
186 if (entry
!= entries
.end()) {
187 retval
= entry
->second
;
193 auto info
= fill(normalizedPath
);
194 cacheBuilderDispatchSync(cache_queue
, [=, &found
, &retval
] {
195 auto entry
= entries
.find(normalizedPath
);
196 if (entry
!= entries
.end()) {
197 retval
= entry
->second
;
199 retval
= entries
[normalizedPath
] = info
;
208 //FIXME error handling
209 std::tuple
<uint8_t*, struct stat
, bool> FileCache::fill(const std::string
& path
) {
210 struct stat stat_buf
;
212 int fd
= ::open(path
.c_str(), O_RDONLY
, 0);
214 verboseLog("can't open file '%s', errno=%d", path
.c_str(), errno
);
215 return std::make_tuple((uint8_t*)(-1), stat_buf
, false);
218 if ( fstat(fd
, &stat_buf
) == -1) {
219 verboseLog("can't stat open file '%s', errno=%d", path
.c_str(), errno
);
221 return std::make_tuple((uint8_t*)(-1), stat_buf
, false);
224 if ( stat_buf
.st_size
< 4096 ) {
225 verboseLog("file too small '%s'", path
.c_str());
227 return std::make_tuple((uint8_t*)(-1), stat_buf
, false);
230 bool rootlessProtected
= isProtectedBySIP(path
, fd
);
232 void* buffer_ptr
= mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
233 if (buffer_ptr
== MAP_FAILED
) {
234 verboseLog("mmap() for file at %s failed, errno=%d", path
.c_str(), errno
);
236 return std::make_tuple((uint8_t*)(-1), stat_buf
, false);
241 //PERF-HACK: touch bits of the MachO before we need them to speed things up on a spinning disk
242 madvise((void*)buffer_ptr
, 4096, MADV_WILLNEED
);
244 //if it is fat we need to touch each architecture
245 const fat_header
* fh
= (fat_header
*)buffer_ptr
;
246 if ( OSSwapBigToHostInt32( fh
->magic
) == FAT_MAGIC
) {
247 const fat_arch
* slices
= (const fat_arch
*)( (char*)fh
+ sizeof( fat_header
) );
248 const uint32_t sliceCount
= OSSwapBigToHostInt32( fh
->nfat_arch
);
249 for ( uint32_t i
= 0; i
< sliceCount
; ++i
) {
250 uint32_t fileOffset
= OSSwapBigToHostInt32( slices
[i
].offset
);
251 madvise((void*)((uint8_t *)buffer_ptr
+fileOffset
), 4096, MADV_WILLNEED
);
255 return std::make_tuple((uint8_t*)buffer_ptr
, stat_buf
, rootlessProtected
);