]>
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
));