2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <sandbox/private.h>
27 #include <bootstrap.h>
28 #include <mach/mach.h>
31 #include <sys/errno.h>
32 #include <dispatch/dispatch.h>
33 #include <dispatch/private.h>
34 #include <bootstrap_priv.h> // for bootstrap_check_in()
35 #include <CrashReporterClient.h>
37 #include <uuid/uuid.h>
41 #include <unordered_map>
43 #include "dyld_priv.h"
44 #include "ImageProxy.h"
45 #include "DyldSharedCache.h"
46 #include "FileUtils.h"
49 #include "closuredProtocolServer.h"
53 static os_log_t sLog
= os_log_create("com.apple.dyld.closured", "closured");
55 static char sCrashMessageBuffer
[1024];
63 mach_msg_type_number_t bufferCnt
,
64 vm_address_t
* returnData
,
65 mach_msg_type_number_t
* returnDataCnt
)
67 dyld3::ClosureBuffer
clsBuff((void*)buffer
, bufferCnt
);
68 const char* imagePath
= clsBuff
.targetPath();
69 os_log(sLog
, "request to build closure for %s\n", imagePath
);
71 // set crash log message in case there is an assert during processing
72 strlcpy(sCrashMessageBuffer
, "building closure for: ", sizeof(sCrashMessageBuffer
));
73 strlcat(sCrashMessageBuffer
, imagePath
, sizeof(sCrashMessageBuffer
));
74 CRSetCrashLogMessage(sCrashMessageBuffer
);
77 const dyld3::launch_cache::binary_format::Closure
* cls
= dyld3::ImageProxyGroup::makeClosure(diag
, clsBuff
, requestor
);
79 os_log_info(sLog
, "finished closure build, closure=%p\n", cls
);
80 for (const std::string
& message
: diag
.warnings())
81 os_log(sLog
, "Image generated warning: %s\n", message
.c_str());
83 if ( diag
.noError() ) {
84 // on success return the closure binary in the "returnData" buffer
85 dyld3::ClosureBuffer
result(cls
);
86 *returnData
= result
.vmBuffer();
87 *returnDataCnt
= result
.vmBufferSize();
90 // on failure return the error message in the "returnData" buffer
91 os_log_error(sLog
, "failed to create ImageGroup: %s\n", diag
.errorMessage().c_str());
92 dyld3::ClosureBuffer
err(diag
.errorMessage().c_str());
93 *returnData
= err
.vmBuffer();
94 *returnDataCnt
= err
.vmBufferSize();
97 CRSetCrashLogMessage(nullptr);
106 mach_msg_type_number_t bufferCnt
,
107 vm_address_t
* returnData
,
108 mach_msg_type_number_t
* returnDataCnt
)
110 dyld3::ClosureBuffer
clsBuff((void*)buffer
, bufferCnt
);
111 const char* imagePath
= clsBuff
.targetPath();
113 char requestorName
[MAXPATHLEN
];
114 if ( pid_for_task(requestor
, &requestorPid
) == 0 ) {
115 int nameLen
= proc_name(requestorPid
, requestorName
, sizeof(requestorName
));
117 strcpy(requestorName
, "???");
118 os_log(sLog
, "request from %d (%s) to build dlopen ImageGroup for %s\n", requestorPid
, requestorName
, imagePath
);
121 // set crash log message in case there is an assert during processing
122 strlcpy(sCrashMessageBuffer
, "building ImageGroup for dlopen(", sizeof(sCrashMessageBuffer
));
123 strlcat(sCrashMessageBuffer
, imagePath
, sizeof(sCrashMessageBuffer
));
124 strlcat(sCrashMessageBuffer
, ") requested by ", sizeof(sCrashMessageBuffer
));
125 strlcat(sCrashMessageBuffer
, requestorName
, sizeof(sCrashMessageBuffer
));
126 CRSetCrashLogMessage(sCrashMessageBuffer
);
128 uuid_string_t uuidStr
;
129 dyld3::ClosureBuffer::CacheIdent cacheIdent
= clsBuff
.cacheIndent();
130 uuid_unparse(cacheIdent
.cacheUUID
, uuidStr
);
131 os_log_info(sLog
, "findDyldCache(): cache addr=0x%llX, size=0x%0llX, uuid = %s\n", cacheIdent
.cacheAddress
, cacheIdent
.cacheMappedSize
, uuidStr
);
134 const dyld3::launch_cache::binary_format::ImageGroup
* imageGroup
= dyld3::ImageProxyGroup::makeDlopenGroup(diag
, clsBuff
, requestor
, {""});
136 os_log(sLog
, "finished ImageGroup build, imageGroup=%p\n", imageGroup
);
137 for (const std::string
& message
: diag
.warnings()) {
138 os_log(sLog
, "Image generated warning: %s\n", message
.c_str());
141 // delete incoming out-of-line data
142 vm_deallocate(mach_task_self(), buffer
, bufferCnt
);
144 if ( diag
.noError() ) {
145 // on success return the ImageGroup binary in the "returnData" buffer
146 dyld3::ClosureBuffer
result(imageGroup
);
147 os_log_info(sLog
, "returning closure buffer: 0x%lX, size=0x%X\n", (long)result
.vmBuffer(), result
.vmBufferSize());
148 *returnData
= result
.vmBuffer();
149 *returnDataCnt
= result
.vmBufferSize();
150 free((void*)imageGroup
);
153 // on failure return the error message in the "returnData" buffer
154 os_log_error(sLog
, "failed to create ImageGroup: %s\n", diag
.errorMessage().c_str());
155 dyld3::ClosureBuffer
err(diag
.errorMessage().c_str());
156 *returnData
= err
.vmBuffer();
157 *returnDataCnt
= err
.vmBufferSize();
160 CRSetCrashLogMessage(nullptr);
167 // /usr/libexec/closured -create_closure /Applications/TextEdit.app -pipefd 4 -env DYLD_FOO=1 -cache_uuid C153F90A-69F2-323E-AC9F-2BBAE48ABAF7
168 int runAsTool(int argc
, const char* argv
[])
170 const char* progPath
= nullptr;
172 bool verbose
= false;
173 std::vector
<std::string
> dyldEnvVars
;
175 dyld3::ClosureBuffer::CacheIdent cacheIdent
;
176 bzero(&cacheIdent
, sizeof(cacheIdent
));
178 // set crash log message in case there is an assert during processing
179 sCrashMessageBuffer
[0] = '\0';
180 for (int i
=0; i
< argc
; ++i
) {
181 strlcat(sCrashMessageBuffer
, argv
[i
], sizeof(sCrashMessageBuffer
));
182 strlcat(sCrashMessageBuffer
, " ", sizeof(sCrashMessageBuffer
));
184 CRSetCrashLogMessage(sCrashMessageBuffer
);
186 for (int i
=1; i
< argc
; ++i
) {
187 const char* arg
= argv
[i
];
188 if ( strcmp(arg
, "-create_closure") == 0 ) {
189 progPath
= argv
[++i
];
190 if ( progPath
== nullptr ) {
191 fprintf(stderr
, "-create_closure option requires a path to follow\n");
195 else if ( strcmp(arg
, "-cache_uuid") == 0 ) {
196 const char* uuidStr
= argv
[++i
];
197 if ( uuidStr
== nullptr ) {
198 fprintf(stderr
, "-cache_uuid option requires a path to follow\n");
201 uuid_parse(uuidStr
, cacheIdent
.cacheUUID
);
203 else if ( strcmp(arg
, "-cache_address") == 0 ) {
204 const char* cacheAddr
= argv
[++i
];
205 if ( cacheAddr
== nullptr ) {
206 fprintf(stderr
, "-cache_address option requires a path to follow\n");
210 cacheIdent
.cacheAddress
= strtol(cacheAddr
, &end
, 0);
212 else if ( strcmp(arg
, "-cache_size") == 0 ) {
213 const char* cacheSize
= argv
[++i
];
214 if ( cacheSize
== nullptr ) {
215 fprintf(stderr
, "-cache_size option requires a path to follow\n");
219 cacheIdent
.cacheMappedSize
= strtol(cacheSize
, &end
, 0);
221 else if ( strcmp(arg
, "-pipefd") == 0 ) {
222 const char* numStr
= argv
[++i
];
223 if ( numStr
== nullptr ) {
224 fprintf(stderr
, "-pipefd option requires an file descriptor number to follow\n");
228 pipeNum
= (int)strtol(numStr
, &end
, 0);
229 if ( (pipeNum
== 0) && (errno
!= 0) ) {
230 fprintf(stderr
, "bad value (%s) for -pipefd option %d\n", numStr
, pipeNum
);
234 else if ( strcmp(arg
, "-env") == 0 ) {
235 const char* var
= argv
[++i
];
236 if ( var
== nullptr ) {
237 fprintf(stderr
, "-env option requires a following VAR=XXX\n");
240 dyldEnvVars
.push_back(var
);
243 fprintf(stderr
, "unknown option: %s\n", arg
);
247 if ( progPath
== nullptr ) {
248 fprintf(stderr
, "missing required -create_closure option\n");
251 if ( pipeNum
== -1 ) {
252 fprintf(stderr
, "missing required -pipefd option\n");
257 fprintf(stderr
, "closured: runAsTool()\n");
258 for (int i
=1; i
< argc
; ++i
)
259 fprintf(stderr
, " argv[%d] = %s\n", i
, argv
[i
]);
262 os_log(sLog
, "fork/exec request to build closure for %s\n", progPath
);
263 SocketBasedClousureHeader header
;
265 // find dyld cache for requested arch
266 size_t currentCacheSize
;
267 const DyldSharedCache
* currentCache
= (const DyldSharedCache
*)_dyld_get_shared_cache_range(¤tCacheSize
);
268 if ( currentCache
== nullptr ) {
269 os_log_error(sLog
, "closured is running without a dyld cache\n");
272 uuid_t currentCacheUUID
;
273 currentCache
->getUUID(currentCacheUUID
);
274 if ( memcmp(currentCacheUUID
, cacheIdent
.cacheUUID
, 16) != 0 ) {
275 const char* errorString
= "closured is running with a different dyld cache than client";
276 os_log_error(sLog
, "%s\n", errorString
);
278 header
.length
= (int)strlen(errorString
) + 1;
279 write(pipeNum
, &header
, sizeof(SocketBasedClousureHeader
));
280 write(pipeNum
, errorString
, header
.length
);
284 dyld3::DyldCacheParser
cacheParser(currentCache
, false);
287 os_log_info(sLog
, "starting closure build\n");
288 const dyld3::launch_cache::BinaryClosureData
* cls
= dyld3::ImageProxyGroup::makeClosure(diag
, cacheParser
, progPath
, false, {""}, dyldEnvVars
);
289 os_log_info(sLog
, "finished closure build, cls=%p\n", cls
);
290 if ( diag
.noError() ) {
291 // on success write the closure binary after the header to the socket
292 dyld3::launch_cache::Closure
closure(cls
);
293 os_log(sLog
, "returning closure, size=%lu\n", closure
.size());
295 header
.length
= (uint32_t)closure
.size();
296 write(pipeNum
, &header
, sizeof(SocketBasedClousureHeader
));
297 write(pipeNum
, cls
, closure
.size());
300 // on failure write the error message after the header to the socket
301 const std::string
& message
= diag
.errorMessage();
302 os_log_error(sLog
, "closure could not be created: %s\n", message
.c_str());
304 header
.length
= (uint32_t)message
.size() + 1;
305 write(pipeNum
, &header
, sizeof(SocketBasedClousureHeader
));
306 write(pipeNum
, message
.c_str(), header
.length
);
316 union __RequestUnion__do_closured_subsystem req
;
317 union __ReplyUnion__do_closured_subsystem rep
;
320 int main(int argc
, const char* argv
[])
322 #if __MAC_OS_X_VERSION_MIN_REQUIRED
323 // establish sandbox around process
324 char* errMsg
= nullptr;
325 if ( sandbox_init_with_parameters("com.apple.dyld.closured", SANDBOX_NAMED
, nullptr, &errMsg
) != 0 ) {
326 os_log_error(sLog
, "Failed to enter sandbox: %{public}s", errMsg
);
332 return runAsTool(argc
, argv
);\
334 mach_port_t serverPort
= MACH_PORT_NULL
;
335 kern_return_t kr
= bootstrap_check_in(bootstrap_port
, CLOSURED_SERVICE_NAME
, &serverPort
);
336 if (kr
!= KERN_SUCCESS
)
339 dispatch_source_t machSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, serverPort
, 0, dispatch_get_main_queue());
340 if (machSource
== nullptr)
343 dispatch_source_set_event_handler(machSource
, ^{
344 dispatch_mig_server(machSource
, sizeof(union MaxMsgSize
), closured_server
);
346 dispatch_source_set_cancel_handler(machSource
, ^{
347 mach_port_t port
= (mach_port_t
)dispatch_source_get_handle(machSource
);
348 kern_return_t kr
= mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_RECEIVE
, -1);
349 if (kr
!= KERN_SUCCESS
)
352 dispatch_resume(machSource
);