]>
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.
23 #include "compile_stubs.h"
25 //#define STORE_PID_IN_SNAPSHOT 1
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
41 Snapshot
*Snapshot::globalSnapshot
= NULL
;
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
)
45 if (globalSnapshot
!= NULL
)
46 throw "only one snapshot supported";
47 globalSnapshot
= this;
53 // Lots of things leak under the assumption the linker is about to exit.
57 void Snapshot::setSnapshotPath(const char *path
)
59 if (fRootDir
== NULL
) {
60 fSnapshotLocation
= strdup(path
);
65 void Snapshot::setSnapshotMode(SnapshotMode mode
)
67 if (fRootDir
== NULL
) {
69 fRecordObjects
= false;
70 fRecordDylibSymbols
= false;
71 fRecordArchiveFiles
= false;
72 fRecordUmbrellaFiles
= false;
73 fRecordDataFiles
= false;
76 case SNAPSHOT_DISABLED
:
79 fRecordArgs
= fRecordObjects
= fRecordDylibSymbols
= fRecordArchiveFiles
= fRecordUmbrellaFiles
= fRecordDataFiles
= true;
87 void Snapshot::setSnapshotName(const char *path
)
89 if (fRootDir
== NULL
) {
90 const char *base
= basename((char *)path
);
91 time_t now
= time(NULL
);
93 localtime_r(&now
, &t
);
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
);
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
)
106 if (fRootDir
== NULL
)
107 throw "snapshot not created";
109 strcpy(buf
, fRootDir
);
113 // implicitly create the subdirectory
114 mkdir(buf
, S_IRUSR
|S_IWUSR
|S_IXUSR
);
118 strcat(buf
, basename((char *)file
));
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
)
128 buildPath(buf
, subdir
, file
);
130 if (stat(buf
, &st
)==0) {
133 char *number
= strrchr(buf
, 0);
137 sprintf(number
, "%d", counter
++);
138 } while (stat(buf
, &st
) == 0);
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
)
150 const int copyBufSize
=(1<<14); // 16kb buffer
151 static void *copyBuf
= NULL
;
153 copyBuf
= malloc(copyBufSize
);
155 char *file
=basename((char *)sourcePath
);
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
);
162 if (out_fd
!= -1 && in_fd
!= -1) {
164 len
= read(in_fd
, copyBuf
, copyBufSize
);
165 if (len
> 0) write(out_fd
, copyBuf
, len
);
166 } while (len
== copyBufSize
);
173 // Create the snapshot root directory.
174 void Snapshot::createSnapshot()
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");
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
);
191 setSnapshotMode(SNAPSHOT_DISABLED
); // don't try to write anything if we can't create snapshot dir
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
);
199 SnapshotLog::iterator it
;
200 for (it
= fLog
.begin(); it
!= fLog
.end(); it
++) {
201 void (^logItem
)(void) = *it
;
203 Block_release(logItem
);
205 fLog
.erase(fLog
.begin(), fLog
.end());
208 writeCommandLine(fRawArgs
, origCommandLineString
, true);
209 writeCommandLine(fArgs
);
212 #if STORE_PID_IN_SNAPSHOT
214 buildUniquePath(path
, NULL
, pidString
);
215 int pidfile
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
, S_IRUSR
|S_IWUSR
);
217 sprintf(pid_buf
, "%lu\n", (long unsigned)getpid());
218 write(pidfile
, pid_buf
, strlen(pid_buf
));
219 write(pidfile
, "\n", 1);
227 // Write the current command line vector to filename.
228 void Snapshot::writeCommandLine(StringVector
&args
, const char *filename
, bool includeCWD
)
230 if (!isLazy() && fRecordArgs
) {
231 // Figure out the file name and open it.
232 if (filename
== NULL
)
233 filename
= linkCommandString
;
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");
240 fprintf(argsStream
, "cd %s\n", getcwd(path
, sizeof(path
)));
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
++) {
251 if (it
!= args
.begin()) fprintf(argsStream
, " ");
252 if (needQuotes
) fprintf(argsStream
, "\"");
253 fprintf(argsStream
, "%s", arg
);
254 if (needQuotes
) fprintf(argsStream
, "\"");
256 fprintf(argsStream
, "\n");
262 // Store the command line args in the snapshot.
263 void Snapshot::recordRawArgs(int argc
, const char *argv
[])
265 // first store the original command line as-is
266 for (int i
=0; i
<argc
; i
++) {
267 fRawArgs
.push_back(argv
[i
]);
269 fArgs
.insert(fArgs
.begin(), argv
[0]);
270 fArgs
.insert(fArgs
.begin()+1, "-Z"); // don't search standard paths when running in the snapshot
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
)
281 if (fRootDir
== NULL
) {
282 fLog
.push_back(Block_copy(^{ this->addSnapshotLinkArg(argIndex
, argCount
, fileArg
); }));
285 const char *subdir
= dataFilesString
;
286 for (int i
=0, arg
=argIndex
; i
<argCount
&& argIndex
+1<(int)fRawArgs
.size(); i
++, arg
++) {
288 fArgs
.push_back(fRawArgs
[arg
]);
290 if (fRecordDataFiles
) {
291 copyFileToSnapshot(fRawArgs
[arg
], subdir
, buf
);
292 fArgs
.push_back(strdup(snapshotRelativePath(buf
)));
294 // if we don't copy the file then just record the original path
295 fArgs
.push_back(strdup(fRawArgs
[arg
]));
302 // Record the -arch string
303 void Snapshot::recordArch(const char *arch
)
305 // must be called after recordRawArgs()
306 if (fRawArgs
.size() == 0)
307 throw "raw args not set";
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)
319 if (fRootDir
== NULL
) {
320 fLog
.push_back(Block_copy(^{ this->recordArch(arch
); }));
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
));
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
)
337 if (fRootDir
== NULL
) {
338 fLog
.push_back(Block_copy(^{ this->recordObjectFile(path
); }));
340 if (fRecordObjects
) {
341 char path_buf
[PATH_MAX
];
342 copyFileToSnapshot(path
, objectsString
, path_buf
);
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
);
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);
362 void Snapshot::addFrameworkArg(const char *framework
)
365 for (unsigned i
=0; i
<fArgs
.size()-1; i
++) {
366 if (strcmp(fArgs
[i
], "-framework") == 0 && strcmp(fArgs
[i
+1], framework
) == 0)
370 if (!fFrameworkArgAdded
) {
371 fFrameworkArgAdded
= true;
372 fArgs
.push_back("-Fframeworks");
374 fArgs
.push_back("-framework");
375 fArgs
.push_back(strdup(framework
));
376 writeCommandLine(fArgs
);
380 void Snapshot::addDylibArg(const char *dylib
)
383 for (unsigned i
=0; i
<fArgs
.size()-1; i
++) {
384 if (strcmp(fArgs
[i
], dylib
) == 0)
389 sprintf(buf
, "%s/%s", dylibsString
, dylib
);
390 fArgs
.push_back(strdup(buf
));
391 writeCommandLine(fArgs
);
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
)
399 if (fRootDir
== NULL
) {
400 fLog
.push_back(Block_copy(^{ this->recordDylibSymbol(dylibFile
, name
); }));
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
);
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
));
417 addFrameworkArg(base_name
);
419 addDylibArg(base_name
);
421 writeCommandLine(fArgs
);
423 dylibFd
= it
->second
;
425 // Record the symbol.
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";
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
));
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
));
453 // Record a .a archive in the snapshot.
454 void Snapshot::recordArchive(const char *archiveFile
)
456 if (fRootDir
== NULL
) {
457 const char *copy
= strdup(archiveFile
);
458 fLog
.push_back(Block_copy(^{ this->recordArchive(archiveFile
); ::free((void *)copy
); }));
460 if (fRecordArchiveFiles
) {
461 // lazily create a vector of .a files that have been added
462 if (fCopiedArchives
== NULL
) {
463 fCopiedArchives
= new StringVector
;
466 // See if we have already added this .a
467 StringVector::iterator it
;
469 for (it
= fCopiedArchives
->begin(); it
!= fCopiedArchives
->end() && !found
; it
++) {
470 if (strcmp(archiveFile
, *it
) == 0)
474 // If this is a new .a then copy it to the snapshot and add it to the snapshot link command.
477 fCopiedArchives
->push_back(archiveFile
);
478 copyFileToSnapshot(archiveFile
, archiveFilesString
, path
);
479 fArgs
.push_back(strdup(snapshotRelativePath(path
)));
480 writeCommandLine(fArgs
);
486 void Snapshot::recordSubUmbrella(const char *frameworkPath
)
488 if (fRootDir
== NULL
) {
489 const char *copy
= strdup(frameworkPath
);
490 fLog
.push_back(Block_copy(^{ this->recordSubUmbrella(copy
); ::free((void *)copy
); }));
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
);
506 void Snapshot::recordSubLibrary(const char *dylibPath
)
508 if (fRootDir
== NULL
) {
509 const char *copy
= strdup(dylibPath
);
510 fLog
.push_back(Block_copy(^{ this->recordSubLibrary(copy
); ::free((void *)copy
); }));
512 if (fRecordUmbrellaFiles
) {
513 copyFileToSnapshot(dylibPath
, dylibsString
);
514 addDylibArg(basename((char *)dylibPath
));
519 void Snapshot::recordAssertionMessage(const char *fmt
, ...)
524 vasprintf(&msg
, fmt
, args
);
527 if (fRootDir
== NULL
) {
528 fLog
.push_back(Block_copy(^{ this->recordAssertionMessage("%s", msg
); free(msg
); }));
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
));