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