blob: 1c453ee0b569dcf5cf3631e41e9a564ce59a2d93 [file] [log] [blame]
Fangrui Songa8aac852018-10-26 17:38:27 +00001//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Builds up (relatively) standard unix archive files (.a) containing LLVM
11// bitcode or other files.
12//
13//===----------------------------------------------------------------------===//
14
15#include "llvm/ADT/StringSwitch.h"
16#include "llvm/ADT/Triple.h"
17#include "llvm/IR/LLVMContext.h"
18#include "llvm/Object/Archive.h"
19#include "llvm/Object/ArchiveWriter.h"
20#include "llvm/Object/MachO.h"
21#include "llvm/Object/ObjectFile.h"
22#include "llvm/Support/Chrono.h"
23#include "llvm/Support/CommandLine.h"
24#include "llvm/Support/Errc.h"
25#include "llvm/Support/FileSystem.h"
26#include "llvm/Support/Format.h"
27#include "llvm/Support/FormatVariadic.h"
28#include "llvm/Support/InitLLVM.h"
29#include "llvm/Support/LineIterator.h"
30#include "llvm/Support/MemoryBuffer.h"
31#include "llvm/Support/Path.h"
32#include "llvm/Support/Process.h"
33#include "llvm/Support/StringSaver.h"
34#include "llvm/Support/TargetSelect.h"
35#include "llvm/Support/ToolOutputFile.h"
Jordan Rupprecht913cfad2019-01-15 21:52:31 +000036#include "llvm/Support/WithColor.h"
Fangrui Songa8aac852018-10-26 17:38:27 +000037#include "llvm/Support/raw_ostream.h"
38#include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"
39#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
40
41#if !defined(_MSC_VER) && !defined(__MINGW32__)
42#include <unistd.h>
43#else
44#include <io.h>
45#endif
46
47using namespace llvm;
48
49// The name this program was invoked as.
50static StringRef ToolName;
51
52// The basename of this program.
53static StringRef Stem;
54
55const char RanlibHelp[] = R"(
56OVERVIEW: LLVM Ranlib (llvm-ranlib)
57
58 This program generates an index to speed access to archives
59
60USAGE: llvm-ranlib <archive-file>
61
62OPTIONS:
63 -help - Display available options
64 -version - Display the version of this program
65)";
66
67const char ArHelp[] = R"(
68OVERVIEW: LLVM Archiver
69
70USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] <archive> [files]
71 llvm-ar -M [<mri-script]
72
73OPTIONS:
74 --format - Archive format to create
75 =default - default
76 =gnu - gnu
77 =darwin - darwin
78 =bsd - bsd
79 --plugin=<string> - Ignored for compatibility
80 --help - Display available options
81 --version - Display the version of this program
82
83OPERATIONS:
84 d - delete [files] from the archive
85 m - move [files] in the archive
86 p - print [files] found in the archive
87 q - quick append [files] to the archive
88 r - replace or insert [files] into the archive
89 s - act as ranlib
90 t - display contents of archive
91 x - extract [files] from the archive
92
93MODIFIERS:
94 [a] - put [files] after [relpos]
95 [b] - put [files] before [relpos] (same as [i])
96 [c] - do not warn if archive had to be created
97 [D] - use zero for timestamps and uids/gids (default)
98 [i] - put [files] before [relpos] (same as [b])
99 [l] - ignored for compatibility
100 [L] - add archive's contents
101 [o] - preserve original dates
102 [s] - create an archive index (cf. ranlib)
103 [S] - do not build a symbol table
104 [T] - create a thin archive
105 [u] - update only [files] newer than archive contents
106 [U] - use actual timestamps and uids/gids
107 [v] - be verbose about actions taken
108)";
109
110void printHelpMessage() {
111 if (Stem.contains_lower("ranlib"))
112 outs() << RanlibHelp;
113 else if (Stem.contains_lower("ar"))
114 outs() << ArHelp;
115}
116
117// Show the error message and exit.
118LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) {
Jordan Rupprecht913cfad2019-01-15 21:52:31 +0000119 WithColor::error(errs(), ToolName) << Error << ".\n";
Fangrui Songa8aac852018-10-26 17:38:27 +0000120 printHelpMessage();
121 exit(1);
122}
123
124static void failIfError(std::error_code EC, Twine Context = "") {
125 if (!EC)
126 return;
127
128 std::string ContextStr = Context.str();
Jordan Rupprechtf6ea1292018-12-20 00:57:06 +0000129 if (ContextStr.empty())
Fangrui Songa8aac852018-10-26 17:38:27 +0000130 fail(EC.message());
131 fail(Context + ": " + EC.message());
132}
133
134static void failIfError(Error E, Twine Context = "") {
135 if (!E)
136 return;
137
138 handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
139 std::string ContextStr = Context.str();
Jordan Rupprechtf6ea1292018-12-20 00:57:06 +0000140 if (ContextStr.empty())
Fangrui Songa8aac852018-10-26 17:38:27 +0000141 fail(EIB.message());
142 fail(Context + ": " + EIB.message());
143 });
144}
145
146static SmallVector<const char *, 256> PositionalArgs;
147
148static bool MRI;
149
150namespace {
151enum Format { Default, GNU, BSD, DARWIN, Unknown };
152}
153
154static Format FormatType = Default;
155
156static std::string Options;
157
158// This enumeration delineates the kinds of operations on an archive
159// that are permitted.
160enum ArchiveOperation {
161 Print, ///< Print the contents of the archive
162 Delete, ///< Delete the specified members
163 Move, ///< Move members to end or as given by {a,b,i} modifiers
164 QuickAppend, ///< Quickly append to end of archive
165 ReplaceOrInsert, ///< Replace or Insert members
166 DisplayTable, ///< Display the table of contents
167 Extract, ///< Extract files back to file system
168 CreateSymTab ///< Create a symbol table in an existing archive
169};
170
171// Modifiers to follow operation to vary behavior
172static bool AddAfter = false; ///< 'a' modifier
173static bool AddBefore = false; ///< 'b' modifier
174static bool Create = false; ///< 'c' modifier
175static bool OriginalDates = false; ///< 'o' modifier
176static bool OnlyUpdate = false; ///< 'u' modifier
177static bool Verbose = false; ///< 'v' modifier
178static bool Symtab = true; ///< 's' modifier
179static bool Deterministic = true; ///< 'D' and 'U' modifiers
180static bool Thin = false; ///< 'T' modifier
181static bool AddLibrary = false; ///< 'L' modifier
182
183// Relative Positional Argument (for insert/move). This variable holds
184// the name of the archive member to which the 'a', 'b' or 'i' modifier
185// refers. Only one of 'a', 'b' or 'i' can be specified so we only need
186// one variable.
187static std::string RelPos;
188
189// This variable holds the name of the archive file as given on the
190// command line.
191static std::string ArchiveName;
192
193// This variable holds the list of member files to proecess, as given
194// on the command line.
195static std::vector<StringRef> Members;
196
197// Extract the member filename from the command line for the [relpos] argument
198// associated with a, b, and i modifiers
199static void getRelPos() {
Jordan Rupprechtf6ea1292018-12-20 00:57:06 +0000200 if (PositionalArgs.empty())
Fangrui Songa8aac852018-10-26 17:38:27 +0000201 fail("Expected [relpos] for a, b, or i modifier");
202 RelPos = PositionalArgs[0];
203 PositionalArgs.erase(PositionalArgs.begin());
204}
205
206// Get the archive file name from the command line
207static void getArchive() {
Jordan Rupprechtf6ea1292018-12-20 00:57:06 +0000208 if (PositionalArgs.empty())
Fangrui Songa8aac852018-10-26 17:38:27 +0000209 fail("An archive name must be specified");
210 ArchiveName = PositionalArgs[0];
211 PositionalArgs.erase(PositionalArgs.begin());
212}
213
214// Copy over remaining items in PositionalArgs to our Members vector
215static void getMembers() {
216 for (auto &Arg : PositionalArgs)
217 Members.push_back(Arg);
218}
219
220std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
221std::vector<std::unique_ptr<object::Archive>> Archives;
222
223static object::Archive &readLibrary(const Twine &Library) {
224 auto BufOrErr = MemoryBuffer::getFile(Library, -1, false);
Jordan Rupprecht913cfad2019-01-15 21:52:31 +0000225 failIfError(BufOrErr.getError(), "Could not open library " + Library);
Fangrui Songa8aac852018-10-26 17:38:27 +0000226 ArchiveBuffers.push_back(std::move(*BufOrErr));
227 auto LibOrErr =
228 object::Archive::create(ArchiveBuffers.back()->getMemBufferRef());
229 failIfError(errorToErrorCode(LibOrErr.takeError()),
230 "Could not parse library");
231 Archives.push_back(std::move(*LibOrErr));
232 return *Archives.back();
233}
234
235static void runMRIScript();
236
237// Parse the command line options as presented and return the operation
238// specified. Process all modifiers and check to make sure that constraints on
239// modifier/operation pairs have not been violated.
240static ArchiveOperation parseCommandLine() {
241 if (MRI) {
242 if (!PositionalArgs.empty() || !Options.empty())
243 fail("Cannot mix -M and other options");
244 runMRIScript();
245 }
246
247 // Keep track of number of operations. We can only specify one
248 // per execution.
249 unsigned NumOperations = 0;
250
251 // Keep track of the number of positional modifiers (a,b,i). Only
252 // one can be specified.
253 unsigned NumPositional = 0;
254
255 // Keep track of which operation was requested
256 ArchiveOperation Operation;
257
258 bool MaybeJustCreateSymTab = false;
259
260 for (unsigned i = 0; i < Options.size(); ++i) {
261 switch (Options[i]) {
262 case 'd':
263 ++NumOperations;
264 Operation = Delete;
265 break;
266 case 'm':
267 ++NumOperations;
268 Operation = Move;
269 break;
270 case 'p':
271 ++NumOperations;
272 Operation = Print;
273 break;
274 case 'q':
275 ++NumOperations;
276 Operation = QuickAppend;
277 break;
278 case 'r':
279 ++NumOperations;
280 Operation = ReplaceOrInsert;
281 break;
282 case 't':
283 ++NumOperations;
284 Operation = DisplayTable;
285 break;
286 case 'x':
287 ++NumOperations;
288 Operation = Extract;
289 break;
290 case 'c':
291 Create = true;
292 break;
293 case 'l': /* accepted but unused */
294 break;
295 case 'o':
296 OriginalDates = true;
297 break;
298 case 's':
299 Symtab = true;
300 MaybeJustCreateSymTab = true;
301 break;
302 case 'S':
303 Symtab = false;
304 break;
305 case 'u':
306 OnlyUpdate = true;
307 break;
308 case 'v':
309 Verbose = true;
310 break;
311 case 'a':
312 getRelPos();
313 AddAfter = true;
314 NumPositional++;
315 break;
316 case 'b':
317 getRelPos();
318 AddBefore = true;
319 NumPositional++;
320 break;
321 case 'i':
322 getRelPos();
323 AddBefore = true;
324 NumPositional++;
325 break;
326 case 'D':
327 Deterministic = true;
328 break;
329 case 'U':
330 Deterministic = false;
331 break;
332 case 'T':
333 Thin = true;
334 break;
335 case 'L':
336 AddLibrary = true;
337 break;
338 default:
339 fail(std::string("unknown option ") + Options[i]);
340 }
341 }
342
343 // At this point, the next thing on the command line must be
344 // the archive name.
345 getArchive();
346
347 // Everything on the command line at this point is a member.
348 getMembers();
349
350 if (NumOperations == 0 && MaybeJustCreateSymTab) {
351 NumOperations = 1;
352 Operation = CreateSymTab;
353 if (!Members.empty())
354 fail("The s operation takes only an archive as argument");
355 }
356
357 // Perform various checks on the operation/modifier specification
358 // to make sure we are dealing with a legal request.
359 if (NumOperations == 0)
360 fail("You must specify at least one of the operations");
361 if (NumOperations > 1)
362 fail("Only one operation may be specified");
363 if (NumPositional > 1)
364 fail("You may only specify one of a, b, and i modifiers");
365 if (AddAfter || AddBefore) {
366 if (Operation != Move && Operation != ReplaceOrInsert)
367 fail("The 'a', 'b' and 'i' modifiers can only be specified with "
368 "the 'm' or 'r' operations");
369 }
370 if (OriginalDates && Operation != Extract)
371 fail("The 'o' modifier is only applicable to the 'x' operation");
372 if (OnlyUpdate && Operation != ReplaceOrInsert)
373 fail("The 'u' modifier is only applicable to the 'r' operation");
374 if (AddLibrary && Operation != QuickAppend)
375 fail("The 'L' modifier is only applicable to the 'q' operation");
376
377 // Return the parsed operation to the caller
378 return Operation;
379}
380
381// Implements the 'p' operation. This function traverses the archive
382// looking for members that match the path list.
383static void doPrint(StringRef Name, const object::Archive::Child &C) {
384 if (Verbose)
385 outs() << "Printing " << Name << "\n";
386
387 Expected<StringRef> DataOrErr = C.getBuffer();
388 failIfError(DataOrErr.takeError());
389 StringRef Data = *DataOrErr;
390 outs().write(Data.data(), Data.size());
391}
392
393// Utility function for printing out the file mode when the 't' operation is in
394// verbose mode.
395static void printMode(unsigned mode) {
396 outs() << ((mode & 004) ? "r" : "-");
397 outs() << ((mode & 002) ? "w" : "-");
398 outs() << ((mode & 001) ? "x" : "-");
399}
400
401// Implement the 't' operation. This function prints out just
402// the file names of each of the members. However, if verbose mode is requested
403// ('v' modifier) then the file type, permission mode, user, group, size, and
404// modification time are also printed.
405static void doDisplayTable(StringRef Name, const object::Archive::Child &C) {
406 if (Verbose) {
407 Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
408 failIfError(ModeOrErr.takeError());
409 sys::fs::perms Mode = ModeOrErr.get();
410 printMode((Mode >> 6) & 007);
411 printMode((Mode >> 3) & 007);
412 printMode(Mode & 007);
413 Expected<unsigned> UIDOrErr = C.getUID();
414 failIfError(UIDOrErr.takeError());
415 outs() << ' ' << UIDOrErr.get();
416 Expected<unsigned> GIDOrErr = C.getGID();
417 failIfError(GIDOrErr.takeError());
418 outs() << '/' << GIDOrErr.get();
419 Expected<uint64_t> Size = C.getSize();
420 failIfError(Size.takeError());
421 outs() << ' ' << format("%6llu", Size.get());
422 auto ModTimeOrErr = C.getLastModified();
423 failIfError(ModTimeOrErr.takeError());
424 // Note: formatv() only handles the default TimePoint<>, which is in
425 // nanoseconds.
426 // TODO: fix format_provider<TimePoint<>> to allow other units.
427 sys::TimePoint<> ModTimeInNs = ModTimeOrErr.get();
428 outs() << ' ' << formatv("{0:%b %e %H:%M %Y}", ModTimeInNs);
429 outs() << ' ';
430 }
431
432 if (C.getParent()->isThin()) {
433 outs() << sys::path::parent_path(ArchiveName);
434 outs() << '/';
435 }
436 outs() << Name << "\n";
437}
438
439// Implement the 'x' operation. This function extracts files back to the file
440// system.
441static void doExtract(StringRef Name, const object::Archive::Child &C) {
442 // Retain the original mode.
443 Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
444 failIfError(ModeOrErr.takeError());
445 sys::fs::perms Mode = ModeOrErr.get();
446
447 int FD;
448 failIfError(sys::fs::openFileForWrite(sys::path::filename(Name), FD,
449 sys::fs::CD_CreateAlways,
450 sys::fs::F_None, Mode),
451 Name);
452
453 {
454 raw_fd_ostream file(FD, false);
455
456 // Get the data and its length
457 Expected<StringRef> BufOrErr = C.getBuffer();
458 failIfError(BufOrErr.takeError());
459 StringRef Data = BufOrErr.get();
460
461 // Write the data.
462 file.write(Data.data(), Data.size());
463 }
464
465 // If we're supposed to retain the original modification times, etc. do so
466 // now.
467 if (OriginalDates) {
468 auto ModTimeOrErr = C.getLastModified();
469 failIfError(ModTimeOrErr.takeError());
470 failIfError(
471 sys::fs::setLastAccessAndModificationTime(FD, ModTimeOrErr.get()));
472 }
473
474 if (close(FD))
475 fail("Could not close the file");
476}
477
478static bool shouldCreateArchive(ArchiveOperation Op) {
479 switch (Op) {
480 case Print:
481 case Delete:
482 case Move:
483 case DisplayTable:
484 case Extract:
485 case CreateSymTab:
486 return false;
487
488 case QuickAppend:
489 case ReplaceOrInsert:
490 return true;
491 }
492
493 llvm_unreachable("Missing entry in covered switch.");
494}
495
496static void performReadOperation(ArchiveOperation Operation,
497 object::Archive *OldArchive) {
498 if (Operation == Extract && OldArchive->isThin())
499 fail("extracting from a thin archive is not supported");
500
501 bool Filter = !Members.empty();
502 {
503 Error Err = Error::success();
504 for (auto &C : OldArchive->children(Err)) {
505 Expected<StringRef> NameOrErr = C.getName();
506 failIfError(NameOrErr.takeError());
507 StringRef Name = NameOrErr.get();
508
509 if (Filter) {
510 auto I = find(Members, Name);
511 if (I == Members.end())
512 continue;
513 Members.erase(I);
514 }
515
516 switch (Operation) {
517 default:
518 llvm_unreachable("Not a read operation");
519 case Print:
520 doPrint(Name, C);
521 break;
522 case DisplayTable:
523 doDisplayTable(Name, C);
524 break;
525 case Extract:
526 doExtract(Name, C);
527 break;
528 }
529 }
530 failIfError(std::move(Err));
531 }
532
533 if (Members.empty())
534 return;
535 for (StringRef Name : Members)
Jordan Rupprecht913cfad2019-01-15 21:52:31 +0000536 WithColor::error(errs(), ToolName) << "'" << Name << "' was not found\n";
Fangrui Songa8aac852018-10-26 17:38:27 +0000537 exit(1);
538}
539
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000540static void addChildMember(std::vector<NewArchiveMember> &Members,
541 const object::Archive::Child &M,
542 bool FlattenArchive = false) {
Fangrui Songa8aac852018-10-26 17:38:27 +0000543 if (Thin && !M.getParent()->isThin())
544 fail("Cannot convert a regular archive to a thin one");
545 Expected<NewArchiveMember> NMOrErr =
546 NewArchiveMember::getOldMember(M, Deterministic);
547 failIfError(NMOrErr.takeError());
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000548 if (FlattenArchive &&
549 identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) {
Jordan Rupprecht913cfad2019-01-15 21:52:31 +0000550 Expected<std::string> FileNameOrErr = M.getFullName();
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000551 failIfError(FileNameOrErr.takeError());
552 object::Archive &Lib = readLibrary(*FileNameOrErr);
553 // When creating thin archives, only flatten if the member is also thin.
554 if (!Thin || Lib.isThin()) {
555 Error Err = Error::success();
556 // Only Thin archives are recursively flattened.
557 for (auto &Child : Lib.children(Err))
558 addChildMember(Members, Child, /*FlattenArchive=*/Thin);
559 failIfError(std::move(Err));
560 return;
561 }
562 }
563 Members.push_back(std::move(*NMOrErr));
Fangrui Songa8aac852018-10-26 17:38:27 +0000564}
565
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000566static void addMember(std::vector<NewArchiveMember> &Members,
567 StringRef FileName, bool FlattenArchive = false) {
Fangrui Songa8aac852018-10-26 17:38:27 +0000568 Expected<NewArchiveMember> NMOrErr =
569 NewArchiveMember::getFile(FileName, Deterministic);
570 failIfError(NMOrErr.takeError(), FileName);
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000571 if (FlattenArchive &&
572 identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) {
Fangrui Songa8aac852018-10-26 17:38:27 +0000573 object::Archive &Lib = readLibrary(FileName);
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000574 // When creating thin archives, only flatten if the member is also thin.
575 if (!Thin || Lib.isThin()) {
576 Error Err = Error::success();
577 // Only Thin archives are recursively flattened.
578 for (auto &Child : Lib.children(Err))
579 addChildMember(Members, Child, /*FlattenArchive=*/Thin);
580 failIfError(std::move(Err));
581 return;
582 }
Fangrui Songa8aac852018-10-26 17:38:27 +0000583 }
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000584 // Use the basename of the object path for the member name.
585 NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName);
586 Members.push_back(std::move(*NMOrErr));
Fangrui Songa8aac852018-10-26 17:38:27 +0000587}
588
589enum InsertAction {
590 IA_AddOldMember,
591 IA_AddNewMember,
592 IA_Delete,
593 IA_MoveOldMember,
594 IA_MoveNewMember
595};
596
597static InsertAction computeInsertAction(ArchiveOperation Operation,
598 const object::Archive::Child &Member,
599 StringRef Name,
600 std::vector<StringRef>::iterator &Pos) {
601 if (Operation == QuickAppend || Members.empty())
602 return IA_AddOldMember;
603
604 auto MI = find_if(Members, [Name](StringRef Path) {
605 return Name == sys::path::filename(Path);
606 });
607
608 if (MI == Members.end())
609 return IA_AddOldMember;
610
611 Pos = MI;
612
613 if (Operation == Delete)
614 return IA_Delete;
615
616 if (Operation == Move)
617 return IA_MoveOldMember;
618
619 if (Operation == ReplaceOrInsert) {
620 StringRef PosName = sys::path::filename(RelPos);
621 if (!OnlyUpdate) {
622 if (PosName.empty())
623 return IA_AddNewMember;
624 return IA_MoveNewMember;
625 }
626
627 // We could try to optimize this to a fstat, but it is not a common
628 // operation.
629 sys::fs::file_status Status;
630 failIfError(sys::fs::status(*MI, Status), *MI);
631 auto ModTimeOrErr = Member.getLastModified();
632 failIfError(ModTimeOrErr.takeError());
633 if (Status.getLastModificationTime() < ModTimeOrErr.get()) {
634 if (PosName.empty())
635 return IA_AddOldMember;
636 return IA_MoveOldMember;
637 }
638
639 if (PosName.empty())
640 return IA_AddNewMember;
641 return IA_MoveNewMember;
642 }
643 llvm_unreachable("No such operation");
644}
645
646// We have to walk this twice and computing it is not trivial, so creating an
647// explicit std::vector is actually fairly efficient.
648static std::vector<NewArchiveMember>
649computeNewArchiveMembers(ArchiveOperation Operation,
650 object::Archive *OldArchive) {
651 std::vector<NewArchiveMember> Ret;
652 std::vector<NewArchiveMember> Moved;
653 int InsertPos = -1;
654 StringRef PosName = sys::path::filename(RelPos);
655 if (OldArchive) {
656 Error Err = Error::success();
657 for (auto &Child : OldArchive->children(Err)) {
658 int Pos = Ret.size();
659 Expected<StringRef> NameOrErr = Child.getName();
660 failIfError(NameOrErr.takeError());
661 StringRef Name = NameOrErr.get();
662 if (Name == PosName) {
663 assert(AddAfter || AddBefore);
664 if (AddBefore)
665 InsertPos = Pos;
666 else
667 InsertPos = Pos + 1;
668 }
669
670 std::vector<StringRef>::iterator MemberI = Members.end();
671 InsertAction Action =
672 computeInsertAction(Operation, Child, Name, MemberI);
673 switch (Action) {
674 case IA_AddOldMember:
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000675 addChildMember(Ret, Child);
Fangrui Songa8aac852018-10-26 17:38:27 +0000676 break;
677 case IA_AddNewMember:
678 addMember(Ret, *MemberI);
679 break;
680 case IA_Delete:
681 break;
682 case IA_MoveOldMember:
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000683 addChildMember(Moved, Child);
Fangrui Songa8aac852018-10-26 17:38:27 +0000684 break;
685 case IA_MoveNewMember:
686 addMember(Moved, *MemberI);
687 break;
688 }
689 if (MemberI != Members.end())
690 Members.erase(MemberI);
691 }
692 failIfError(std::move(Err));
693 }
694
695 if (Operation == Delete)
696 return Ret;
697
698 if (!RelPos.empty() && InsertPos == -1)
699 fail("Insertion point not found");
700
701 if (RelPos.empty())
702 InsertPos = Ret.size();
703
704 assert(unsigned(InsertPos) <= Ret.size());
705 int Pos = InsertPos;
706 for (auto &M : Moved) {
707 Ret.insert(Ret.begin() + Pos, std::move(M));
708 ++Pos;
709 }
710
711 if (AddLibrary) {
712 assert(Operation == QuickAppend);
713 for (auto &Member : Members)
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000714 addMember(Ret, Member, /*FlattenArchive=*/true);
Fangrui Songa8aac852018-10-26 17:38:27 +0000715 return Ret;
716 }
717
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000718 std::vector<NewArchiveMember> NewMembers;
719 for (auto &Member : Members)
720 addMember(NewMembers, Member, /*FlattenArchive=*/Thin);
721 Ret.reserve(Ret.size() + NewMembers.size());
722 std::move(NewMembers.begin(), NewMembers.end(),
723 std::inserter(Ret, std::next(Ret.begin(), InsertPos)));
Fangrui Songa8aac852018-10-26 17:38:27 +0000724
725 return Ret;
726}
727
728static object::Archive::Kind getDefaultForHost() {
729 return Triple(sys::getProcessTriple()).isOSDarwin()
730 ? object::Archive::K_DARWIN
731 : object::Archive::K_GNU;
732}
733
734static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) {
735 Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =
736 object::ObjectFile::createObjectFile(Member.Buf->getMemBufferRef());
737
738 if (OptionalObject)
739 return isa<object::MachOObjectFile>(**OptionalObject)
740 ? object::Archive::K_DARWIN
741 : object::Archive::K_GNU;
742
743 // squelch the error in case we had a non-object file
744 consumeError(OptionalObject.takeError());
745 return getDefaultForHost();
746}
747
748static void performWriteOperation(ArchiveOperation Operation,
749 object::Archive *OldArchive,
750 std::unique_ptr<MemoryBuffer> OldArchiveBuf,
751 std::vector<NewArchiveMember> *NewMembersP) {
752 std::vector<NewArchiveMember> NewMembers;
753 if (!NewMembersP)
754 NewMembers = computeNewArchiveMembers(Operation, OldArchive);
755
756 object::Archive::Kind Kind;
757 switch (FormatType) {
758 case Default:
759 if (Thin)
760 Kind = object::Archive::K_GNU;
761 else if (OldArchive)
762 Kind = OldArchive->kind();
763 else if (NewMembersP)
Jordan Rupprechtf6ea1292018-12-20 00:57:06 +0000764 Kind = !NewMembersP->empty() ? getKindFromMember(NewMembersP->front())
765 : getDefaultForHost();
Fangrui Songa8aac852018-10-26 17:38:27 +0000766 else
Jordan Rupprechtf6ea1292018-12-20 00:57:06 +0000767 Kind = !NewMembers.empty() ? getKindFromMember(NewMembers.front())
768 : getDefaultForHost();
Fangrui Songa8aac852018-10-26 17:38:27 +0000769 break;
770 case GNU:
771 Kind = object::Archive::K_GNU;
772 break;
773 case BSD:
774 if (Thin)
775 fail("Only the gnu format has a thin mode");
776 Kind = object::Archive::K_BSD;
777 break;
778 case DARWIN:
779 if (Thin)
780 fail("Only the gnu format has a thin mode");
781 Kind = object::Archive::K_DARWIN;
782 break;
783 case Unknown:
784 llvm_unreachable("");
785 }
786
787 Error E =
788 writeArchive(ArchiveName, NewMembersP ? *NewMembersP : NewMembers, Symtab,
789 Kind, Deterministic, Thin, std::move(OldArchiveBuf));
790 failIfError(std::move(E), ArchiveName);
791}
792
793static void createSymbolTable(object::Archive *OldArchive) {
794 // When an archive is created or modified, if the s option is given, the
795 // resulting archive will have a current symbol table. If the S option
796 // is given, it will have no symbol table.
797 // In summary, we only need to update the symbol table if we have none.
798 // This is actually very common because of broken build systems that think
799 // they have to run ranlib.
800 if (OldArchive->hasSymbolTable())
801 return;
802
803 performWriteOperation(CreateSymTab, OldArchive, nullptr, nullptr);
804}
805
806static void performOperation(ArchiveOperation Operation,
807 object::Archive *OldArchive,
808 std::unique_ptr<MemoryBuffer> OldArchiveBuf,
809 std::vector<NewArchiveMember> *NewMembers) {
810 switch (Operation) {
811 case Print:
812 case DisplayTable:
813 case Extract:
814 performReadOperation(Operation, OldArchive);
815 return;
816
817 case Delete:
818 case Move:
819 case QuickAppend:
820 case ReplaceOrInsert:
821 performWriteOperation(Operation, OldArchive, std::move(OldArchiveBuf),
822 NewMembers);
823 return;
824 case CreateSymTab:
825 createSymbolTable(OldArchive);
826 return;
827 }
828 llvm_unreachable("Unknown operation.");
829}
830
831static int performOperation(ArchiveOperation Operation,
832 std::vector<NewArchiveMember> *NewMembers) {
833 // Create or open the archive object.
834 ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
835 MemoryBuffer::getFile(ArchiveName, -1, false);
836 std::error_code EC = Buf.getError();
837 if (EC && EC != errc::no_such_file_or_directory)
838 fail("error opening '" + ArchiveName + "': " + EC.message() + "!");
839
840 if (!EC) {
841 Error Err = Error::success();
842 object::Archive Archive(Buf.get()->getMemBufferRef(), Err);
843 EC = errorToErrorCode(std::move(Err));
844 failIfError(EC,
845 "error loading '" + ArchiveName + "': " + EC.message() + "!");
846 performOperation(Operation, &Archive, std::move(Buf.get()), NewMembers);
847 return 0;
848 }
849
850 assert(EC == errc::no_such_file_or_directory);
851
852 if (!shouldCreateArchive(Operation)) {
853 failIfError(EC, Twine("error loading '") + ArchiveName + "'");
854 } else {
855 if (!Create) {
856 // Produce a warning if we should and we're creating the archive
Jordan Rupprecht913cfad2019-01-15 21:52:31 +0000857 WithColor::warning(errs(), ToolName)
858 << "creating " << ArchiveName << "\n";
Fangrui Songa8aac852018-10-26 17:38:27 +0000859 }
860 }
861
862 performOperation(Operation, nullptr, nullptr, NewMembers);
863 return 0;
864}
865
866static void runMRIScript() {
867 enum class MRICommand { AddLib, AddMod, Create, Delete, Save, End, Invalid };
868
869 ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getSTDIN();
870 failIfError(Buf.getError());
871 const MemoryBuffer &Ref = *Buf.get();
872 bool Saved = false;
873 std::vector<NewArchiveMember> NewMembers;
874
875 for (line_iterator I(Ref, /*SkipBlanks*/ false), E; I != E; ++I) {
876 StringRef Line = *I;
877 Line = Line.split(';').first;
878 Line = Line.split('*').first;
879 Line = Line.trim();
880 if (Line.empty())
881 continue;
882 StringRef CommandStr, Rest;
883 std::tie(CommandStr, Rest) = Line.split(' ');
884 Rest = Rest.trim();
885 if (!Rest.empty() && Rest.front() == '"' && Rest.back() == '"')
886 Rest = Rest.drop_front().drop_back();
887 auto Command = StringSwitch<MRICommand>(CommandStr.lower())
888 .Case("addlib", MRICommand::AddLib)
889 .Case("addmod", MRICommand::AddMod)
890 .Case("create", MRICommand::Create)
891 .Case("delete", MRICommand::Delete)
892 .Case("save", MRICommand::Save)
893 .Case("end", MRICommand::End)
894 .Default(MRICommand::Invalid);
895
896 switch (Command) {
897 case MRICommand::AddLib: {
898 object::Archive &Lib = readLibrary(Rest);
899 {
900 Error Err = Error::success();
901 for (auto &Member : Lib.children(Err))
Jordan Rupprecht19263d12019-01-14 21:11:46 +0000902 addChildMember(NewMembers, Member);
Fangrui Songa8aac852018-10-26 17:38:27 +0000903 failIfError(std::move(Err));
904 }
905 break;
906 }
907 case MRICommand::AddMod:
908 addMember(NewMembers, Rest);
909 break;
910 case MRICommand::Create:
911 Create = true;
912 if (!ArchiveName.empty())
913 fail("Editing multiple archives not supported");
914 if (Saved)
915 fail("File already saved");
916 ArchiveName = Rest;
917 break;
918 case MRICommand::Delete: {
919 StringRef Name = sys::path::filename(Rest);
920 llvm::erase_if(NewMembers,
921 [=](NewArchiveMember &M) { return M.MemberName == Name; });
922 break;
923 }
924 case MRICommand::Save:
925 Saved = true;
926 break;
927 case MRICommand::End:
928 break;
929 case MRICommand::Invalid:
930 fail("Unknown command: " + CommandStr);
931 }
932 }
933
934 // Nothing to do if not saved.
935 if (Saved)
936 performOperation(ReplaceOrInsert, &NewMembers);
937 exit(0);
938}
939
940static bool handleGenericOption(StringRef arg) {
941 if (arg == "-help" || arg == "--help") {
942 printHelpMessage();
943 return true;
944 }
945 if (arg == "-version" || arg == "--version") {
946 cl::PrintVersionMessage();
947 return true;
948 }
949 return false;
950}
951
952static int ar_main(int argc, char **argv) {
953 SmallVector<const char *, 0> Argv(argv, argv + argc);
954 BumpPtrAllocator Alloc;
955 StringSaver Saver(Alloc);
956 cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv);
957 for (size_t i = 1; i < Argv.size(); ++i) {
958 StringRef Arg = Argv[i];
959 const char *match;
960 auto MatchFlagWithArg = [&](const char *expected) {
961 size_t len = strlen(expected);
962 if (Arg == expected) {
963 if (++i >= Argv.size())
964 fail(std::string(expected) + " requires an argument");
965 match = Argv[i];
966 return true;
967 }
968 if (Arg.startswith(expected) && Arg.size() > len && Arg[len] == '=') {
969 match = Arg.data() + len + 1;
970 return true;
971 }
972 return false;
973 };
974 if (handleGenericOption(Argv[i]))
975 return 0;
976 if (Arg == "--") {
977 for (; i < Argv.size(); ++i)
978 PositionalArgs.push_back(Argv[i]);
979 break;
980 }
981 if (Arg[0] == '-') {
982 if (Arg.startswith("--"))
983 Arg = Argv[i] + 2;
984 else
985 Arg = Argv[i] + 1;
986 if (Arg == "M") {
987 MRI = true;
988 } else if (MatchFlagWithArg("format")) {
989 FormatType = StringSwitch<Format>(match)
990 .Case("default", Default)
991 .Case("gnu", GNU)
992 .Case("darwin", DARWIN)
993 .Case("bsd", BSD)
994 .Default(Unknown);
995 if (FormatType == Unknown)
996 fail(std::string("Invalid format ") + match);
997 } else if (MatchFlagWithArg("plugin")) {
998 // Ignored.
999 } else {
1000 Options += Argv[i] + 1;
1001 }
1002 } else if (Options.empty()) {
1003 Options += Argv[i];
1004 } else {
1005 PositionalArgs.push_back(Argv[i]);
1006 }
1007 }
1008 ArchiveOperation Operation = parseCommandLine();
1009 return performOperation(Operation, nullptr);
1010}
1011
1012static int ranlib_main(int argc, char **argv) {
1013 bool ArchiveSpecified = false;
1014 for (int i = 1; i < argc; ++i) {
1015 if (handleGenericOption(argv[i])) {
1016 return 0;
1017 } else {
1018 if (ArchiveSpecified)
1019 fail("Exactly one archive should be specified");
1020 ArchiveSpecified = true;
1021 ArchiveName = argv[i];
1022 }
1023 }
1024 return performOperation(CreateSymTab, nullptr);
1025}
1026
1027int main(int argc, char **argv) {
1028 InitLLVM X(argc, argv);
1029 ToolName = argv[0];
1030
1031 llvm::InitializeAllTargetInfos();
1032 llvm::InitializeAllTargetMCs();
1033 llvm::InitializeAllAsmParsers();
1034
1035 Stem = sys::path::stem(ToolName);
1036 if (Stem.contains_lower("dlltool"))
1037 return dlltoolDriverMain(makeArrayRef(argv, argc));
1038
1039 if (Stem.contains_lower("ranlib"))
1040 return ranlib_main(argc, argv);
1041
1042 if (Stem.contains_lower("lib"))
1043 return libDriverMain(makeArrayRef(argv, argc));
1044
1045 if (Stem.contains_lower("ar"))
1046 return ar_main(argc, argv);
1047 fail("Not ranlib, ar, lib or dlltool!");
1048}