]>
Commit | Line | Data |
---|---|---|
10b92d3b A |
1 | /* |
2 | * Copyright (c) 2017 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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 | |
11 | * file. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <stdio.h> | |
25 | #include <string.h> | |
26 | #include <sandbox/private.h> | |
27 | #include <bootstrap.h> | |
28 | #include <mach/mach.h> | |
29 | #include <os/log.h> | |
30 | #include <sys/mman.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> | |
36 | #include <libproc.h> | |
37 | #include <uuid/uuid.h> | |
38 | ||
39 | #include <string> | |
40 | #include <vector> | |
41 | #include <unordered_map> | |
42 | ||
43 | #include "dyld_priv.h" | |
44 | #include "ImageProxy.h" | |
45 | #include "DyldSharedCache.h" | |
46 | #include "FileUtils.h" | |
47 | ||
48 | extern "C" { | |
49 | #include "closuredProtocolServer.h" | |
50 | } | |
51 | ||
52 | ||
53 | static os_log_t sLog = os_log_create("com.apple.dyld.closured", "closured"); | |
54 | ||
55 | static char sCrashMessageBuffer[1024]; | |
56 | ||
57 | ||
58 | kern_return_t | |
59 | do_CreateClosure( | |
60 | mach_port_t port, | |
61 | task_t requestor, | |
62 | vm_address_t buffer, | |
63 | mach_msg_type_number_t bufferCnt, | |
64 | vm_address_t* returnData, | |
65 | mach_msg_type_number_t* returnDataCnt) | |
66 | { | |
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); | |
70 | ||
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); | |
75 | ||
76 | Diagnostics diag; | |
77 | const dyld3::launch_cache::binary_format::Closure* cls = dyld3::ImageProxyGroup::makeClosure(diag, clsBuff, requestor); | |
78 | ||
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()); | |
82 | ||
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(); | |
88 | } | |
89 | else { | |
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(); | |
95 | } | |
96 | ||
97 | CRSetCrashLogMessage(nullptr); | |
98 | return KERN_SUCCESS; | |
99 | } | |
100 | ||
101 | kern_return_t | |
102 | do_CreateImageGroup( | |
103 | mach_port_t port, | |
104 | task_t requestor, | |
105 | vm_address_t buffer, | |
106 | mach_msg_type_number_t bufferCnt, | |
107 | vm_address_t* returnData, | |
108 | mach_msg_type_number_t* returnDataCnt) | |
109 | { | |
110 | dyld3::ClosureBuffer clsBuff((void*)buffer, bufferCnt); | |
111 | const char* imagePath = clsBuff.targetPath(); | |
112 | int requestorPid; | |
113 | char requestorName[MAXPATHLEN]; | |
114 | if ( pid_for_task(requestor, &requestorPid) == 0 ) { | |
115 | int nameLen = proc_name(requestorPid, requestorName, sizeof(requestorName)); | |
116 | if ( nameLen <= 0 ) | |
117 | strcpy(requestorName, "???"); | |
118 | os_log(sLog, "request from %d (%s) to build dlopen ImageGroup for %s\n", requestorPid, requestorName, imagePath); | |
119 | } | |
120 | ||
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); | |
127 | ||
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); | |
132 | ||
133 | Diagnostics diag; | |
134 | const dyld3::launch_cache::binary_format::ImageGroup* imageGroup = dyld3::ImageProxyGroup::makeDlopenGroup(diag, clsBuff, requestor, {""}); | |
135 | ||
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()); | |
139 | } | |
140 | ||
141 | // delete incoming out-of-line data | |
142 | vm_deallocate(mach_task_self(), buffer, bufferCnt); | |
143 | ||
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); | |
151 | } | |
152 | else { | |
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(); | |
158 | } | |
159 | ||
160 | CRSetCrashLogMessage(nullptr); | |
161 | return KERN_SUCCESS; | |
162 | } | |
163 | ||
164 | ||
165 | ||
166 | ||
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[]) | |
169 | { | |
170 | const char* progPath = nullptr; | |
171 | int pipeNum = -1; | |
172 | bool verbose = false; | |
173 | std::vector<std::string> dyldEnvVars; | |
174 | ||
175 | dyld3::ClosureBuffer::CacheIdent cacheIdent; | |
176 | bzero(&cacheIdent, sizeof(cacheIdent)); | |
177 | ||
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)); | |
183 | } | |
184 | CRSetCrashLogMessage(sCrashMessageBuffer); | |
185 | ||
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"); | |
192 | return 1; | |
193 | } | |
194 | } | |
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"); | |
199 | return 1; | |
200 | } | |
201 | uuid_parse(uuidStr, cacheIdent.cacheUUID); | |
202 | } | |
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"); | |
207 | return 1; | |
208 | } | |
209 | char *end; | |
210 | cacheIdent.cacheAddress = strtol(cacheAddr, &end, 0); | |
211 | } | |
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"); | |
216 | return 1; | |
217 | } | |
218 | char *end; | |
219 | cacheIdent.cacheMappedSize = strtol(cacheSize, &end, 0); | |
220 | } | |
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"); | |
225 | return 1; | |
226 | } | |
227 | char *end; | |
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); | |
231 | return 1; | |
232 | } | |
233 | } | |
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"); | |
238 | return 1; | |
239 | } | |
240 | dyldEnvVars.push_back(var); | |
241 | } | |
242 | else { | |
243 | fprintf(stderr, "unknown option: %s\n", arg); | |
244 | return 1; | |
245 | } | |
246 | } | |
247 | if ( progPath == nullptr ) { | |
248 | fprintf(stderr, "missing required -create_closure option\n"); | |
249 | return 1; | |
250 | } | |
251 | if ( pipeNum == -1 ) { | |
252 | fprintf(stderr, "missing required -pipefd option\n"); | |
253 | return 1; | |
254 | } | |
255 | ||
256 | if ( verbose ) { | |
257 | fprintf(stderr, "closured: runAsTool()\n"); | |
258 | for (int i=1; i < argc; ++i) | |
259 | fprintf(stderr, " argv[%d] = %s\n", i, argv[i]); | |
260 | } | |
261 | ||
262 | os_log(sLog, "fork/exec request to build closure for %s\n", progPath); | |
263 | SocketBasedClousureHeader header; | |
264 | ||
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"); | |
270 | return 1; | |
271 | } | |
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); | |
277 | header.success = 0; | |
278 | header.length = (int)strlen(errorString) + 1; | |
279 | write(pipeNum, &header, sizeof(SocketBasedClousureHeader)); | |
280 | write(pipeNum, errorString, header.length); | |
281 | close(pipeNum); | |
282 | return 0; | |
283 | } | |
284 | dyld3::DyldCacheParser cacheParser(currentCache, false); | |
285 | ||
286 | Diagnostics diag; | |
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()); | |
294 | header.success = 1; | |
295 | header.length = (uint32_t)closure.size(); | |
296 | write(pipeNum, &header, sizeof(SocketBasedClousureHeader)); | |
297 | write(pipeNum, cls, closure.size()); | |
298 | } | |
299 | else { | |
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()); | |
303 | header.success = 0; | |
304 | header.length = (uint32_t)message.size() + 1; | |
305 | write(pipeNum, &header, sizeof(SocketBasedClousureHeader)); | |
306 | write(pipeNum, message.c_str(), header.length); | |
307 | } | |
308 | ||
309 | close(pipeNum); | |
310 | ||
311 | return 0; | |
312 | } | |
313 | ||
314 | ||
315 | union MaxMsgSize { | |
316 | union __RequestUnion__do_closured_subsystem req; | |
317 | union __ReplyUnion__do_closured_subsystem rep; | |
318 | }; | |
319 | ||
320 | int main(int argc, const char* argv[]) | |
321 | { | |
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); | |
327 | exit(EXIT_FAILURE); | |
328 | } | |
329 | #endif | |
330 | ||
331 | if ( argc != 1 ) | |
332 | return runAsTool(argc, argv);\ | |
333 | ||
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) | |
337 | exit(-1); | |
338 | ||
339 | dispatch_source_t machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, serverPort, 0, dispatch_get_main_queue()); | |
340 | if (machSource == nullptr) | |
341 | exit(-1); | |
342 | ||
343 | dispatch_source_set_event_handler(machSource, ^{ | |
344 | dispatch_mig_server(machSource, sizeof(union MaxMsgSize), closured_server); | |
345 | }); | |
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) | |
350 | exit(-1); | |
351 | }); | |
352 | dispatch_resume(machSource); | |
353 | dispatch_main(); | |
354 | ||
355 | return 0; | |
356 | } | |
357 |