]>
git.saurik.com Git - apple/ld64.git/blob - src/ld/Snapshot.cpp
5 // Created by Josh Behnke on 8/25/11.
6 // Copyright (c) 2011 Apple Inc. All rights reserved.
24 #include "compile_stubs.h"
26 //#define STORE_PID_IN_SNAPSHOT 1
28 // Well known snapshot file/directory names. These appear in the root of the snapshot.
29 // They are collected together here to make managing the namespace easier.
30 static const char *frameworksString
= "frameworks"; // directory containing framework stubs (mach-o files)
31 static const char *dylibsString
= "dylibs"; // directory containing dylib stubs (mach-o files)
32 static const char *archiveFilesString
= "archive_files"; // directory containing .a files
33 static const char *objectsString
= "objects"; // directory containing object files
34 static const char *frameworkStubsString
= "framework_stubs"; // directory containing framework stub info (text files)
35 static const char *dataFilesString
= "data_files"; // arbitrary data files referenced on the command line
36 static const char *dylibStubsString
= "dylib_stubs"; // directory containing dylib stub info (text files)
37 static const char *filesString
= "files"; // directory containing files
38 static const char *origCommandLineString
= "orig_command_line"; // text file containing the original command line
39 static const char *linkCommandString
= "link_command"; // text file containing the snapshot equivalent command line
40 static const char *assertFileString
= "assert_info"; // text file containing assertion failure logs
41 static const char *compileFileString
= "compile_stubs"; // text file containing compile_stubs script
43 Snapshot
*Snapshot::globalSnapshot
= NULL
;
45 Snapshot::Snapshot(const Options
* opts
) : fOptions(opts
), fRecordArgs(false), fRecordObjects(false), fRecordDylibSymbols(false), fRecordArchiveFiles(false), fRecordUmbrellaFiles(false), fRecordDataFiles(false), fFrameworkArgAdded(false), fRecordKext(false), fSnapshotLocation(NULL
), fSnapshotName(NULL
), fRootDir(NULL
), fFilelistFile(-1), fCopiedArchives(NULL
)
47 if (globalSnapshot
!= NULL
)
48 throw "only one snapshot supported";
49 globalSnapshot
= this;
55 // Lots of things leak under the assumption the linker is about to exit.
59 void Snapshot::setSnapshotPath(const char *path
)
61 if (fRootDir
== NULL
) {
62 fSnapshotLocation
= strdup(path
);
67 void Snapshot::setSnapshotMode(SnapshotMode mode
)
69 if (fRootDir
== NULL
) {
74 if (fRootDir
== NULL
) {
76 fRecordObjects
= false;
77 fRecordDylibSymbols
= false;
78 fRecordArchiveFiles
= false;
79 fRecordUmbrellaFiles
= false;
80 fRecordDataFiles
= false;
84 case SNAPSHOT_DISABLED
:
87 fRecordArgs
= fRecordObjects
= fRecordDylibSymbols
= fRecordArchiveFiles
= fRecordUmbrellaFiles
= fRecordDataFiles
= true;
90 fRecordKext
= fRecordArgs
= fRecordObjects
= fRecordDylibSymbols
= fRecordArchiveFiles
= fRecordUmbrellaFiles
= fRecordDataFiles
= true;
98 void Snapshot::setOutputPath(const char *path
)
100 fOutputPath
= strdup(path
);
103 void Snapshot::setSnapshotName()
105 if (fRootDir
== NULL
) {
106 if (fOutputPath
== NULL
) {
107 fSnapshotName
= strdup("ld_snapshot");
109 const char *base
= basename((char *)fOutputPath
);
111 const char *kextobjects
;
112 if ((kextobjects
= fOptions
->kextObjectsPath())) {
113 fSnapshotLocation
= strdup(kextobjects
);
115 fSnapshotLocation
= strdup(dirname((char *)fOutputPath
));
117 asprintf((char **)&fSnapshotName
, "%s.%s.ld", base
, fArchString
);
119 time_t now
= time(NULL
);
121 localtime_r(&now
, &t
);
123 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
);
124 fSnapshotName
= strdup(buf
);
131 // Construct a path string in the snapshot.
132 // subdir - an optional subdirectory name
133 // file - the file name
134 void Snapshot::buildPath(char *buf
, const char *subdir
, const char *file
)
136 if (fRootDir
== NULL
)
137 throw "snapshot not created";
139 strcpy(buf
, fRootDir
);
143 // implicitly create the subdirectory
144 mode_t mode
= fRecordKext
? (S_IRUSR
|S_IWUSR
|S_IXUSR
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) : (S_IRUSR
|S_IWUSR
|S_IXUSR
);
149 strcat(buf
, basename((char *)file
));
153 // Construct a unique path string in the snapshot. If a path collision is detected then uniquing
154 // is accomplished by appending a counter to the path until there is no preexisting file.
155 // subdir - an optional subdirectory name
156 // file - the file name
157 void Snapshot::buildUniquePath(char *buf
, const char *subdir
, const char *file
)
159 buildPath(buf
, subdir
, file
);
161 if (!fRecordKext
&& (stat(buf
, &st
)==0)) {
164 char *number
= strrchr(buf
, 0);
168 sprintf(number
, "%d", counter
++);
169 } while (stat(buf
, &st
) == 0);
173 const char * Snapshot::subdir(const char *subdir
)
181 // Copy a file to the snapshot.
182 // sourcePath is the original file
183 // subdir is an optional subdirectory in the snapshot
184 // path is an optional out parameter containing the final uniqued path in the snapshot
185 // where the file was copied
186 void Snapshot::copyFileToSnapshot(const char *sourcePath
, const char *subdir
, char *path
)
188 const int copyBufSize
=(1<<14); // 16kb buffer
189 static void *copyBuf
= NULL
;
193 for (const char* sdkPath
: fOptions
->sdkPaths()) {
194 const char *toolchainPath
;
195 inSdk
= (!strncmp(sdkPath
, sourcePath
, strlen(sdkPath
)));
196 if (!inSdk
&& (toolchainPath
= fOptions
->toolchainPath()))
197 inSdk
= (!strncmp(toolchainPath
, sourcePath
, strlen(toolchainPath
)));
200 strcpy(path
, sourcePath
);
208 copyBuf
= malloc(copyBufSize
);
210 char *file
=basename((char *)sourcePath
);
212 if (path
== NULL
) path
= buf
;
213 buildUniquePath(path
, subdir
, file
);
214 mode_t mode
= fRecordKext
? (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) : (S_IRUSR
|S_IWUSR
);
215 int out_fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
, mode
);
216 int in_fd
= open(sourcePath
, O_RDONLY
);
218 if (out_fd
!= -1 && in_fd
!= -1) {
220 len
= read(in_fd
, copyBuf
, copyBufSize
);
221 if (len
> 0) write(out_fd
, copyBuf
, len
);
222 } while (len
== copyBufSize
);
227 const char * relPath
= snapshotRelativePath(path
);
228 memmove(path
, relPath
, 1+strlen(relPath
));
231 // Create the snapshot root directory.
232 void Snapshot::createSnapshot()
234 if (fRootDir
== NULL
) {
236 mode_t mask
= umask(0);
238 // provide default name and location
240 if (fSnapshotLocation
== NULL
)
241 fSnapshotLocation
= "/tmp";
244 fRootDir
= (char *)fSnapshotLocation
;
245 buildUniquePath(buf
, NULL
, fSnapshotName
);
246 fRootDir
= strdup(buf
);
248 int mkpatherr
= mkpath_np(fRootDir
, (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
|S_IXUSR
|S_IXGRP
|S_IXOTH
));
249 if ((mkpatherr
!=0) && !(fRecordKext
&& (mkpatherr
==EEXIST
))) {
250 warning("unable to create link snapshot directory: %s", fRootDir
);
252 setSnapshotMode(SNAPSHOT_DISABLED
); // don't try to write anything if we can't create snapshot dir
256 buildPath(buf
, NULL
, compileFileString
);
257 mode_t mode
= fRecordKext
? (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) : (S_IXUSR
|S_IRUSR
|S_IWUSR
);
258 int compileScript
= open(buf
, O_WRONLY
|O_CREAT
|O_TRUNC
, mode
);
259 write(compileScript
, compile_stubs
, strlen(compile_stubs
));
260 close(compileScript
);
263 SnapshotLog::iterator it
;
264 for (it
= fLog
.begin(); it
!= fLog
.end(); it
++) {
265 void (^logItem
)(void) = *it
;
267 Block_release(logItem
);
269 fLog
.erase(fLog
.begin(), fLog
.end());
272 writeCommandLine(true);
276 #if STORE_PID_IN_SNAPSHOT
278 buildUniquePath(path
, NULL
, pidString
);
279 mode
= fRecordKext
? (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) : (S_IRUSR
|S_IWUSR
);
280 int pidfile
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
, mode
);
282 sprintf(pid_buf
, "%lu\n", (long unsigned)getpid());
283 write(pidfile
, pid_buf
, strlen(pid_buf
));
284 write(pidfile
, "\n", 1);
292 // Write the current command line vector to filename.
293 void Snapshot::writeCommandLine(bool rawArgs
)
295 StringVector
&args
= rawArgs
? fRawArgs
: fArgs
;
296 const char *filename
;
300 filename
= origCommandLineString
;
303 filename
= linkCommandString
;
306 if (!isLazy() && fRecordArgs
) {
309 buildPath(path
, NULL
, filename
);
311 mode_t mode
= fRecordKext
? (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) : (S_IXUSR
|S_IRUSR
|S_IWUSR
);
312 int argsFile
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
, mode
);
313 FILE *argsStream
= fdopen(argsFile
, "w");
316 fprintf(argsStream
, "cd %s\n", getcwd(path
, sizeof(path
)));
318 // iterate to write args, quoting as needed
323 for (idx
= idxidx
= 0; idx
< args
.size(); idx
++) {
324 const char *arg
= args
[idx
];
325 bool needQuotes
= false;
327 if (fRecordKext
&& !rawArgs
) {
328 if (idx
== fArgIndicies
[idxidx
]) {
331 fprintf(argsStream
, "\n");
336 for (const char *c
= arg
; *c
!= 0 && !needQuotes
; c
++) {
340 if (inner
) fprintf(argsStream
, " ");
342 if (needQuotes
) fprintf(argsStream
, "\"");
343 fprintf(argsStream
, "%s", arg
);
344 if (needQuotes
) fprintf(argsStream
, "\"");
346 fprintf(argsStream
, "\n");
352 // Store the command line args in the snapshot.
353 void Snapshot::recordRawArgs(int argc
, const char *argv
[])
355 // first store the original command line as-is
356 for (int i
=0; i
<argc
; i
++) {
357 fRawArgs
.push_back(argv
[i
]);
359 fArgIndicies
.push_back(fArgs
.size());
360 fArgs
.insert(fArgs
.begin(), argv
[0]);
361 fArgIndicies
.push_back(fArgs
.size());
362 fArgs
.insert(fArgs
.begin()+1, "-Z"); // don't search standard paths when running in the snapshot
366 // Adds one or more args to the snapshot link command.
367 // argIndex is the index in the original raw args vector to start adding args
368 // argCount is the count of args to copy from the raw args vector
369 // fileArg is the index relative to argIndex of a file arg. The file is copied into the
370 // snapshot and the path is fixed up in the snapshot link command. (skipped if fileArg==-1)
371 void Snapshot::addSnapshotLinkArg(int argIndex
, int argCount
, int fileArg
)
373 if (fRootDir
== NULL
) {
374 fLog
.push_back(Block_copy(^{ this->addSnapshotLinkArg(argIndex
, argCount
, fileArg
); }));
377 fArgIndicies
.push_back(fArgs
.size());
378 for (int i
=0, arg
=argIndex
; i
<argCount
&& argIndex
+1<(int)fRawArgs
.size(); i
++, arg
++) {
380 fArgs
.push_back(fRawArgs
[arg
]);
382 if (fRecordDataFiles
) {
383 copyFileToSnapshot(fRawArgs
[arg
], subdir(dataFilesString
), buf
);
384 fArgs
.push_back(strdup(buf
));
386 // if we don't copy the file then just record the original path
387 fArgs
.push_back(strdup(fRawArgs
[arg
]));
394 // Record the -arch string
395 void Snapshot::recordArch(const char *arch
)
397 // must be called after recordRawArgs()
398 if (fRawArgs
.size() == 0)
399 throw "raw args not set";
401 fArchString
= strdup(arch
);
403 // only need to add the arch argument explicitly if it is not mentioned on the command line
404 bool archInArgs
= false;
405 StringVector::iterator it
;
406 for (it
= fRawArgs
.begin(); it
!= fRawArgs
.end() && !archInArgs
; it
++) {
407 const char *arg
= *it
;
408 if (strcmp(arg
, "-arch") == 0)
413 if (fRootDir
== NULL
) {
414 fLog
.push_back(Block_copy(^{ this->recordArch(arch
); }));
416 char path_buf
[PATH_MAX
];
417 buildUniquePath(path_buf
, NULL
, "arch");
418 mode_t mode
= fRecordKext
? (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) : (S_IRUSR
|S_IWUSR
);
419 int fd
=open(path_buf
, O_WRONLY
|O_CREAT
|O_TRUNC
, mode
);
420 write(fd
, arch
, strlen(arch
));
426 // Record an object file in the snapshot.
427 // path - the object file's path
428 // fileContent - a pointer to the object file content
429 // fileLength - the buffer size of fileContent
430 void Snapshot::recordObjectFile(const char *path
)
432 if (fRootDir
== NULL
) {
433 fLog
.push_back(Block_copy(^{ this->recordObjectFile(path
); }));
435 if (fRecordObjects
) {
436 char path_buf
[PATH_MAX
];
437 copyFileToSnapshot(path
, subdir(objectsString
), path_buf
);
439 // lazily open the filelist file
440 if (fFilelistFile
== -1) {
441 char filelist_path
[PATH_MAX
];
443 dir
= (fRecordKext
? NULL
: subdir(objectsString
));
444 buildUniquePath(filelist_path
, dir
, "filelist");
445 mode_t mode
= fRecordKext
? (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) : (S_IRUSR
|S_IWUSR
);
446 fFilelistFile
= open(filelist_path
, O_WRONLY
|O_CREAT
|O_TRUNC
, mode
);
449 fArgIndicies
.push_back(fArgs
.size());
450 fArgs
.push_back("-filelist");
451 fArgs
.push_back(strdup(snapshotRelativePath(filelist_path
)));
456 // record the snapshot path in the filelist
457 write(fFilelistFile
, path_buf
, strlen(path_buf
));
458 write(fFilelistFile
, "\n", 1);
463 void Snapshot::addFrameworkArg(const char *framework
)
466 for (unsigned i
=0; i
<fArgs
.size()-1; i
++) {
467 if (strcmp(fArgs
[i
], "-framework") == 0 && strcmp(fArgs
[i
+1], framework
) == 0)
471 if (!fFrameworkArgAdded
) {
472 fFrameworkArgAdded
= true;
473 fArgIndicies
.push_back(fArgs
.size());
474 fArgs
.push_back("-Fframeworks");
476 fArgIndicies
.push_back(fArgs
.size());
477 fArgs
.push_back("-framework");
478 fArgs
.push_back(strdup(framework
));
483 void Snapshot::addDylibArg(const char *dylib
)
486 for (unsigned i
=0; i
<fArgs
.size()-1; i
++) {
487 if (strcmp(fArgs
[i
], dylib
) == 0)
492 sprintf(buf
, "%s/%s", subdir(dylibsString
), dylib
);
493 fArgIndicies
.push_back(fArgs
.size());
494 fArgs
.push_back(strdup(buf
));
499 // Record a dylib symbol reference in the snapshot.
500 // (References are not written to the snapshot until writeStubDylibs() is called.)
501 void Snapshot::recordDylibSymbol(ld::dylib::File
* dylibFile
, const char *name
)
503 if (fRootDir
== NULL
) {
504 fLog
.push_back(Block_copy(^{ this->recordDylibSymbol(dylibFile
, name
); }));
506 if (fRecordDylibSymbols
) {
507 // find the dylib in the table
508 DylibMap::iterator it
;
509 const char *dylibPath
= dylibFile
->path();
511 it
= fDylibSymbols
.find(dylibPath
);
512 bool isFramework
= (strstr(dylibPath
, "framework") != NULL
);
514 if (it
== fDylibSymbols
.end()) {
515 // Didn't find a file descriptor for this dylib. Create one and add it to the dylib map.
516 char path_buf
[PATH_MAX
];
517 buildUniquePath(path_buf
, subdir(isFramework
? frameworkStubsString
: dylibStubsString
), dylibPath
);
519 mode_t mode
= fRecordKext
? (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) : (S_IRUSR
|S_IWUSR
);
520 dylibFd
= open(path_buf
, O_WRONLY
|O_APPEND
|O_CREAT
, mode
);
521 fDylibSymbols
.insert(std::pair
<const char *, int>(dylibPath
, dylibFd
));
522 char *base_name
= strdup(basename(path_buf
));
524 addFrameworkArg(base_name
);
526 addDylibArg(base_name
);
530 dylibFd
= it
->second
;
532 // Record the symbol.
534 bool isIdentifier
= (name
[0] == '_');
535 for (const char *c
= name
; *c
!= 0 && isIdentifier
; c
++)
536 if (!isalnum(*c
) && *c
!='_')
537 isIdentifier
= false;
538 const char *prefix
= "void ";
539 const char *weakAttr
= "__attribute__ ((weak)) ";
540 const char *suffix
= "(void){}\n";
542 write(dylibFd
, prefix
, strlen(prefix
));
543 if (dylibFile
->hasWeakExternals() && dylibFile
->hasWeakDefinition(name
))
544 write(dylibFd
, weakAttr
, strlen(weakAttr
));
545 if (*name
== '_') name
++;
546 write(dylibFd
, name
, strlen(name
));
547 write(dylibFd
, suffix
, strlen(suffix
));
549 static int symbolCounter
= 0;
550 char buf
[64+strlen(name
)];
551 sprintf(buf
, "void s_%5.5d(void) __asm(\"%s\");\nvoid s_%5.5d(){}\n", symbolCounter
, name
, symbolCounter
);
552 write(dylibFd
, buf
, strlen(buf
));
560 // Record a .a archive in the snapshot.
561 void Snapshot::recordArchive(const char *archiveFile
)
563 if (fRootDir
== NULL
) {
564 const char *copy
= strdup(archiveFile
);
565 fLog
.push_back(Block_copy(^{ this->recordArchive(archiveFile
); ::free((void *)copy
); }));
567 if (fRecordArchiveFiles
) {
569 // lazily create a vector of .a files that have been added
570 if (fCopiedArchives
== NULL
) {
571 fCopiedArchives
= new StringVector
;
574 // See if we have already added this .a
575 StringVector::iterator it
;
577 for (it
= fCopiedArchives
->begin(); it
!= fCopiedArchives
->end() && !found
; it
++) {
578 if (strcmp(archiveFile
, *it
) == 0)
582 // If this is a new .a then copy it to the snapshot and add it to the snapshot link command.
585 fCopiedArchives
->push_back(archiveFile
);
588 recordObjectFile(archiveFile
);
590 copyFileToSnapshot(archiveFile
, subdir(archiveFilesString
), path
);
591 fArgIndicies
.push_back(fArgs
.size());
592 fArgs
.push_back(strdup(path
));
600 void Snapshot::recordSubUmbrella(const char *frameworkPath
)
602 if (fRootDir
== NULL
) {
603 const char *copy
= strdup(frameworkPath
);
604 fLog
.push_back(Block_copy(^{ this->recordSubUmbrella(copy
); ::free((void *)copy
); }));
606 if (fRecordUmbrellaFiles
) {
607 const char *framework
= basename((char *)frameworkPath
);
608 char buf
[PATH_MAX
], wrapper
[PATH_MAX
];
609 strcpy(wrapper
, subdir(frameworksString
));
610 buildPath(buf
, wrapper
, NULL
); // ensure the frameworks directory exists
611 strcat(wrapper
, "/");
612 strcat(wrapper
, framework
);
613 strcat(wrapper
, ".framework");
614 copyFileToSnapshot(frameworkPath
, wrapper
);
615 addFrameworkArg(framework
);
620 void Snapshot::recordSubLibrary(const char *dylibPath
)
622 if (fRootDir
== NULL
) {
623 const char *copy
= strdup(dylibPath
);
624 fLog
.push_back(Block_copy(^{ this->recordSubLibrary(copy
); ::free((void *)copy
); }));
626 if (fRecordUmbrellaFiles
) {
627 copyFileToSnapshot(dylibPath
, subdir(dylibsString
));
628 addDylibArg(basename((char *)dylibPath
));
633 void Snapshot::recordAssertionMessage(const char *fmt
, ...)
638 vasprintf(&msg
, fmt
, args
);
641 if (fRootDir
== NULL
) {
642 fLog
.push_back(Block_copy(^{ this->recordAssertionMessage("%s", msg
); free(msg
); }));
645 buildPath(path
, NULL
, assertFileString
);
646 mode_t mode
= fRecordKext
? (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) : (S_IRUSR
|S_IWUSR
);
647 int log
= open(path
, O_WRONLY
|O_APPEND
|O_CREAT
, mode
);
648 write(log
, msg
, strlen(msg
));