]> git.saurik.com Git - apple/ld64.git/blob - src/ld/Snapshot.cpp
ld64-409.12.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 <errno.h>
12 #include <stdio.h>
13 #include <limits.h>
14 #include <fcntl.h>
15 #include <ctype.h>
16 #include <sys/stat.h>
17 #include <libgen.h>
18 #include <time.h>
19 #include <Block.h>
20
21 #include "Snapshot.h"
22 #include "Options.h"
23
24 #include "compile_stubs.h"
25
26 //#define STORE_PID_IN_SNAPSHOT 1
27
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
42
43 Snapshot *Snapshot::globalSnapshot = NULL;
44
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)
46 {
47 if (globalSnapshot != NULL)
48 throw "only one snapshot supported";
49 globalSnapshot = this;
50 }
51
52
53 Snapshot::~Snapshot()
54 {
55 // Lots of things leak under the assumption the linker is about to exit.
56 }
57
58
59 void Snapshot::setSnapshotPath(const char *path)
60 {
61 if (fRootDir == NULL) {
62 fSnapshotLocation = strdup(path);
63 }
64 }
65
66
67 void Snapshot::setSnapshotMode(SnapshotMode mode)
68 {
69 if (fRootDir == NULL) {
70 // free stuff
71 fRootDir = NULL;
72 }
73
74 if (fRootDir == NULL) {
75 fRecordArgs = false;
76 fRecordObjects = false;
77 fRecordDylibSymbols = false;
78 fRecordArchiveFiles = false;
79 fRecordUmbrellaFiles = false;
80 fRecordDataFiles = false;
81 fRecordKext = false;
82
83 switch (mode) {
84 case SNAPSHOT_DISABLED:
85 break;
86 case SNAPSHOT_DEBUG:
87 fRecordArgs = fRecordObjects = fRecordDylibSymbols = fRecordArchiveFiles = fRecordUmbrellaFiles = fRecordDataFiles = true;
88 break;
89 case SNAPSHOT_KEXT:
90 fRecordKext = fRecordArgs = fRecordObjects = fRecordDylibSymbols = fRecordArchiveFiles = fRecordUmbrellaFiles = fRecordDataFiles = true;
91 break;
92 default:
93 break;
94 }
95 }
96 }
97
98 void Snapshot::setOutputPath(const char *path)
99 {
100 fOutputPath = strdup(path);
101 }
102
103 void Snapshot::setSnapshotName()
104 {
105 if (fRootDir == NULL) {
106 if (fOutputPath == NULL) {
107 fSnapshotName = strdup("ld_snapshot");
108 } else {
109 const char *base = basename((char *)fOutputPath);
110 if (fRecordKext) {
111 const char *kextobjects;
112 if ((kextobjects = fOptions->kextObjectsPath())) {
113 fSnapshotLocation = strdup(kextobjects);
114 } else {
115 fSnapshotLocation = strdup(dirname((char *)fOutputPath));
116 }
117 asprintf((char **)&fSnapshotName, "%s.%s.ld", base, fArchString);
118 } else {
119 time_t now = time(NULL);
120 struct tm t;
121 localtime_r(&now, &t);
122 char buf[PATH_MAX];
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);
125 }
126 }
127 }
128 }
129
130
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)
135 {
136 if (fRootDir == NULL)
137 throw "snapshot not created";
138
139 strcpy(buf, fRootDir);
140 strcat(buf, "/");
141 if (subdir) {
142 strcat(buf, subdir);
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);
145 mkdir(buf, mode);
146 strcat(buf, "/");
147 }
148 if (file != NULL)
149 strcat(buf, basename((char *)file));
150 }
151
152
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)
158 {
159 buildPath(buf, subdir, file);
160 struct stat st;
161 if (!fRecordKext && (stat(buf, &st)==0)) {
162 // make it unique
163 int counter=1;
164 char *number = strrchr(buf, 0);
165 number[0]='-';
166 number++;
167 do {
168 sprintf(number, "%d", counter++);
169 } while (stat(buf, &st) == 0);
170 }
171 }
172
173 const char * Snapshot::subdir(const char *subdir)
174 {
175 if (fRecordKext) {
176 return filesString;
177 }
178 return subdir;
179 }
180
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)
187 {
188 const int copyBufSize=(1<<14); // 16kb buffer
189 static void *copyBuf = NULL;
190 bool inSdk;
191
192 if (fRecordKext) {
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)));
198 if (inSdk) {
199 if (path) {
200 strcpy(path, sourcePath);
201 }
202 return;
203 }
204 }
205 }
206
207 if (copyBuf == NULL)
208 copyBuf = malloc(copyBufSize);
209
210 char *file=basename((char *)sourcePath);
211 char buf[PATH_MAX];
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);
217 int len;
218 if (out_fd != -1 && in_fd != -1) {
219 do {
220 len = read(in_fd, copyBuf, copyBufSize);
221 if (len > 0) write(out_fd, copyBuf, len);
222 } while (len == copyBufSize);
223 }
224 close(in_fd);
225 close(out_fd);
226
227 const char * relPath = snapshotRelativePath(path);
228 memmove(path, relPath, 1+strlen(relPath));
229 }
230
231 // Create the snapshot root directory.
232 void Snapshot::createSnapshot()
233 {
234 if (fRootDir == NULL) {
235
236 mode_t mask = umask(0);
237
238 // provide default name and location
239 setSnapshotName();
240 if (fSnapshotLocation == NULL)
241 fSnapshotLocation = "/tmp";
242
243 char buf[PATH_MAX];
244 fRootDir = (char *)fSnapshotLocation;
245 buildUniquePath(buf, NULL, fSnapshotName);
246 fRootDir = strdup(buf);
247
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);
251 fRootDir = NULL;
252 setSnapshotMode(SNAPSHOT_DISABLED); // don't try to write anything if we can't create snapshot dir
253 }
254
255 if (!fRecordKext) {
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);
261 }
262
263 SnapshotLog::iterator it;
264 for (it = fLog.begin(); it != fLog.end(); it++) {
265 void (^logItem)(void) = *it;
266 logItem();
267 Block_release(logItem);
268 }
269 fLog.erase(fLog.begin(), fLog.end());
270
271 if (fRecordArgs) {
272 writeCommandLine(true);
273 writeCommandLine();
274 }
275
276 #if STORE_PID_IN_SNAPSHOT
277 char path[PATH_MAX];
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);
281 char pid_buf[32];
282 sprintf(pid_buf, "%lu\n", (long unsigned)getpid());
283 write(pidfile, pid_buf, strlen(pid_buf));
284 write(pidfile, "\n", 1);
285 close(pidfile);
286 #endif
287 umask(mask);
288 }
289 }
290
291
292 // Write the current command line vector to filename.
293 void Snapshot::writeCommandLine(bool rawArgs)
294 {
295 StringVector &args = rawArgs ? fRawArgs : fArgs;
296 const char *filename;
297
298 if (rawArgs) {
299 args = fRawArgs;
300 filename = origCommandLineString;
301 } else {
302 args = fArgs;
303 filename = linkCommandString;
304 }
305
306 if (!isLazy() && fRecordArgs) {
307 // Open the file
308 char path[PATH_MAX];
309 buildPath(path, NULL, filename);
310
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");
314
315 if (rawArgs)
316 fprintf(argsStream, "cd %s\n", getcwd(path, sizeof(path)));
317
318 // iterate to write args, quoting as needed
319 unsigned idx;
320 unsigned idxidx;
321 bool inner = false;
322
323 for (idx = idxidx = 0; idx < args.size(); idx++) {
324 const char *arg = args[idx];
325 bool needQuotes = false;
326
327 if (fRecordKext && !rawArgs) {
328 if (idx == fArgIndicies[idxidx]) {
329 idxidx++;
330 if (idx > 0) {
331 fprintf(argsStream, "\n");
332 inner = false;
333 }
334 }
335 }
336 for (const char *c = arg; *c != 0 && !needQuotes; c++) {
337 if (isspace(*c))
338 needQuotes = true;
339 }
340 if (inner) fprintf(argsStream, " ");
341 inner = true;
342 if (needQuotes) fprintf(argsStream, "\"");
343 fprintf(argsStream, "%s", arg);
344 if (needQuotes) fprintf(argsStream, "\"");
345 }
346 fprintf(argsStream, "\n");
347 fclose(argsStream);
348 }
349 }
350
351
352 // Store the command line args in the snapshot.
353 void Snapshot::recordRawArgs(int argc, const char *argv[])
354 {
355 // first store the original command line as-is
356 for (int i=0; i<argc; i++) {
357 fRawArgs.push_back(argv[i]);
358 }
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
363 }
364
365
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)
372 {
373 if (fRootDir == NULL) {
374 fLog.push_back(Block_copy(^{ this->addSnapshotLinkArg(argIndex, argCount, fileArg); }));
375 } else {
376 char buf[PATH_MAX];
377 fArgIndicies.push_back(fArgs.size());
378 for (int i=0, arg=argIndex; i<argCount && argIndex+1<(int)fRawArgs.size(); i++, arg++) {
379 if (i != fileArg) {
380 fArgs.push_back(fRawArgs[arg]);
381 } else {
382 if (fRecordDataFiles) {
383 copyFileToSnapshot(fRawArgs[arg], subdir(dataFilesString), buf);
384 fArgs.push_back(strdup(buf));
385 } else {
386 // if we don't copy the file then just record the original path
387 fArgs.push_back(strdup(fRawArgs[arg]));
388 }
389 }
390 }
391 }
392 }
393
394 // Record the -arch string
395 void Snapshot::recordArch(const char *arch)
396 {
397 // must be called after recordRawArgs()
398 if (fRawArgs.size() == 0)
399 throw "raw args not set";
400
401 fArchString = strdup(arch);
402
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)
409 archInArgs = true;
410 }
411
412 if (!archInArgs) {
413 if (fRootDir == NULL) {
414 fLog.push_back(Block_copy(^{ this->recordArch(arch); }));
415 } else {
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));
421 close(fd);
422 }
423 }
424 }
425
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)
431 {
432 if (fRootDir == NULL) {
433 fLog.push_back(Block_copy(^{ this->recordObjectFile(path); }));
434 } else {
435 if (fRecordObjects) {
436 char path_buf[PATH_MAX];
437 copyFileToSnapshot(path, subdir(objectsString), path_buf);
438
439 // lazily open the filelist file
440 if (fFilelistFile == -1) {
441 char filelist_path[PATH_MAX];
442 const char * dir;
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);
447
448 if (!fRecordKext) {
449 fArgIndicies.push_back(fArgs.size());
450 fArgs.push_back("-filelist");
451 fArgs.push_back(strdup(snapshotRelativePath(filelist_path)));
452 writeCommandLine();
453 }
454 }
455
456 // record the snapshot path in the filelist
457 write(fFilelistFile, path_buf, strlen(path_buf));
458 write(fFilelistFile, "\n", 1);
459 }
460 }
461 }
462
463 void Snapshot::addFrameworkArg(const char *framework)
464 {
465 bool found=false;
466 for (unsigned i=0; i<fArgs.size()-1; i++) {
467 if (strcmp(fArgs[i], "-framework") == 0 && strcmp(fArgs[i+1], framework) == 0)
468 found = true;
469 }
470 if (!found) {
471 if (!fFrameworkArgAdded) {
472 fFrameworkArgAdded = true;
473 fArgIndicies.push_back(fArgs.size());
474 fArgs.push_back("-Fframeworks");
475 }
476 fArgIndicies.push_back(fArgs.size());
477 fArgs.push_back("-framework");
478 fArgs.push_back(strdup(framework));
479 writeCommandLine();
480 }
481 }
482
483 void Snapshot::addDylibArg(const char *dylib)
484 {
485 bool found=false;
486 for (unsigned i=0; i<fArgs.size()-1; i++) {
487 if (strcmp(fArgs[i], dylib) == 0)
488 found = true;
489 }
490 if (!found) {
491 char buf[ARG_MAX];
492 sprintf(buf, "%s/%s", subdir(dylibsString), dylib);
493 fArgIndicies.push_back(fArgs.size());
494 fArgs.push_back(strdup(buf));
495 writeCommandLine();
496 }
497 }
498
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)
502 {
503 if (fRootDir == NULL) {
504 fLog.push_back(Block_copy(^{ this->recordDylibSymbol(dylibFile, name); }));
505 } else {
506 if (fRecordDylibSymbols) {
507 // find the dylib in the table
508 DylibMap::iterator it;
509 const char *dylibPath = dylibFile->path();
510
511 it = fDylibSymbols.find(dylibPath);
512 bool isFramework = (strstr(dylibPath, "framework") != NULL);
513 int dylibFd;
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);
518
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));
523 if (isFramework) {
524 addFrameworkArg(base_name);
525 } else {
526 addDylibArg(base_name);
527 }
528 writeCommandLine();
529 } else {
530 dylibFd = it->second;
531 }
532 // Record the symbol.
533
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";
541 if (isIdentifier) {
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));
548 } else {
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));
553 symbolCounter++;
554 }
555 }
556 }
557 }
558
559
560 // Record a .a archive in the snapshot.
561 void Snapshot::recordArchive(const char *archiveFile)
562 {
563 if (fRootDir == NULL) {
564 const char *copy = strdup(archiveFile);
565 fLog.push_back(Block_copy(^{ this->recordArchive(archiveFile); ::free((void *)copy); }));
566 } else {
567 if (fRecordArchiveFiles) {
568
569 // lazily create a vector of .a files that have been added
570 if (fCopiedArchives == NULL) {
571 fCopiedArchives = new StringVector;
572 }
573
574 // See if we have already added this .a
575 StringVector::iterator it;
576 bool found = false;
577 for (it = fCopiedArchives->begin(); it != fCopiedArchives->end() && !found; it++) {
578 if (strcmp(archiveFile, *it) == 0)
579 found = true;
580 }
581
582 // If this is a new .a then copy it to the snapshot and add it to the snapshot link command.
583 if (!found) {
584 char path[PATH_MAX];
585 fCopiedArchives->push_back(archiveFile);
586
587 if (fRecordKext) {
588 recordObjectFile(archiveFile);
589 } else {
590 copyFileToSnapshot(archiveFile, subdir(archiveFilesString), path);
591 fArgIndicies.push_back(fArgs.size());
592 fArgs.push_back(strdup(path));
593 writeCommandLine();
594 }
595 }
596 }
597 }
598 }
599
600 void Snapshot::recordSubUmbrella(const char *frameworkPath)
601 {
602 if (fRootDir == NULL) {
603 const char *copy = strdup(frameworkPath);
604 fLog.push_back(Block_copy(^{ this->recordSubUmbrella(copy); ::free((void *)copy); }));
605 } else {
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);
616 }
617 }
618 }
619
620 void Snapshot::recordSubLibrary(const char *dylibPath)
621 {
622 if (fRootDir == NULL) {
623 const char *copy = strdup(dylibPath);
624 fLog.push_back(Block_copy(^{ this->recordSubLibrary(copy); ::free((void *)copy); }));
625 } else {
626 if (fRecordUmbrellaFiles) {
627 copyFileToSnapshot(dylibPath, subdir(dylibsString));
628 addDylibArg(basename((char *)dylibPath));
629 }
630 }
631 }
632
633 void Snapshot::recordAssertionMessage(const char *fmt, ...)
634 {
635 char *msg;
636 va_list args;
637 va_start(args, fmt);
638 vasprintf(&msg, fmt, args);
639 va_end(args);
640 if (msg != NULL) {
641 if (fRootDir == NULL) {
642 fLog.push_back(Block_copy(^{ this->recordAssertionMessage("%s", msg); free(msg); }));
643 } else {
644 char path[PATH_MAX];
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));
649 close(log);
650 free(msg);
651 }
652 }
653 }