]> git.saurik.com Git - apple/ld64.git/blob - src/ld/Snapshot.cpp
ld64-305.tar.gz
[apple/ld64.git] / src / ld / Snapshot.cpp
1 //
2 // Snapshot.cpp
3 // ld64
4 //
5 // Created by Josh Behnke on 8/25/11.
6 // Copyright (c) 2011 Apple Inc. All rights reserved.
7 //
8
9 #include <string.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <limits.h>
13 #include <fcntl.h>
14 #include <ctype.h>
15 #include <sys/stat.h>
16 #include <libgen.h>
17 #include <time.h>
18 #include <Block.h>
19
20 #include "Snapshot.h"
21 #include "Options.h"
22
23 #include "compile_stubs.h"
24
25 //#define STORE_PID_IN_SNAPSHOT 1
26
27 // Well known snapshot file/directory names. These appear in the root of the snapshot.
28 // They are collected together here to make managing the namespace easier.
29 static const char *frameworksString = "frameworks"; // directory containing framework stubs (mach-o files)
30 static const char *dylibsString = "dylibs"; // directory containing dylib stubs (mach-o files)
31 static const char *archiveFilesString = "archive_files"; // directory containing .a files
32 static const char *origCommandLineString = "orig_command_line"; // text file containing the original command line
33 static const char *linkCommandString = "link_command"; // text file containing the snapshot equivalent command line
34 static const char *dataFilesString = "data_files"; // arbitrary data files referenced on the command line
35 static const char *objectsString = "objects"; // directory containing object files
36 static const char *frameworkStubsString = "framework_stubs"; // directory containing framework stub info (text files)
37 static const char *dylibStubsString = "dylib_stubs"; // directory containing dylib stub info (text files)
38 static const char *assertFileString = "assert_info"; // text file containing assertion failure logs
39 static const char *compileFileString = "compile_stubs"; // text file containing compile_stubs script
40
41 Snapshot *Snapshot::globalSnapshot = NULL;
42
43 Snapshot::Snapshot() : fRecordArgs(false), fRecordObjects(false), fRecordDylibSymbols(false), fRecordArchiveFiles(false), fRecordUmbrellaFiles(false), fRecordDataFiles(false), fFrameworkArgAdded(false), fSnapshotLocation(NULL), fSnapshotName(NULL), fRootDir(NULL), fFilelistFile(-1), fCopiedArchives(NULL)
44 {
45 if (globalSnapshot != NULL)
46 throw "only one snapshot supported";
47 globalSnapshot = this;
48 }
49
50
51 Snapshot::~Snapshot()
52 {
53 // Lots of things leak under the assumption the linker is about to exit.
54 }
55
56
57 void Snapshot::setSnapshotPath(const char *path)
58 {
59 if (fRootDir == NULL) {
60 fSnapshotLocation = strdup(path);
61 }
62 }
63
64
65 void Snapshot::setSnapshotMode(SnapshotMode mode)
66 {
67 if (fRootDir == NULL) {
68 fRecordArgs = false;
69 fRecordObjects = false;
70 fRecordDylibSymbols = false;
71 fRecordArchiveFiles = false;
72 fRecordUmbrellaFiles = false;
73 fRecordDataFiles = false;
74
75 switch (mode) {
76 case SNAPSHOT_DISABLED:
77 break;
78 case SNAPSHOT_DEBUG:
79 fRecordArgs = fRecordObjects = fRecordDylibSymbols = fRecordArchiveFiles = fRecordUmbrellaFiles = fRecordDataFiles = true;
80 break;
81 default:
82 break;
83 }
84 }
85 }
86
87 void Snapshot::setSnapshotName(const char *path)
88 {
89 if (fRootDir == NULL) {
90 const char *base = basename((char *)path);
91 time_t now = time(NULL);
92 struct tm t;
93 localtime_r(&now, &t);
94 char buf[PATH_MAX];
95 snprintf(buf, sizeof(buf)-1, "%s-%4.4d-%2.2d-%2.2d-%2.2d%2.2d%2.2d.ld-snapshot", base, t.tm_year+1900, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
96 fSnapshotName = strdup(buf);
97 }
98 }
99
100
101 // Construct a path string in the snapshot.
102 // subdir - an optional subdirectory name
103 // file - the file name
104 void Snapshot::buildPath(char *buf, const char *subdir, const char *file)
105 {
106 if (fRootDir == NULL)
107 throw "snapshot not created";
108
109 strcpy(buf, fRootDir);
110 strcat(buf, "/");
111 if (subdir) {
112 strcat(buf, subdir);
113 // implicitly create the subdirectory
114 mkdir(buf, S_IRUSR|S_IWUSR|S_IXUSR);
115 strcat(buf, "/");
116 }
117 if (file != NULL)
118 strcat(buf, basename((char *)file));
119 }
120
121
122 // Construct a unique path string in the snapshot. If a path collision is detected then uniquing
123 // is accomplished by appending a counter to the path until there is no preexisting file.
124 // subdir - an optional subdirectory name
125 // file - the file name
126 void Snapshot::buildUniquePath(char *buf, const char *subdir, const char *file)
127 {
128 buildPath(buf, subdir, file);
129 struct stat st;
130 if (stat(buf, &st)==0) {
131 // make it unique
132 int counter=1;
133 char *number = strrchr(buf, 0);
134 number[0]='-';
135 number++;
136 do {
137 sprintf(number, "%d", counter++);
138 } while (stat(buf, &st) == 0);
139 }
140 }
141
142
143 // Copy a file to the snapshot.
144 // sourcePath is the original file
145 // subdir is an optional subdirectory in the snapshot
146 // path is an optional out parameter containing the final uniqued path in the snapshot
147 // where the file was copied
148 void Snapshot::copyFileToSnapshot(const char *sourcePath, const char *subdir, char *path)
149 {
150 const int copyBufSize=(1<<14); // 16kb buffer
151 static void *copyBuf = NULL;
152 if (copyBuf == NULL)
153 copyBuf = malloc(copyBufSize);
154
155 char *file=basename((char *)sourcePath);
156 char buf[PATH_MAX];
157 if (path == NULL) path = buf;
158 buildUniquePath(path, subdir, file);
159 int out_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
160 int in_fd = open(sourcePath, O_RDONLY);
161 int len;
162 if (out_fd != -1 && in_fd != -1) {
163 do {
164 len = read(in_fd, copyBuf, copyBufSize);
165 if (len > 0) write(out_fd, copyBuf, len);
166 } while (len == copyBufSize);
167 }
168 close(in_fd);
169 close(out_fd);
170 }
171
172
173 // Create the snapshot root directory.
174 void Snapshot::createSnapshot()
175 {
176 if (fRootDir == NULL) {
177 // provide default name and location
178 if (fSnapshotLocation == NULL)
179 fSnapshotLocation = "/tmp";
180 if (fSnapshotName == NULL) {
181 setSnapshotName("ld_snapshot");
182 }
183
184 char buf[PATH_MAX];
185 fRootDir = (char *)fSnapshotLocation;
186 buildUniquePath(buf, NULL, fSnapshotName);
187 fRootDir = strdup(buf);
188 if (mkdir(fRootDir, S_IRUSR|S_IWUSR|S_IXUSR)!=0) {
189 warning("unable to create link snapshot directory: %s", fRootDir);
190 fRootDir = NULL;
191 setSnapshotMode(SNAPSHOT_DISABLED); // don't try to write anything if we can't create snapshot dir
192 }
193
194 buildPath(buf, NULL, compileFileString);
195 int compileScript = open(buf, O_WRONLY|O_CREAT|O_TRUNC, S_IXUSR|S_IRUSR|S_IWUSR);
196 write(compileScript, compile_stubs, strlen(compile_stubs));
197 close(compileScript);
198
199 SnapshotLog::iterator it;
200 for (it = fLog.begin(); it != fLog.end(); it++) {
201 void (^logItem)(void) = *it;
202 logItem();
203 Block_release(logItem);
204 }
205 fLog.erase(fLog.begin(), fLog.end());
206
207 if (fRecordArgs) {
208 writeCommandLine(fRawArgs, origCommandLineString, true);
209 writeCommandLine(fArgs);
210 }
211
212 #if STORE_PID_IN_SNAPSHOT
213 char path[PATH_MAX];
214 buildUniquePath(path, NULL, pidString);
215 int pidfile = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
216 char pid_buf[32];
217 sprintf(pid_buf, "%lu\n", (long unsigned)getpid());
218 write(pidfile, pid_buf, strlen(pid_buf));
219 write(pidfile, "\n", 1);
220 close(pidfile);
221 #endif
222
223 }
224 }
225
226
227 // Write the current command line vector to filename.
228 void Snapshot::writeCommandLine(StringVector &args, const char *filename, bool includeCWD)
229 {
230 if (!isLazy() && fRecordArgs) {
231 // Figure out the file name and open it.
232 if (filename == NULL)
233 filename = linkCommandString;
234 char path[PATH_MAX];
235 buildPath(path, NULL, filename);
236 int argsFile = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IXUSR|S_IRUSR|S_IWUSR);
237 FILE *argsStream = fdopen(argsFile, "w");
238
239 if (includeCWD)
240 fprintf(argsStream, "cd %s\n", getcwd(path, sizeof(path)));
241
242 // iterate to write args, quoting as needed
243 StringVector::iterator it;
244 for (it = args.begin(); it != args.end(); it++) {
245 const char *arg = *it;
246 bool needQuotes = false;
247 for (const char *c = arg; *c != 0 && !needQuotes; c++) {
248 if (isspace(*c))
249 needQuotes = true;
250 }
251 if (it != args.begin()) fprintf(argsStream, " ");
252 if (needQuotes) fprintf(argsStream, "\"");
253 fprintf(argsStream, "%s", arg);
254 if (needQuotes) fprintf(argsStream, "\"");
255 }
256 fprintf(argsStream, "\n");
257 fclose(argsStream);
258 }
259 }
260
261
262 // Store the command line args in the snapshot.
263 void Snapshot::recordRawArgs(int argc, const char *argv[])
264 {
265 // first store the original command line as-is
266 for (int i=0; i<argc; i++) {
267 fRawArgs.push_back(argv[i]);
268 }
269 fArgs.insert(fArgs.begin(), argv[0]);
270 fArgs.insert(fArgs.begin()+1, "-Z"); // don't search standard paths when running in the snapshot
271 }
272
273
274 // Adds one or more args to the snapshot link command.
275 // argIndex is the index in the original raw args vector to start adding args
276 // argCount is the count of args to copy from the raw args vector
277 // fileArg is the index relative to argIndex of a file arg. The file is copied into the
278 // snapshot and the path is fixed up in the snapshot link command. (skipped if fileArg==-1)
279 void Snapshot::addSnapshotLinkArg(int argIndex, int argCount, int fileArg)
280 {
281 if (fRootDir == NULL) {
282 fLog.push_back(Block_copy(^{ this->addSnapshotLinkArg(argIndex, argCount, fileArg); }));
283 } else {
284 char buf[PATH_MAX];
285 const char *subdir = dataFilesString;
286 for (int i=0, arg=argIndex; i<argCount && argIndex+1<(int)fRawArgs.size(); i++, arg++) {
287 if (i != fileArg) {
288 fArgs.push_back(fRawArgs[arg]);
289 } else {
290 if (fRecordDataFiles) {
291 copyFileToSnapshot(fRawArgs[arg], subdir, buf);
292 fArgs.push_back(strdup(snapshotRelativePath(buf)));
293 } else {
294 // if we don't copy the file then just record the original path
295 fArgs.push_back(strdup(fRawArgs[arg]));
296 }
297 }
298 }
299 }
300 }
301
302 // Record the -arch string
303 void Snapshot::recordArch(const char *arch)
304 {
305 // must be called after recordRawArgs()
306 if (fRawArgs.size() == 0)
307 throw "raw args not set";
308
309 // only need to store the arch explicitly if it is not mentioned on the command line
310 bool archInArgs = false;
311 StringVector::iterator it;
312 for (it = fRawArgs.begin(); it != fRawArgs.end() && !archInArgs; it++) {
313 const char *arg = *it;
314 if (strcmp(arg, "-arch") == 0)
315 archInArgs = true;
316 }
317
318 if (!archInArgs) {
319 if (fRootDir == NULL) {
320 fLog.push_back(Block_copy(^{ this->recordArch(arch); }));
321 } else {
322 char path_buf[PATH_MAX];
323 buildUniquePath(path_buf, NULL, "arch");
324 int fd=open(path_buf, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
325 write(fd, arch, strlen(arch));
326 close(fd);
327 }
328 }
329 }
330
331 // Record an object file in the snapshot.
332 // path - the object file's path
333 // fileContent - a pointer to the object file content
334 // fileLength - the buffer size of fileContent
335 void Snapshot::recordObjectFile(const char *path)
336 {
337 if (fRootDir == NULL) {
338 fLog.push_back(Block_copy(^{ this->recordObjectFile(path); }));
339 } else {
340 if (fRecordObjects) {
341 char path_buf[PATH_MAX];
342 copyFileToSnapshot(path, objectsString, path_buf);
343
344 // lazily open the filelist file
345 if (fFilelistFile == -1) {
346 char filelist_path[PATH_MAX];
347 buildUniquePath(filelist_path, objectsString, "filelist");
348 fFilelistFile = open(filelist_path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
349 fArgs.push_back("-filelist");
350 fArgs.push_back(strdup(snapshotRelativePath(filelist_path)));
351 writeCommandLine(fArgs);
352 }
353
354 // record the snapshot path in the filelist
355 const char *relative_path = snapshotRelativePath(path_buf);
356 write(fFilelistFile, relative_path, strlen(relative_path));
357 write(fFilelistFile, "\n", 1);
358 }
359 }
360 }
361
362 void Snapshot::addFrameworkArg(const char *framework)
363 {
364 bool found=false;
365 for (unsigned i=0; i<fArgs.size()-1; i++) {
366 if (strcmp(fArgs[i], "-framework") == 0 && strcmp(fArgs[i+1], framework) == 0)
367 found = true;
368 }
369 if (!found) {
370 if (!fFrameworkArgAdded) {
371 fFrameworkArgAdded = true;
372 fArgs.push_back("-Fframeworks");
373 }
374 fArgs.push_back("-framework");
375 fArgs.push_back(strdup(framework));
376 writeCommandLine(fArgs);
377 }
378 }
379
380 void Snapshot::addDylibArg(const char *dylib)
381 {
382 bool found=false;
383 for (unsigned i=0; i<fArgs.size()-1; i++) {
384 if (strcmp(fArgs[i], dylib) == 0)
385 found = true;
386 }
387 if (!found) {
388 char buf[ARG_MAX];
389 sprintf(buf, "%s/%s", dylibsString, dylib);
390 fArgs.push_back(strdup(buf));
391 writeCommandLine(fArgs);
392 }
393 }
394
395 // Record a dylib symbol reference in the snapshot.
396 // (References are not written to the snapshot until writeStubDylibs() is called.)
397 void Snapshot::recordDylibSymbol(ld::dylib::File* dylibFile, const char *name)
398 {
399 if (fRootDir == NULL) {
400 fLog.push_back(Block_copy(^{ this->recordDylibSymbol(dylibFile, name); }));
401 } else {
402 if (fRecordDylibSymbols) {
403 // find the dylib in the table
404 DylibMap::iterator it;
405 const char *dylibPath = dylibFile->path();
406 it = fDylibSymbols.find(dylibPath);
407 bool isFramework = (strstr(dylibPath, "framework") != NULL);
408 int dylibFd;
409 if (it == fDylibSymbols.end()) {
410 // Didn't find a file descriptor for this dylib. Create one and add it to the dylib map.
411 char path_buf[PATH_MAX];
412 buildUniquePath(path_buf, isFramework ? frameworkStubsString : dylibStubsString, dylibPath);
413 dylibFd = open(path_buf, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
414 fDylibSymbols.insert(std::pair<const char *, int>(dylibPath, dylibFd));
415 char *base_name = strdup(basename(path_buf));
416 if (isFramework) {
417 addFrameworkArg(base_name);
418 } else {
419 addDylibArg(base_name);
420 }
421 writeCommandLine(fArgs);
422 } else {
423 dylibFd = it->second;
424 }
425 // Record the symbol.
426
427 bool isIdentifier = (name[0] == '_');
428 for (const char *c = name; *c != 0 && isIdentifier; c++)
429 if (!isalnum(*c) && *c!='_')
430 isIdentifier = false;
431 const char *prefix = "void ";
432 const char *weakAttr = "__attribute__ ((weak)) ";
433 const char *suffix = "(void){}\n";
434 if (isIdentifier) {
435 write(dylibFd, prefix, strlen(prefix));
436 if (dylibFile->hasWeakExternals() && dylibFile->hasWeakDefinition(name))
437 write(dylibFd, weakAttr, strlen(weakAttr));
438 if (*name == '_') name++;
439 write(dylibFd, name, strlen(name));
440 write(dylibFd, suffix, strlen(suffix));
441 } else {
442 static int symbolCounter = 0;
443 char buf[64+strlen(name)];
444 sprintf(buf, "void s_%5.5d(void) __asm(\"%s\");\nvoid s_%5.5d(){}\n", symbolCounter, name, symbolCounter);
445 write(dylibFd, buf, strlen(buf));
446 symbolCounter++;
447 }
448 }
449 }
450 }
451
452
453 // Record a .a archive in the snapshot.
454 void Snapshot::recordArchive(const char *archiveFile)
455 {
456 if (fRootDir == NULL) {
457 const char *copy = strdup(archiveFile);
458 fLog.push_back(Block_copy(^{ this->recordArchive(archiveFile); ::free((void *)copy); }));
459 } else {
460 if (fRecordArchiveFiles) {
461 // lazily create a vector of .a files that have been added
462 if (fCopiedArchives == NULL) {
463 fCopiedArchives = new StringVector;
464 }
465
466 // See if we have already added this .a
467 StringVector::iterator it;
468 bool found = false;
469 for (it = fCopiedArchives->begin(); it != fCopiedArchives->end() && !found; it++) {
470 if (strcmp(archiveFile, *it) == 0)
471 found = true;
472 }
473
474 // If this is a new .a then copy it to the snapshot and add it to the snapshot link command.
475 if (!found) {
476 char path[PATH_MAX];
477 fCopiedArchives->push_back(archiveFile);
478 copyFileToSnapshot(archiveFile, archiveFilesString, path);
479 fArgs.push_back(strdup(snapshotRelativePath(path)));
480 writeCommandLine(fArgs);
481 }
482 }
483 }
484 }
485
486 void Snapshot::recordSubUmbrella(const char *frameworkPath)
487 {
488 if (fRootDir == NULL) {
489 const char *copy = strdup(frameworkPath);
490 fLog.push_back(Block_copy(^{ this->recordSubUmbrella(copy); ::free((void *)copy); }));
491 } else {
492 if (fRecordUmbrellaFiles) {
493 const char *framework = basename((char *)frameworkPath);
494 char buf[PATH_MAX], wrapper[PATH_MAX];
495 strcpy(wrapper, frameworksString);
496 buildPath(buf, wrapper, NULL); // ensure the frameworks directory exists
497 strcat(wrapper, "/");
498 strcat(wrapper, framework);
499 strcat(wrapper, ".framework");
500 copyFileToSnapshot(frameworkPath, wrapper);
501 addFrameworkArg(framework);
502 }
503 }
504 }
505
506 void Snapshot::recordSubLibrary(const char *dylibPath)
507 {
508 if (fRootDir == NULL) {
509 const char *copy = strdup(dylibPath);
510 fLog.push_back(Block_copy(^{ this->recordSubLibrary(copy); ::free((void *)copy); }));
511 } else {
512 if (fRecordUmbrellaFiles) {
513 copyFileToSnapshot(dylibPath, dylibsString);
514 addDylibArg(basename((char *)dylibPath));
515 }
516 }
517 }
518
519 void Snapshot::recordAssertionMessage(const char *fmt, ...)
520 {
521 char *msg;
522 va_list args;
523 va_start(args, fmt);
524 vasprintf(&msg, fmt, args);
525 va_end(args);
526 if (msg != NULL) {
527 if (fRootDir == NULL) {
528 fLog.push_back(Block_copy(^{ this->recordAssertionMessage("%s", msg); free(msg); }));
529 } else {
530 char path[PATH_MAX];
531 buildPath(path, NULL, assertFileString);
532 int log = open(path, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
533 write(log, msg, strlen(msg));
534 close(log);
535 free(msg);
536 }
537 }
538 }