Bug Summary

File:build/source/llvm/tools/llvm-ar/llvm-ar.cpp
Warning:line 1371, column 9
Null pointer passed to 1st parameter expecting 'nonnull'

Annotated Source Code

Press '?' to see keyboard shortcuts

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