1//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Builds up (relatively) standard unix archive files (.a) containing LLVM
10// bitcode or other files.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/ADT/StringExtras.h"
15#include "llvm/ADT/StringMap.h"
16#include "llvm/ADT/StringSwitch.h"
17#include "llvm/BinaryFormat/Magic.h"
18#include "llvm/IR/LLVMContext.h"
19#include "llvm/Object/Archive.h"
20#include "llvm/Object/ArchiveWriter.h"
21#include "llvm/Object/SymbolicFile.h"
22#include "llvm/Support/Chrono.h"
23#include "llvm/Support/CommandLine.h"
24#include "llvm/Support/ConvertUTF.h"
25#include "llvm/Support/Errc.h"
26#include "llvm/Support/FileSystem.h"
27#include "llvm/Support/Format.h"
28#include "llvm/Support/FormatVariadic.h"
29#include "llvm/Support/LLVMDriver.h"
30#include "llvm/Support/LineIterator.h"
31#include "llvm/Support/MemoryBuffer.h"
32#include "llvm/Support/Path.h"
33#include "llvm/Support/Process.h"
34#include "llvm/Support/StringSaver.h"
35#include "llvm/Support/TargetSelect.h"
36#include "llvm/Support/ToolOutputFile.h"
37#include "llvm/Support/WithColor.h"
38#include "llvm/Support/raw_ostream.h"
39#include "llvm/TargetParser/Host.h"
40#include "llvm/TargetParser/Triple.h"
41#include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"
42#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
43
44#if !defined(_MSC_VER) && !defined(__MINGW32__)
45#include <unistd.h>
46#else
47#include <io.h>
48#endif
49
50#ifdef _WIN32
51#include "llvm/Support/Windows/WindowsSupport.h"
52#endif
53
54using namespace llvm;
55using namespace llvm::object;
56
57// The name this program was invoked as.
58static StringRef ToolName;
59
60// The basename of this program.
61static StringRef Stem;
62
63static void printRanLibHelp(StringRef ToolName) {
64 outs() << "OVERVIEW: LLVM ranlib\n\n"
65 << "Generate an index for archives\n\n"
66 << "USAGE: " + ToolName + " archive...\n\n"
67 << "OPTIONS:\n"
68 << " -h --help - Display available options\n"
69 << " -V --version - Display the version of this program\n"
70 << " -D - Use zero for timestamps and uids/gids "
71 "(default)\n"
72 << " -U - Use actual timestamps and uids/gids\n"
73 << " -X{32|64|32_64|any} - Specify which archive symbol tables "
74 "should be generated if they do not already exist (AIX OS only)\n";
75}
76
77static void printArHelp(StringRef ToolName) {
78 const char ArOptions[] =
79 R"(OPTIONS:
80 --format - archive format to create
81 =default - default
82 =gnu - gnu
83 =darwin - darwin
84 =bsd - bsd
85 =bigarchive - big archive (AIX OS)
86 =zos - zos archive (z/OS OS)
87 =coff - coff
88 --plugin=<string> - ignored for compatibility
89 -h --help - display this help and exit
90 --output - the directory to extract archive members to
91 --rsp-quoting - quoting style for response files
92 =posix - posix
93 =windows - windows
94 --thin - create a thin archive
95 --version - print the version and exit
96 -X{32|64|32_64|any} - object mode (only for AIX OS)
97 @<file> - read options from <file>
98
99OPERATIONS:
100 d - delete [files] from the archive
101 m - move [files] in the archive
102 p - print contents of [files] found in the archive
103 q - quick append [files] to the archive
104 r - replace or insert [files] into the archive
105 s - act as ranlib
106 t - display list of files in archive
107 x - extract [files] from the archive
108
109MODIFIERS:
110 [a] - put [files] after [relpos]
111 [b] - put [files] before [relpos] (same as [i])
112 [c] - do not warn if archive had to be created
113 [D] - use zero for timestamps and uids/gids (default)
114 [h] - display this help and exit
115 [i] - put [files] before [relpos] (same as [b])
116 [l] - ignored for compatibility
117 [L] - add archive's contents
118 [N] - use instance [count] of name
119 [o] - preserve original dates
120 [O] - display member offsets
121 [P] - use full names when matching (implied for thin archives)
122 [s] - create an archive index (cf. ranlib)
123 [S] - do not build a symbol table
124 [T] - deprecated, use --thin instead
125 [u] - update only [files] newer than archive contents
126 [U] - use actual timestamps and uids/gids
127 [v] - be verbose about actions taken
128 [V] - display the version and exit
129)";
130
131 outs() << "OVERVIEW: LLVM Archiver\n\n"
132 << "USAGE: " + ToolName +
133 " [options] [-]<operation>[modifiers] [relpos] "
134 "[count] <archive> [files]\n"
135 << " " + ToolName + " -M [< mri-script]\n\n";
136
137 outs() << ArOptions;
138}
139
140static void printHelpMessage() {
141 if (Stem.contains_insensitive(Other: "ranlib"))
142 printRanLibHelp(ToolName: Stem);
143 else if (Stem.contains_insensitive(Other: "ar"))
144 printArHelp(ToolName: Stem);
145}
146
147static unsigned MRILineNumber;
148static bool ParsingMRIScript;
149
150// Show the error plus the usage message, and exit.
151[[noreturn]] static void badUsage(Twine Error) {
152 WithColor::error(OS&: errs(), Prefix: ToolName) << Error << "\n";
153 printHelpMessage();
154 exit(status: 1);
155}
156
157// Show the error message and exit.
158[[noreturn]] static void fail(Twine Error) {
159 if (ParsingMRIScript) {
160 WithColor::error(OS&: errs(), Prefix: ToolName)
161 << "script line " << MRILineNumber << ": " << Error << "\n";
162 } else {
163 WithColor::error(OS&: errs(), Prefix: ToolName) << Error << "\n";
164 }
165 exit(status: 1);
166}
167
168static void failIfError(std::error_code EC, Twine Context = "") {
169 if (!EC)
170 return;
171
172 std::string ContextStr = Context.str();
173 if (ContextStr.empty())
174 fail(Error: EC.message());
175 fail(Error: Context + ": " + EC.message());
176}
177
178static void failIfError(Error E, Twine Context = "") {
179 if (!E)
180 return;
181
182 handleAllErrors(E: std::move(E), Handlers: [&](const llvm::ErrorInfoBase &EIB) {
183 std::string ContextStr = Context.str();
184 if (ContextStr.empty())
185 fail(Error: EIB.message());
186 fail(Error: Context + ": " + EIB.message());
187 });
188}
189
190static void warn(Twine Message) {
191 WithColor::warning(OS&: errs(), Prefix: ToolName) << Message << "\n";
192}
193
194static SmallVector<const char *, 256> PositionalArgs;
195
196static bool MRI;
197
198namespace {
199enum Format {
200 Default,
201 GNU,
202 COFF,
203 BSD,
204 DARWIN,
205 BIGARCHIVE,
206 ZOSARCHIVE,
207 Unknown
208};
209}
210
211static Format FormatType = Default;
212
213static std::string Options;
214
215// This enumeration delineates the kinds of operations on an archive
216// that are permitted.
217enum ArchiveOperation {
218 Print, ///< Print the contents of the archive
219 Delete, ///< Delete the specified members
220 Move, ///< Move members to end or as given by {a,b,i} modifiers
221 QuickAppend, ///< Quickly append to end of archive
222 ReplaceOrInsert, ///< Replace or Insert members
223 DisplayTable, ///< Display the table of contents
224 Extract, ///< Extract files back to file system
225 CreateSymTab ///< Create a symbol table in an existing archive
226};
227
228enum class BitModeTy { Bit32, Bit64, Bit32_64, Any, Unknown };
229
230static BitModeTy BitMode = BitModeTy::Bit32;
231
232// Modifiers to follow operation to vary behavior
233static bool AddAfter = false; ///< 'a' modifier
234static bool AddBefore = false; ///< 'b' modifier
235static bool Create = false; ///< 'c' modifier
236static bool OriginalDates = false; ///< 'o' modifier
237static bool DisplayMemberOffsets = false; ///< 'O' modifier
238static bool CompareFullPath = false; ///< 'P' modifier
239static bool OnlyUpdate = false; ///< 'u' modifier
240static bool Verbose = false; ///< 'v' modifier
241static SymtabWritingMode Symtab =
242 SymtabWritingMode::NormalSymtab; ///< 's' modifier
243static bool Deterministic = true; ///< 'D' and 'U' modifiers
244static bool Thin = false; ///< 'T' modifier
245static bool AddLibrary = false; ///< 'L' modifier
246
247// Relative Positional Argument (for insert/move). This variable holds
248// the name of the archive member to which the 'a', 'b' or 'i' modifier
249// refers. Only one of 'a', 'b' or 'i' can be specified so we only need
250// one variable.
251static std::string RelPos;
252
253// Count parameter for 'N' modifier. This variable specifies which file should
254// match for extract/delete operations when there are multiple matches. This is
255// 1-indexed. A value of 0 is invalid, and implies 'N' is not used.
256static int CountParam = 0;
257
258// This variable holds the name of the archive file as given on the
259// command line.
260static std::string ArchiveName;
261
262// Output directory specified by --output.
263static std::string OutputDir;
264
265static std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;
266static std::vector<std::unique_ptr<object::Archive>> Archives;
267
268// This variable holds the list of member files to proecess, as given
269// on the command line.
270static std::vector<StringRef> Members;
271
272// Static buffer to hold StringRefs.
273static BumpPtrAllocator Alloc;
274
275// Extract the member filename from the command line for the [relpos] argument
276// associated with a, b, and i modifiers
277static void getRelPos() {
278 if (PositionalArgs.empty())
279 fail(Error: "expected [relpos] for 'a', 'b', or 'i' modifier");
280 RelPos = PositionalArgs[0];
281 PositionalArgs.erase(CI: PositionalArgs.begin());
282}
283
284// Extract the parameter from the command line for the [count] argument
285// associated with the N modifier
286static void getCountParam() {
287 if (PositionalArgs.empty())
288 badUsage(Error: "expected [count] for 'N' modifier");
289 auto CountParamArg = StringRef(PositionalArgs[0]);
290 if (CountParamArg.getAsInteger(Radix: 10, Result&: CountParam))
291 badUsage(Error: "value for [count] must be numeric, got: " + CountParamArg);
292 if (CountParam < 1)
293 badUsage(Error: "value for [count] must be positive, got: " + CountParamArg);
294 PositionalArgs.erase(CI: PositionalArgs.begin());
295}
296
297// Get the archive file name from the command line
298static void getArchive() {
299 if (PositionalArgs.empty())
300 badUsage(Error: "an archive name must be specified");
301 ArchiveName = PositionalArgs[0];
302 PositionalArgs.erase(CI: PositionalArgs.begin());
303}
304
305static object::Archive &readLibrary(const Twine &Library) {
306 auto BufOrErr = MemoryBuffer::getFile(Filename: Library, /*IsText=*/false,
307 /*RequiresNullTerminator=*/false);
308 failIfError(EC: BufOrErr.getError(), Context: "could not open library " + Library);
309 ArchiveBuffers.push_back(x: std::move(*BufOrErr));
310 auto LibOrErr =
311 object::Archive::create(Source: ArchiveBuffers.back()->getMemBufferRef());
312 failIfError(EC: errorToErrorCode(Err: LibOrErr.takeError()),
313 Context: "could not parse library");
314 Archives.push_back(x: std::move(*LibOrErr));
315 return *Archives.back();
316}
317
318static void runMRIScript();
319
320// Parse the command line options as presented and return the operation
321// specified. Process all modifiers and check to make sure that constraints on
322// modifier/operation pairs have not been violated.
323static ArchiveOperation parseCommandLine() {
324 if (MRI) {
325 if (!PositionalArgs.empty() || !Options.empty())
326 badUsage(Error: "cannot mix -M and other options");
327 runMRIScript();
328 }
329
330 // Keep track of number of operations. We can only specify one
331 // per execution.
332 unsigned NumOperations = 0;
333
334 // Keep track of the number of positional modifiers (a,b,i). Only
335 // one can be specified.
336 unsigned NumPositional = 0;
337
338 // Keep track of which operation was requested
339 ArchiveOperation Operation;
340
341 bool MaybeJustCreateSymTab = false;
342
343 for (unsigned i = 0; i < Options.size(); ++i) {
344 switch (Options[i]) {
345 case 'd':
346 ++NumOperations;
347 Operation = Delete;
348 break;
349 case 'm':
350 ++NumOperations;
351 Operation = Move;
352 break;
353 case 'p':
354 ++NumOperations;
355 Operation = Print;
356 break;
357 case 'q':
358 ++NumOperations;
359 Operation = QuickAppend;
360 break;
361 case 'r':
362 ++NumOperations;
363 Operation = ReplaceOrInsert;
364 break;
365 case 't':
366 ++NumOperations;
367 Operation = DisplayTable;
368 break;
369 case 'x':
370 ++NumOperations;
371 Operation = Extract;
372 break;
373 case 'c':
374 Create = true;
375 break;
376 case 'l': /* accepted but unused */
377 break;
378 case 'o':
379 OriginalDates = true;
380 break;
381 case 'O':
382 DisplayMemberOffsets = true;
383 break;
384 case 'P':
385 CompareFullPath = true;
386 break;
387 case 's':
388 Symtab = SymtabWritingMode::NormalSymtab;
389 MaybeJustCreateSymTab = true;
390 break;
391 case 'S':
392 Symtab = SymtabWritingMode::NoSymtab;
393 break;
394 case 'u':
395 OnlyUpdate = true;
396 break;
397 case 'v':
398 Verbose = true;
399 break;
400 case 'a':
401 getRelPos();
402 AddAfter = true;
403 NumPositional++;
404 break;
405 case 'b':
406 getRelPos();
407 AddBefore = true;
408 NumPositional++;
409 break;
410 case 'i':
411 getRelPos();
412 AddBefore = true;
413 NumPositional++;
414 break;
415 case 'D':
416 Deterministic = true;
417 break;
418 case 'U':
419 Deterministic = false;
420 break;
421 case 'N':
422 getCountParam();
423 break;
424 case 'T':
425 Thin = true;
426 break;
427 case 'L':
428 AddLibrary = true;
429 break;
430 case 'V':
431 cl::PrintVersionMessage();
432 exit(status: 0);
433 case 'h':
434 printHelpMessage();
435 exit(status: 0);
436 default:
437 badUsage(Error: std::string("unknown option ") + Options[i]);
438 }
439 }
440
441 // Thin archives store path names, so P should be forced.
442 if (Thin)
443 CompareFullPath = true;
444
445 // At this point, the next thing on the command line must be
446 // the archive name.
447 getArchive();
448
449 // Everything on the command line at this point is a member.
450 Members.assign(first: PositionalArgs.begin(), last: PositionalArgs.end());
451
452 if (NumOperations == 0 && MaybeJustCreateSymTab) {
453 NumOperations = 1;
454 Operation = CreateSymTab;
455 if (!Members.empty())
456 badUsage(Error: "the 's' operation takes only an archive as argument");
457 }
458
459 // Perform various checks on the operation/modifier specification
460 // to make sure we are dealing with a legal request.
461 if (NumOperations == 0)
462 badUsage(Error: "you must specify at least one of the operations");
463 if (NumOperations > 1)
464 badUsage(Error: "only one operation may be specified");
465 if (NumPositional > 1)
466 badUsage(Error: "you may only specify one of 'a', 'b', and 'i' modifiers");
467 if (AddAfter || AddBefore)
468 if (Operation != Move && Operation != ReplaceOrInsert)
469 badUsage(Error: "the 'a', 'b' and 'i' modifiers can only be specified with "
470 "the 'm' or 'r' operations");
471 if (CountParam)
472 if (Operation != Extract && Operation != Delete)
473 badUsage(Error: "the 'N' modifier can only be specified with the 'x' or 'd' "
474 "operations");
475 if (OriginalDates && Operation != Extract)
476 badUsage(Error: "the 'o' modifier is only applicable to the 'x' operation");
477 if (OnlyUpdate && Operation != ReplaceOrInsert)
478 badUsage(Error: "the 'u' modifier is only applicable to the 'r' operation");
479 if (AddLibrary && Operation != QuickAppend)
480 badUsage(Error: "the 'L' modifier is only applicable to the 'q' operation");
481
482 if (!OutputDir.empty()) {
483 if (Operation != Extract)
484 badUsage(Error: "--output is only applicable to the 'x' operation");
485 bool IsDir = false;
486 // If OutputDir is not a directory, create_directories may still succeed if
487 // all components of the path prefix are directories. Test is_directory as
488 // well.
489 if (!sys::fs::create_directories(path: OutputDir))
490 sys::fs::is_directory(path: OutputDir, result&: IsDir);
491 if (!IsDir)
492 fail(Error: "'" + OutputDir + "' is not a directory");
493 }
494
495 // Return the parsed operation to the caller
496 return Operation;
497}
498
499// Implements the 'p' operation. This function traverses the archive
500// looking for members that match the path list.
501static void doPrint(StringRef Name, const object::Archive::Child &C) {
502 if (Verbose)
503 outs() << "Printing " << Name << "\n";
504
505 Expected<StringRef> DataOrErr = C.getBuffer();
506 failIfError(E: DataOrErr.takeError());
507 StringRef Data = *DataOrErr;
508 outs().write(Ptr: Data.data(), Size: Data.size());
509}
510
511// Utility function for printing out the file mode when the 't' operation is in
512// verbose mode.
513static void printMode(unsigned mode) {
514 outs() << ((mode & 004) ? "r" : "-");
515 outs() << ((mode & 002) ? "w" : "-");
516 outs() << ((mode & 001) ? "x" : "-");
517}
518
519// Implement the 't' operation. This function prints out just
520// the file names of each of the members. However, if verbose mode is requested
521// ('v' modifier) then the file type, permission mode, user, group, size, and
522// modification time are also printed.
523static void doDisplayTable(StringRef Name, const object::Archive::Child &C) {
524 if (Verbose) {
525 Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
526 failIfError(E: ModeOrErr.takeError());
527 sys::fs::perms Mode = ModeOrErr.get();
528 printMode(mode: (Mode >> 6) & 007);
529 printMode(mode: (Mode >> 3) & 007);
530 printMode(mode: Mode & 007);
531 Expected<unsigned> UIDOrErr = C.getUID();
532 failIfError(E: UIDOrErr.takeError());
533 outs() << ' ' << UIDOrErr.get();
534 Expected<unsigned> GIDOrErr = C.getGID();
535 failIfError(E: GIDOrErr.takeError());
536 outs() << '/' << GIDOrErr.get();
537 Expected<uint64_t> Size = C.getSize();
538 failIfError(E: Size.takeError());
539 outs() << ' ' << format(Fmt: "%6llu", Vals: Size.get());
540 auto ModTimeOrErr = C.getLastModified();
541 failIfError(E: ModTimeOrErr.takeError());
542 // Note: formatv() only handles the default TimePoint<>, which is in
543 // nanoseconds.
544 // TODO: fix format_provider<TimePoint<>> to allow other units.
545 sys::TimePoint<> ModTimeInNs = ModTimeOrErr.get();
546 outs() << ' ' << formatv(Fmt: "{0:%b %e %H:%M %Y}", Vals&: ModTimeInNs);
547 outs() << ' ';
548 }
549
550 if (C.getParent()->isThin()) {
551 if (!sys::path::is_absolute(path: Name)) {
552 StringRef ParentDir = sys::path::parent_path(path: ArchiveName);
553 if (!ParentDir.empty())
554 outs() << sys::path::convert_to_slash(path: ParentDir) << '/';
555 }
556 outs() << Name;
557 } else {
558 outs() << Name;
559 if (DisplayMemberOffsets)
560 outs() << " 0x" << utohexstr(X: C.getDataOffset(), LowerCase: true);
561 }
562 outs() << '\n';
563}
564
565static std::string normalizePath(StringRef Path) {
566 return CompareFullPath ? sys::path::convert_to_slash(path: Path)
567 : std::string(sys::path::filename(path: Path));
568}
569
570static bool comparePaths(StringRef Path1, StringRef Path2) {
571// When on Windows this function calls CompareStringOrdinal
572// as Windows file paths are case-insensitive.
573// CompareStringOrdinal compares two Unicode strings for
574// binary equivalence and allows for case insensitivity.
575#ifdef _WIN32
576 SmallVector<wchar_t, 128> WPath1, WPath2;
577 failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path1), WPath1));
578 failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path2), WPath2));
579
580 return CompareStringOrdinal(WPath1.data(), WPath1.size(), WPath2.data(),
581 WPath2.size(), true) == CSTR_EQUAL;
582#else
583 return normalizePath(Path: Path1) == normalizePath(Path: Path2);
584#endif
585}
586
587// Implement the 'x' operation. This function extracts files back to the file
588// system.
589static void doExtract(StringRef Name, const object::Archive::Child &C) {
590 // Retain the original mode.
591 Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
592 failIfError(E: ModeOrErr.takeError());
593 sys::fs::perms Mode = ModeOrErr.get();
594
595 StringRef outputFilePath;
596 SmallString<128> path;
597 if (OutputDir.empty()) {
598 outputFilePath = sys::path::filename(path: Name);
599 } else {
600 sys::path::append(path, a: OutputDir, b: sys::path::filename(path: Name));
601 outputFilePath = path.str();
602 }
603
604 if (Verbose)
605 outs() << "x - " << outputFilePath << '\n';
606
607 int FD;
608 failIfError(EC: sys::fs::openFileForWrite(Name: outputFilePath, ResultFD&: FD,
609 Disp: sys::fs::CD_CreateAlways,
610 Flags: sys::fs::OF_None, Mode),
611 Context: Name);
612
613 {
614 raw_fd_ostream file(FD, false);
615
616 // Get the data and its length
617 Expected<StringRef> BufOrErr = C.getBuffer();
618 failIfError(E: BufOrErr.takeError());
619 StringRef Data = BufOrErr.get();
620
621 // Write the data.
622 file.write(Ptr: Data.data(), Size: Data.size());
623 }
624
625 // If we're supposed to retain the original modification times, etc. do so
626 // now.
627 if (OriginalDates) {
628 auto ModTimeOrErr = C.getLastModified();
629 failIfError(E: ModTimeOrErr.takeError());
630 failIfError(
631 EC: sys::fs::setLastAccessAndModificationTime(FD, Time: ModTimeOrErr.get()));
632 }
633
634 if (close(fd: FD))
635 fail(Error: "Could not close the file");
636}
637
638static bool shouldCreateArchive(ArchiveOperation Op) {
639 switch (Op) {
640 case Print:
641 case Delete:
642 case Move:
643 case DisplayTable:
644 case Extract:
645 case CreateSymTab:
646 return false;
647
648 case QuickAppend:
649 case ReplaceOrInsert:
650 return true;
651 }
652
653 llvm_unreachable("Missing entry in covered switch.");
654}
655
656static bool isValidInBitMode(Binary &Bin) {
657 if (BitMode == BitModeTy::Bit32_64 || BitMode == BitModeTy::Any)
658 return true;
659
660 if (SymbolicFile *SymFile = dyn_cast<SymbolicFile>(Val: &Bin)) {
661 bool Is64Bit = SymFile->is64Bit();
662 if ((Is64Bit && (BitMode == BitModeTy::Bit32)) ||
663 (!Is64Bit && (BitMode == BitModeTy::Bit64)))
664 return false;
665 }
666 // In AIX "ar", non-object files are always considered to have a valid bit
667 // mode.
668 return true;
669}
670
671Expected<std::unique_ptr<Binary>> getAsBinary(const NewArchiveMember &NM,
672 LLVMContext *Context) {
673 auto BinaryOrErr = createBinary(Source: NM.Buf->getMemBufferRef(), Context);
674 if (BinaryOrErr)
675 return std::move(*BinaryOrErr);
676 return BinaryOrErr.takeError();
677}
678
679Expected<std::unique_ptr<Binary>> getAsBinary(const Archive::Child &C,
680 LLVMContext *Context) {
681 return C.getAsBinary(Context);
682}
683
684template <class A> static bool isValidInBitMode(const A &Member) {
685 if (object::Archive::getDefaultKind() != object::Archive::K_AIXBIG)
686 return true;
687 LLVMContext Context;
688 Expected<std::unique_ptr<Binary>> BinOrErr = getAsBinary(Member, &Context);
689 // In AIX "ar", if there is a non-object file member, it is never ignored due
690 // to the bit mode setting.
691 if (!BinOrErr) {
692 consumeError(Err: BinOrErr.takeError());
693 return true;
694 }
695 return isValidInBitMode(Bin&: *BinOrErr.get());
696}
697
698static void warnInvalidObjectForFileMode(Twine Name) {
699 warn(Message: "'" + Name + "' is not valid with the current object file mode");
700}
701
702static void performReadOperation(ArchiveOperation Operation,
703 object::Archive *OldArchive) {
704 if (Operation == Extract && OldArchive->isThin())
705 fail(Error: "extracting from a thin archive is not supported");
706
707 bool Filter = !Members.empty();
708 StringMap<int> MemberCount;
709 {
710 Error Err = Error::success();
711 for (auto &C : OldArchive->children(Err)) {
712 Expected<StringRef> NameOrErr = C.getName();
713 failIfError(E: NameOrErr.takeError());
714 StringRef Name = NameOrErr.get();
715
716 // Check whether to ignore this object due to its bitness.
717 if (!isValidInBitMode(Member: C))
718 continue;
719
720 if (Filter) {
721 auto I = find_if(Range&: Members, P: [Name](StringRef Path) {
722 return comparePaths(Path1: Name, Path2: Path);
723 });
724 if (I == Members.end())
725 continue;
726 if (CountParam) {
727 std::string CountKey = normalizePath(Path: *I);
728 if (++MemberCount[CountKey] != CountParam)
729 continue;
730 }
731 Members.erase(position: I);
732 }
733
734 switch (Operation) {
735 default:
736 llvm_unreachable("Not a read operation");
737 case Print:
738 doPrint(Name, C);
739 break;
740 case DisplayTable:
741 doDisplayTable(Name, C);
742 break;
743 case Extract:
744 doExtract(Name, C);
745 break;
746 }
747 }
748 failIfError(E: std::move(Err));
749 }
750
751 if (Members.empty())
752 return;
753 for (StringRef Name : Members)
754 WithColor::error(OS&: errs(), Prefix: ToolName) << "'" << Name << "' was not found\n";
755 exit(status: 1);
756}
757
758static void addChildMember(std::vector<NewArchiveMember> &Members,
759 const object::Archive::Child &M,
760 bool FlattenArchive = false) {
761 Expected<NewArchiveMember> NMOrErr =
762 NewArchiveMember::getOldMember(OldMember: M, Deterministic);
763 failIfError(E: NMOrErr.takeError());
764 // If the child member we're trying to add is thin, use the path relative to
765 // the archive it's in, so the file resolves correctly.
766 if (Thin && FlattenArchive) {
767 StringSaver Saver(Alloc);
768 Expected<std::string> FileNameOrErr(M.getName());
769 failIfError(E: FileNameOrErr.takeError());
770 if (sys::path::is_absolute(path: *FileNameOrErr)) {
771 NMOrErr->MemberName = Saver.save(S: sys::path::convert_to_slash(path: *FileNameOrErr));
772 } else {
773 FileNameOrErr = M.getFullName();
774 failIfError(E: FileNameOrErr.takeError());
775 Expected<std::string> PathOrErr =
776 computeArchiveRelativePath(From: ArchiveName, To: *FileNameOrErr);
777 NMOrErr->MemberName = Saver.save(
778 S: PathOrErr ? *PathOrErr : sys::path::convert_to_slash(path: *FileNameOrErr));
779 }
780 }
781 if (FlattenArchive &&
782 identify_magic(magic: NMOrErr->Buf->getBuffer()) == file_magic::archive) {
783 Expected<std::string> FileNameOrErr = M.getFullName();
784 failIfError(E: FileNameOrErr.takeError());
785 object::Archive &Lib = readLibrary(Library: *FileNameOrErr);
786 // When creating thin archives, only flatten if the member is also thin.
787 if (!Thin || Lib.isThin()) {
788 Error Err = Error::success();
789 // Only Thin archives are recursively flattened.
790 for (auto &Child : Lib.children(Err))
791 addChildMember(Members, M: Child, /*FlattenArchive=*/Thin);
792 failIfError(E: std::move(Err));
793 return;
794 }
795 }
796 Members.push_back(x: std::move(*NMOrErr));
797}
798
799static NewArchiveMember getArchiveMember(StringRef FileName) {
800 Expected<NewArchiveMember> NMOrErr =
801 NewArchiveMember::getFile(FileName, Deterministic);
802 failIfError(E: NMOrErr.takeError(), Context: FileName);
803 StringSaver Saver(Alloc);
804 // For regular archives, use the basename of the object path for the member
805 // name. For thin archives, use the full relative paths so the file resolves
806 // correctly.
807 if (!Thin) {
808 NMOrErr->MemberName = sys::path::filename(path: NMOrErr->MemberName);
809 } else {
810 if (sys::path::is_absolute(path: FileName))
811 NMOrErr->MemberName = Saver.save(S: sys::path::convert_to_slash(path: FileName));
812 else {
813 Expected<std::string> PathOrErr =
814 computeArchiveRelativePath(From: ArchiveName, To: FileName);
815 NMOrErr->MemberName = Saver.save(
816 S: PathOrErr ? *PathOrErr : sys::path::convert_to_slash(path: FileName));
817 }
818 }
819 return std::move(*NMOrErr);
820}
821
822static void addMember(std::vector<NewArchiveMember> &Members,
823 NewArchiveMember &NM) {
824 Members.push_back(x: std::move(NM));
825}
826
827static void addMember(std::vector<NewArchiveMember> &Members,
828 StringRef FileName, bool FlattenArchive = false) {
829 NewArchiveMember NM = getArchiveMember(FileName);
830 if (!isValidInBitMode(Member: NM)) {
831 warnInvalidObjectForFileMode(Name: FileName);
832 return;
833 }
834
835 if (FlattenArchive &&
836 identify_magic(magic: NM.Buf->getBuffer()) == file_magic::archive) {
837 object::Archive &Lib = readLibrary(Library: FileName);
838 // When creating thin archives, only flatten if the member is also thin.
839 if (!Thin || Lib.isThin()) {
840 Error Err = Error::success();
841 // Only Thin archives are recursively flattened.
842 for (auto &Child : Lib.children(Err))
843 addChildMember(Members, M: Child, /*FlattenArchive=*/Thin);
844 failIfError(E: std::move(Err));
845 return;
846 }
847 }
848 Members.push_back(x: std::move(NM));
849}
850
851enum InsertAction {
852 IA_AddOldMember,
853 IA_AddNewMember,
854 IA_Delete,
855 IA_MoveOldMember,
856 IA_MoveNewMember
857};
858
859static InsertAction computeInsertAction(ArchiveOperation Operation,
860 const object::Archive::Child &Member,
861 StringRef Name,
862 std::vector<StringRef>::iterator &Pos,
863 StringMap<int> &MemberCount) {
864 if (!isValidInBitMode(Member))
865 return IA_AddOldMember;
866
867 if (Operation == QuickAppend || Members.empty())
868 return IA_AddOldMember;
869
870 std::string CountKey;
871 auto MI = find_if(Range&: Members, P: [Name, &CountKey](StringRef Path) {
872 SmallString<128> MatchPath(Path);
873 if (Thin && !sys::path::is_absolute(path: Path)) {
874 Expected<std::string> PathOrErr =
875 computeArchiveRelativePath(From: ArchiveName, To: Path);
876 if (PathOrErr)
877 MatchPath = *PathOrErr;
878 }
879 if (!comparePaths(Path1: Name, Path2: MatchPath))
880 return false;
881 CountKey = normalizePath(Path: MatchPath);
882 return true;
883 });
884
885 if (MI == Members.end())
886 return IA_AddOldMember;
887
888 Pos = MI;
889
890 if (Operation == Delete) {
891 if (CountParam && ++MemberCount[CountKey] != CountParam)
892 return IA_AddOldMember;
893 return IA_Delete;
894 }
895
896 if (Operation == Move)
897 return IA_MoveOldMember;
898
899 if (Operation == ReplaceOrInsert) {
900 if (!OnlyUpdate) {
901 if (RelPos.empty())
902 return IA_AddNewMember;
903 return IA_MoveNewMember;
904 }
905
906 // We could try to optimize this to a fstat, but it is not a common
907 // operation.
908 sys::fs::file_status Status;
909 failIfError(EC: sys::fs::status(path: *MI, result&: Status), Context: *MI);
910 auto ModTimeOrErr = Member.getLastModified();
911 failIfError(E: ModTimeOrErr.takeError());
912 if (Status.getLastModificationTime() < ModTimeOrErr.get()) {
913 if (RelPos.empty())
914 return IA_AddOldMember;
915 return IA_MoveOldMember;
916 }
917
918 if (RelPos.empty())
919 return IA_AddNewMember;
920 return IA_MoveNewMember;
921 }
922 llvm_unreachable("No such operation");
923}
924
925// We have to walk this twice and computing it is not trivial, so creating an
926// explicit std::vector is actually fairly efficient.
927static std::vector<NewArchiveMember>
928computeNewArchiveMembers(ArchiveOperation Operation,
929 object::Archive *OldArchive) {
930 std::vector<NewArchiveMember> Ret;
931 std::vector<NewArchiveMember> Moved;
932 int InsertPos = -1;
933 if (OldArchive) {
934 Error Err = Error::success();
935 StringMap<int> MemberCount;
936 for (auto &Child : OldArchive->children(Err)) {
937 int Pos = Ret.size();
938 Expected<StringRef> NameOrErr = Child.getName();
939 failIfError(E: NameOrErr.takeError());
940 std::string Name = std::string(NameOrErr.get());
941 if (comparePaths(Path1: Name, Path2: RelPos) && isValidInBitMode(Member: Child)) {
942 assert(AddAfter || AddBefore);
943 if (AddBefore)
944 InsertPos = Pos;
945 else
946 InsertPos = Pos + 1;
947 }
948
949 std::vector<StringRef>::iterator MemberI = Members.end();
950 InsertAction Action =
951 computeInsertAction(Operation, Member: Child, Name, Pos&: MemberI, MemberCount);
952
953 auto HandleNewMember = [](auto Member, auto &Members, auto &Child) {
954 NewArchiveMember NM = getArchiveMember(*Member);
955 if (isValidInBitMode(Member: NM))
956 addMember(Members, NM);
957 else {
958 // If a new member is not a valid object for the bit mode, add
959 // the old member back.
960 warnInvalidObjectForFileMode(*Member);
961 addChildMember(Members, Child, /*FlattenArchive=*/Thin);
962 }
963 };
964
965 switch (Action) {
966 case IA_AddOldMember:
967 addChildMember(Members&: Ret, M: Child, /*FlattenArchive=*/Thin);
968 break;
969 case IA_AddNewMember:
970 HandleNewMember(MemberI, Ret, Child);
971 break;
972 case IA_Delete:
973 break;
974 case IA_MoveOldMember:
975 addChildMember(Members&: Moved, M: Child, /*FlattenArchive=*/Thin);
976 break;
977 case IA_MoveNewMember:
978 HandleNewMember(MemberI, Moved, Child);
979 break;
980 }
981 // When processing elements with the count param, we need to preserve the
982 // full members list when iterating over all archive members. For
983 // instance, "llvm-ar dN 2 archive.a member.o" should delete the second
984 // file named member.o it sees; we are not done with member.o the first
985 // time we see it in the archive.
986 if (MemberI != Members.end() && !CountParam)
987 Members.erase(position: MemberI);
988 }
989 failIfError(E: std::move(Err));
990 }
991
992 if (Operation == Delete)
993 return Ret;
994
995 if (!RelPos.empty() && InsertPos == -1)
996 fail(Error: "insertion point not found");
997
998 if (RelPos.empty())
999 InsertPos = Ret.size();
1000
1001 assert(unsigned(InsertPos) <= Ret.size());
1002 int Pos = InsertPos;
1003 for (auto &M : Moved) {
1004 Ret.insert(position: Ret.begin() + Pos, x: std::move(M));
1005 ++Pos;
1006 }
1007
1008 if (AddLibrary) {
1009 assert(Operation == QuickAppend);
1010 for (auto &Member : Members)
1011 addMember(Members&: Ret, FileName: Member, /*FlattenArchive=*/true);
1012 return Ret;
1013 }
1014
1015 std::vector<NewArchiveMember> NewMembers;
1016 for (auto &Member : Members)
1017 addMember(Members&: NewMembers, FileName: Member, /*FlattenArchive=*/Thin);
1018 Ret.reserve(n: Ret.size() + NewMembers.size());
1019 std::move(first: NewMembers.begin(), last: NewMembers.end(),
1020 result: std::inserter(x&: Ret, i: std::next(x: Ret.begin(), n: InsertPos)));
1021
1022 return Ret;
1023}
1024
1025static void performWriteOperation(ArchiveOperation Operation,
1026 object::Archive *OldArchive,
1027 std::unique_ptr<MemoryBuffer> OldArchiveBuf,
1028 std::vector<NewArchiveMember> *NewMembersP) {
1029 if (OldArchive) {
1030 if (Thin && !OldArchive->isThin())
1031 fail(Error: "cannot convert a regular archive to a thin one");
1032
1033 if (OldArchive->isThin())
1034 Thin = true;
1035 }
1036
1037 std::vector<NewArchiveMember> NewMembers;
1038 if (!NewMembersP)
1039 NewMembers = computeNewArchiveMembers(Operation, OldArchive);
1040
1041 object::Archive::Kind Kind;
1042 switch (FormatType) {
1043 case Default:
1044 if (Thin)
1045 Kind = object::Archive::K_GNU;
1046 else if (OldArchive) {
1047 Kind = OldArchive->kind();
1048 std::optional<object::Archive::Kind> AltKind;
1049 if (Kind == object::Archive::K_BSD)
1050 AltKind = object::Archive::K_DARWIN;
1051 else if (Kind == object::Archive::K_GNU && !OldArchive->hasSymbolTable())
1052 // If there is no symbol table, we can't tell GNU from COFF format
1053 // from the old archive type.
1054 AltKind = object::Archive::K_COFF;
1055 if (AltKind) {
1056 auto InferredKind = Kind;
1057 if (NewMembersP && !NewMembersP->empty())
1058 InferredKind = NewMembersP->front().detectKindFromObject();
1059 else if (!NewMembers.empty())
1060 InferredKind = NewMembers.front().detectKindFromObject();
1061 if (InferredKind == AltKind)
1062 Kind = *AltKind;
1063 }
1064 } else if (NewMembersP)
1065 Kind = !NewMembersP->empty() ? NewMembersP->front().detectKindFromObject()
1066 : object::Archive::getDefaultKind();
1067 else
1068 Kind = !NewMembers.empty() ? NewMembers.front().detectKindFromObject()
1069 : object::Archive::getDefaultKind();
1070 break;
1071 case GNU:
1072 Kind = object::Archive::K_GNU;
1073 break;
1074 case COFF:
1075 Kind = object::Archive::K_COFF;
1076 break;
1077 case BSD:
1078 if (Thin)
1079 fail(Error: "only the gnu format has a thin mode");
1080 Kind = object::Archive::K_BSD;
1081 break;
1082 case DARWIN:
1083 if (Thin)
1084 fail(Error: "only the gnu format has a thin mode");
1085 Kind = object::Archive::K_DARWIN;
1086 break;
1087 case BIGARCHIVE:
1088 if (Thin)
1089 fail(Error: "only the gnu format has a thin mode");
1090 Kind = object::Archive::K_AIXBIG;
1091 break;
1092 case ZOSARCHIVE:
1093 if (Thin)
1094 fail(Error: "only the gnu format has a thin mode");
1095 Kind = object::Archive::K_ZOS;
1096 break;
1097 case Unknown:
1098 llvm_unreachable("");
1099 }
1100
1101 Error E =
1102 writeArchive(ArcName: ArchiveName, NewMembers: NewMembersP ? *NewMembersP : NewMembers, WriteSymtab: Symtab,
1103 Kind, Deterministic, Thin, OldArchiveBuf: std::move(OldArchiveBuf));
1104 failIfError(E: std::move(E), Context: ArchiveName);
1105}
1106
1107static void createSymbolTable(object::Archive *OldArchive) {
1108 // When an archive is created or modified, if the s option is given, the
1109 // resulting archive will have a current symbol table. If the S option
1110 // is given, it will have no symbol table.
1111 // In summary, we only need to update the symbol table if we have none.
1112 // This is actually very common because of broken build systems that think
1113 // they have to run ranlib.
1114 if (OldArchive->hasSymbolTable()) {
1115 if (OldArchive->kind() != object::Archive::K_AIXBIG)
1116 return;
1117
1118 // For archives in the Big Archive format, the bit mode option specifies
1119 // which symbol table to generate. The presence of a symbol table that does
1120 // not match the specified bit mode does not prevent creation of the symbol
1121 // table that has been requested.
1122 if (OldArchive->kind() == object::Archive::K_AIXBIG) {
1123 BigArchive *BigArc = dyn_cast<BigArchive>(Val: OldArchive);
1124 if (BigArc->has32BitGlobalSymtab() &&
1125 Symtab == SymtabWritingMode::BigArchive32)
1126 return;
1127
1128 if (BigArc->has64BitGlobalSymtab() &&
1129 Symtab == SymtabWritingMode::BigArchive64)
1130 return;
1131
1132 if (BigArc->has32BitGlobalSymtab() && BigArc->has64BitGlobalSymtab() &&
1133 Symtab == SymtabWritingMode::NormalSymtab)
1134 return;
1135
1136 Symtab = SymtabWritingMode::NormalSymtab;
1137 }
1138 }
1139 if (OldArchive->isThin())
1140 Thin = true;
1141 performWriteOperation(Operation: CreateSymTab, OldArchive, OldArchiveBuf: nullptr, NewMembersP: nullptr);
1142}
1143
1144static void performOperation(ArchiveOperation Operation,
1145 object::Archive *OldArchive,
1146 std::unique_ptr<MemoryBuffer> OldArchiveBuf,
1147 std::vector<NewArchiveMember> *NewMembers) {
1148 switch (Operation) {
1149 case Print:
1150 case DisplayTable:
1151 case Extract:
1152 performReadOperation(Operation, OldArchive);
1153 return;
1154
1155 case Delete:
1156 case Move:
1157 case QuickAppend:
1158 case ReplaceOrInsert:
1159 performWriteOperation(Operation, OldArchive, OldArchiveBuf: std::move(OldArchiveBuf),
1160 NewMembersP: NewMembers);
1161 return;
1162 case CreateSymTab:
1163 createSymbolTable(OldArchive);
1164 return;
1165 }
1166 llvm_unreachable("Unknown operation.");
1167}
1168
1169static int performOperation(ArchiveOperation Operation) {
1170 // Create or open the archive object.
1171 ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(
1172 Filename: ArchiveName, /*IsText=*/false, /*RequiresNullTerminator=*/false);
1173 std::error_code EC = Buf.getError();
1174 if (EC && EC != errc::no_such_file_or_directory)
1175 fail(Error: "unable to open '" + ArchiveName + "': " + EC.message());
1176
1177 if (!EC) {
1178 Expected<std::unique_ptr<object::Archive>> ArchiveOrError =
1179 object::Archive::create(Source: Buf.get()->getMemBufferRef());
1180 if (!ArchiveOrError)
1181 failIfError(E: ArchiveOrError.takeError(),
1182 Context: "unable to load '" + ArchiveName + "'");
1183
1184 std::unique_ptr<object::Archive> Archive = std::move(ArchiveOrError.get());
1185 if (Archive->isThin())
1186 CompareFullPath = true;
1187 performOperation(Operation, OldArchive: Archive.get(), OldArchiveBuf: std::move(Buf.get()),
1188 /*NewMembers=*/nullptr);
1189 return 0;
1190 }
1191
1192 assert(EC == errc::no_such_file_or_directory);
1193
1194 if (!shouldCreateArchive(Op: Operation)) {
1195 failIfError(EC, Context: Twine("unable to load '") + ArchiveName + "'");
1196 } else {
1197 if (!Create) {
1198 // Produce a warning if we should and we're creating the archive
1199 warn(Message: "creating " + ArchiveName);
1200 }
1201 }
1202
1203 performOperation(Operation, OldArchive: nullptr, OldArchiveBuf: nullptr, /*NewMembers=*/nullptr);
1204 return 0;
1205}
1206
1207static void runMRIScript() {
1208 enum class MRICommand { AddLib, AddMod, Create, CreateThin, Delete, Save, End, Invalid };
1209
1210 ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getSTDIN();
1211 failIfError(EC: Buf.getError());
1212 const MemoryBuffer &Ref = *Buf.get();
1213 bool Saved = false;
1214 std::vector<NewArchiveMember> NewMembers;
1215 ParsingMRIScript = true;
1216
1217 for (line_iterator I(Ref, /*SkipBlanks*/ false), E; I != E; ++I) {
1218 ++MRILineNumber;
1219 StringRef Line = *I;
1220 Line = Line.split(Separator: ';').first;
1221 Line = Line.split(Separator: '*').first;
1222 Line = Line.trim();
1223 if (Line.empty())
1224 continue;
1225 StringRef CommandStr, Rest;
1226 std::tie(args&: CommandStr, args&: Rest) = Line.split(Separator: ' ');
1227 Rest = Rest.trim();
1228 if (!Rest.empty() && Rest.front() == '"' && Rest.back() == '"')
1229 Rest = Rest.drop_front().drop_back();
1230 auto Command = StringSwitch<MRICommand>(CommandStr.lower())
1231 .Case(S: "addlib", Value: MRICommand::AddLib)
1232 .Case(S: "addmod", Value: MRICommand::AddMod)
1233 .Case(S: "create", Value: MRICommand::Create)
1234 .Case(S: "createthin", Value: MRICommand::CreateThin)
1235 .Case(S: "delete", Value: MRICommand::Delete)
1236 .Case(S: "save", Value: MRICommand::Save)
1237 .Case(S: "end", Value: MRICommand::End)
1238 .Default(Value: MRICommand::Invalid);
1239
1240 switch (Command) {
1241 case MRICommand::AddLib: {
1242 if (!Create)
1243 fail(Error: "no output archive has been opened");
1244 object::Archive &Lib = readLibrary(Library: Rest);
1245 {
1246 if (Thin && !Lib.isThin())
1247 fail(Error: "cannot add a regular archive's contents to a thin archive");
1248 Error Err = Error::success();
1249 for (auto &Member : Lib.children(Err))
1250 addChildMember(Members&: NewMembers, M: Member, /*FlattenArchive=*/Thin);
1251 failIfError(E: std::move(Err));
1252 }
1253 break;
1254 }
1255 case MRICommand::AddMod:
1256 if (!Create)
1257 fail(Error: "no output archive has been opened");
1258 addMember(Members&: NewMembers, FileName: Rest);
1259 break;
1260 case MRICommand::CreateThin:
1261 Thin = true;
1262 [[fallthrough]];
1263 case MRICommand::Create:
1264 Create = true;
1265 if (!ArchiveName.empty())
1266 fail(Error: "editing multiple archives not supported");
1267 if (Saved)
1268 fail(Error: "file already saved");
1269 ArchiveName = std::string(Rest);
1270 if (ArchiveName.empty())
1271 fail(Error: "missing archive name");
1272 break;
1273 case MRICommand::Delete: {
1274 llvm::erase_if(C&: NewMembers, P: [=](NewArchiveMember &M) {
1275 return comparePaths(Path1: M.MemberName, Path2: Rest);
1276 });
1277 break;
1278 }
1279 case MRICommand::Save:
1280 Saved = true;
1281 break;
1282 case MRICommand::End:
1283 break;
1284 case MRICommand::Invalid:
1285 fail(Error: "unknown command: " + CommandStr);
1286 }
1287 }
1288
1289 ParsingMRIScript = false;
1290
1291 // Nothing to do if not saved.
1292 if (Saved)
1293 performOperation(Operation: ReplaceOrInsert, /*OldArchive=*/nullptr,
1294 /*OldArchiveBuf=*/nullptr, NewMembers: &NewMembers);
1295 exit(status: 0);
1296}
1297
1298static bool handleGenericOption(StringRef arg) {
1299 if (arg == "--help" || arg == "-h") {
1300 printHelpMessage();
1301 return true;
1302 }
1303 if (arg == "--version") {
1304 cl::PrintVersionMessage();
1305 return true;
1306 }
1307 return false;
1308}
1309
1310static BitModeTy getBitMode(const char *RawBitMode) {
1311 return StringSwitch<BitModeTy>(RawBitMode)
1312 .Case(S: "32", Value: BitModeTy::Bit32)
1313 .Case(S: "64", Value: BitModeTy::Bit64)
1314 .Case(S: "32_64", Value: BitModeTy::Bit32_64)
1315 .Case(S: "any", Value: BitModeTy::Any)
1316 .Default(Value: BitModeTy::Unknown);
1317}
1318
1319static const char *matchFlagWithArg(StringRef Expected,
1320 ArrayRef<const char *>::iterator &ArgIt,
1321 ArrayRef<const char *> Args) {
1322 StringRef Arg = *ArgIt;
1323
1324 Arg.consume_front(Prefix: "--");
1325
1326 size_t len = Expected.size();
1327 if (Arg == Expected) {
1328 if (++ArgIt == Args.end())
1329 fail(Error: std::string(Expected) + " requires an argument");
1330
1331 return *ArgIt;
1332 }
1333 if (Arg.starts_with(Prefix: Expected) && Arg.size() > len && Arg[len] == '=')
1334 return Arg.data() + len + 1;
1335
1336 return nullptr;
1337}
1338
1339static cl::TokenizerCallback getRspQuoting(ArrayRef<const char *> ArgsArr) {
1340 cl::TokenizerCallback Ret =
1341 Triple(sys::getProcessTriple()).getOS() == Triple::Win32
1342 ? cl::TokenizeWindowsCommandLine
1343 : cl::TokenizeGNUCommandLine;
1344
1345 for (ArrayRef<const char *>::iterator ArgIt = ArgsArr.begin();
1346 ArgIt != ArgsArr.end(); ++ArgIt) {
1347 if (const char *Match = matchFlagWithArg(Expected: "rsp-quoting", ArgIt, Args: ArgsArr)) {
1348 StringRef MatchRef = Match;
1349 if (MatchRef == "posix")
1350 Ret = cl::TokenizeGNUCommandLine;
1351 else if (MatchRef == "windows")
1352 Ret = cl::TokenizeWindowsCommandLine;
1353 else
1354 fail(Error: std::string("Invalid response file quoting style ") + Match);
1355 }
1356 }
1357
1358 return Ret;
1359}
1360
1361static int ar_main(int argc, char **argv) {
1362 SmallVector<const char *, 0> Argv(argv + 1, argv + argc);
1363 StringSaver Saver(Alloc);
1364
1365 cl::ExpandResponseFiles(Saver, Tokenizer: getRspQuoting(ArgsArr: ArrayRef(argv, argc)), Argv);
1366
1367 // Get BitMode from enviorment variable "OBJECT_MODE" for AIX OS, if
1368 // specified.
1369 if (object::Archive::getDefaultKind() == object::Archive::K_AIXBIG) {
1370 BitMode = getBitMode(RawBitMode: getenv(name: "OBJECT_MODE"));
1371 if (BitMode == BitModeTy::Unknown)
1372 BitMode = BitModeTy::Bit32;
1373 }
1374
1375 for (ArrayRef<const char *>::iterator ArgIt = Argv.begin();
1376 ArgIt != Argv.end(); ++ArgIt) {
1377 const char *Match = nullptr;
1378
1379 if (handleGenericOption(arg: *ArgIt))
1380 return 0;
1381 if (strcmp(s1: *ArgIt, s2: "--") == 0) {
1382 ++ArgIt;
1383 for (; ArgIt != Argv.end(); ++ArgIt)
1384 PositionalArgs.push_back(Elt: *ArgIt);
1385 break;
1386 }
1387
1388 if (*ArgIt[0] != '-') {
1389 if (Options.empty())
1390 Options += *ArgIt;
1391 else
1392 PositionalArgs.push_back(Elt: *ArgIt);
1393 continue;
1394 }
1395
1396 if (strcmp(s1: *ArgIt, s2: "-M") == 0) {
1397 MRI = true;
1398 continue;
1399 }
1400
1401 if (strcmp(s1: *ArgIt, s2: "--thin") == 0) {
1402 Thin = true;
1403 continue;
1404 }
1405
1406 Match = matchFlagWithArg(Expected: "format", ArgIt, Args: Argv);
1407 if (Match) {
1408 FormatType = StringSwitch<Format>(Match)
1409 .Case(S: "default", Value: Default)
1410 .Case(S: "gnu", Value: GNU)
1411 .Case(S: "darwin", Value: DARWIN)
1412 .Case(S: "bsd", Value: BSD)
1413 .Case(S: "bigarchive", Value: BIGARCHIVE)
1414 .Case(S: "coff", Value: COFF)
1415 .Case(S: "zos", Value: ZOSARCHIVE)
1416 .Default(Value: Unknown);
1417 if (FormatType == Unknown)
1418 fail(Error: std::string("Invalid format ") + Match);
1419 continue;
1420 }
1421
1422 if ((Match = matchFlagWithArg(Expected: "output", ArgIt, Args: Argv))) {
1423 OutputDir = Match;
1424 continue;
1425 }
1426
1427 if (matchFlagWithArg(Expected: "plugin", ArgIt, Args: Argv) ||
1428 matchFlagWithArg(Expected: "rsp-quoting", ArgIt, Args: Argv))
1429 continue;
1430
1431 if (strncmp(s1: *ArgIt, s2: "-X", n: 2) == 0) {
1432 if (object::Archive::getDefaultKind() == object::Archive::K_AIXBIG) {
1433 Match = *(*ArgIt + 2) != '\0' ? *ArgIt + 2 : *(++ArgIt);
1434 BitMode = getBitMode(RawBitMode: Match);
1435 if (BitMode == BitModeTy::Unknown)
1436 fail(Error: Twine("invalid bit mode: ") + Match);
1437 continue;
1438 } else {
1439 fail(Error: Twine(*ArgIt) + " option not supported on non AIX OS");
1440 }
1441 }
1442
1443 Options += *ArgIt + 1;
1444 }
1445
1446 return performOperation(Operation: parseCommandLine());
1447}
1448
1449static int ranlib_main(int argc, char **argv) {
1450 std::vector<StringRef> Archives;
1451 bool HasAIXXOption = false;
1452
1453 for (int i = 1; i < argc; ++i) {
1454 StringRef arg(argv[i]);
1455 if (handleGenericOption(arg)) {
1456 return 0;
1457 } else if (arg.consume_front(Prefix: "-")) {
1458 // Handle the -D/-U flag
1459 while (!arg.empty()) {
1460 if (arg.front() == 'D') {
1461 Deterministic = true;
1462 } else if (arg.front() == 'U') {
1463 Deterministic = false;
1464 } else if (arg.front() == 'h') {
1465 printHelpMessage();
1466 return 0;
1467 } else if (arg.front() == 'V') {
1468 cl::PrintVersionMessage();
1469 return 0;
1470 } else if (arg.front() == 'X') {
1471 if (object::Archive::getDefaultKind() == object::Archive::K_AIXBIG) {
1472 HasAIXXOption = true;
1473 arg.consume_front(Prefix: "X");
1474 const char *Xarg = arg.data();
1475 if (Xarg[0] == '\0') {
1476 if (argv[i + 1][0] != '-')
1477 BitMode = getBitMode(RawBitMode: argv[++i]);
1478 else
1479 BitMode = BitModeTy::Unknown;
1480 } else
1481 BitMode = getBitMode(RawBitMode: arg.data());
1482
1483 if (BitMode == BitModeTy::Unknown)
1484 fail(Error: "the specified object mode is not valid. Specify -X32, "
1485 "-X64, -X32_64, or -Xany");
1486 } else {
1487 fail(Error: Twine("-") + Twine(arg) +
1488 " option not supported on non AIX OS");
1489 }
1490 break;
1491 } else {
1492 // TODO: GNU ranlib also supports a -t flag
1493 fail(Error: "Invalid option: '-" + arg + "'");
1494 }
1495 arg = arg.drop_front(N: 1);
1496 }
1497 } else {
1498 Archives.push_back(x: arg);
1499 }
1500 }
1501
1502 if (object::Archive::getDefaultKind() == object::Archive::K_AIXBIG) {
1503 // If not specify -X option, get BitMode from enviorment variable
1504 // "OBJECT_MODE" for AIX OS if specify.
1505 if (!HasAIXXOption) {
1506 if (char *EnvObjectMode = getenv(name: "OBJECT_MODE")) {
1507 BitMode = getBitMode(RawBitMode: EnvObjectMode);
1508 if (BitMode == BitModeTy::Unknown)
1509 fail(Error: "the OBJECT_MODE environment variable has an invalid value. "
1510 "OBJECT_MODE must be 32, 64, 32_64, or any");
1511 }
1512 }
1513
1514 switch (BitMode) {
1515 case BitModeTy::Bit32:
1516 Symtab = SymtabWritingMode::BigArchive32;
1517 break;
1518 case BitModeTy::Bit64:
1519 Symtab = SymtabWritingMode::BigArchive64;
1520 break;
1521 default:
1522 Symtab = SymtabWritingMode::NormalSymtab;
1523 break;
1524 }
1525 }
1526
1527 for (StringRef Archive : Archives) {
1528 ArchiveName = Archive.str();
1529 performOperation(Operation: CreateSymTab);
1530 }
1531 if (Archives.empty())
1532 badUsage(Error: "an archive name must be specified");
1533 return 0;
1534}
1535
1536int llvm_ar_main(int argc, char **argv, const llvm::ToolContext &) {
1537 ToolName = argv[0];
1538
1539 llvm::InitializeAllTargetInfos();
1540 llvm::InitializeAllTargetMCs();
1541 llvm::InitializeAllAsmParsers();
1542
1543 Stem = sys::path::stem(path: ToolName);
1544 auto Is = [](StringRef Tool) {
1545 // We need to recognize the following filenames.
1546 //
1547 // Lib.exe -> lib (see D44808, MSBuild runs Lib.exe)
1548 // dlltool.exe -> dlltool
1549 // arm-pokymllib32-linux-gnueabi-llvm-ar-10 -> ar
1550 auto I = Stem.rfind_insensitive(Str: Tool);
1551 return I != StringRef::npos &&
1552 (I + Tool.size() == Stem.size() || !isAlnum(C: Stem[I + Tool.size()]));
1553 };
1554
1555 if (Is("dlltool"))
1556 return dlltoolDriverMain(ArgsArr: ArrayRef(argv, argc));
1557 if (Is("ranlib"))
1558 return ranlib_main(argc, argv);
1559 if (Is("lib"))
1560 return libDriverMain(ARgs: ArrayRef(argv, argc));
1561 if (Is("ar"))
1562 return ar_main(argc, argv);
1563
1564 fail(Error: "not ranlib, ar, lib or dlltool");
1565}
1566