xref: /freebsd-src/contrib/llvm-project/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===- LibDriver.cpp - lib.exe-compatible driver --------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // Defines an interface to a lib.exe-compatible driver that also understands
100b57cec5SDimitry Andric // bitcode files. Used by llvm-lib and lld-link /lib.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
150b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
168bcb0991SDimitry Andric #include "llvm/ADT/StringSet.h"
170b57cec5SDimitry Andric #include "llvm/BinaryFormat/COFF.h"
180b57cec5SDimitry Andric #include "llvm/BinaryFormat/Magic.h"
190b57cec5SDimitry Andric #include "llvm/Bitcode/BitcodeReader.h"
200b57cec5SDimitry Andric #include "llvm/Object/ArchiveWriter.h"
210b57cec5SDimitry Andric #include "llvm/Object/COFF.h"
2206c3fb27SDimitry Andric #include "llvm/Object/COFFModuleDefinition.h"
230b57cec5SDimitry Andric #include "llvm/Object/WindowsMachineFlag.h"
240b57cec5SDimitry Andric #include "llvm/Option/Arg.h"
250b57cec5SDimitry Andric #include "llvm/Option/ArgList.h"
265f757f3fSDimitry Andric #include "llvm/Option/OptTable.h"
270b57cec5SDimitry Andric #include "llvm/Option/Option.h"
280b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
290b57cec5SDimitry Andric #include "llvm/Support/Path.h"
300b57cec5SDimitry Andric #include "llvm/Support/Process.h"
310b57cec5SDimitry Andric #include "llvm/Support/StringSaver.h"
320b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
33bdd1243dSDimitry Andric #include <optional>
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric using namespace llvm;
3606c3fb27SDimitry Andric using namespace llvm::object;
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric namespace {
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric enum {
410b57cec5SDimitry Andric   OPT_INVALID = 0,
425f757f3fSDimitry Andric #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
430b57cec5SDimitry Andric #include "Options.inc"
440b57cec5SDimitry Andric #undef OPTION
450b57cec5SDimitry Andric };
460b57cec5SDimitry Andric 
47bdd1243dSDimitry Andric #define PREFIX(NAME, VALUE)                                                    \
48bdd1243dSDimitry Andric   static constexpr StringLiteral NAME##_init[] = VALUE;                        \
49bdd1243dSDimitry Andric   static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
50bdd1243dSDimitry Andric                                                 std::size(NAME##_init) - 1);
510b57cec5SDimitry Andric #include "Options.inc"
520b57cec5SDimitry Andric #undef PREFIX
530b57cec5SDimitry Andric 
545f757f3fSDimitry Andric using namespace llvm::opt;
55bdd1243dSDimitry Andric static constexpr opt::OptTable::Info InfoTable[] = {
565f757f3fSDimitry Andric #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
570b57cec5SDimitry Andric #include "Options.inc"
580b57cec5SDimitry Andric #undef OPTION
590b57cec5SDimitry Andric };
600b57cec5SDimitry Andric 
61bdd1243dSDimitry Andric class LibOptTable : public opt::GenericOptTable {
620b57cec5SDimitry Andric public:
63bdd1243dSDimitry Andric   LibOptTable() : opt::GenericOptTable(InfoTable, true) {}
640b57cec5SDimitry Andric };
6506c3fb27SDimitry Andric } // namespace
660b57cec5SDimitry Andric 
675ffd83dbSDimitry Andric static std::string getDefaultOutputPath(const NewArchiveMember &FirstMember) {
680b57cec5SDimitry Andric   SmallString<128> Val = StringRef(FirstMember.Buf->getBufferIdentifier());
690b57cec5SDimitry Andric   sys::path::replace_extension(Val, ".lib");
707a6dacacSDimitry Andric   return std::string(Val);
710b57cec5SDimitry Andric }
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric static std::vector<StringRef> getSearchPaths(opt::InputArgList *Args,
740b57cec5SDimitry Andric                                              StringSaver &Saver) {
750b57cec5SDimitry Andric   std::vector<StringRef> Ret;
760b57cec5SDimitry Andric   // Add current directory as first item of the search path.
770b57cec5SDimitry Andric   Ret.push_back("");
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric   // Add /libpath flags.
800b57cec5SDimitry Andric   for (auto *Arg : Args->filtered(OPT_libpath))
810b57cec5SDimitry Andric     Ret.push_back(Arg->getValue());
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric   // Add $LIB.
84bdd1243dSDimitry Andric   std::optional<std::string> EnvOpt = sys::Process::GetEnv("LIB");
8581ad6265SDimitry Andric   if (!EnvOpt)
860b57cec5SDimitry Andric     return Ret;
870b57cec5SDimitry Andric   StringRef Env = Saver.save(*EnvOpt);
880b57cec5SDimitry Andric   while (!Env.empty()) {
890b57cec5SDimitry Andric     StringRef Path;
900b57cec5SDimitry Andric     std::tie(Path, Env) = Env.split(';');
910b57cec5SDimitry Andric     Ret.push_back(Path);
920b57cec5SDimitry Andric   }
930b57cec5SDimitry Andric   return Ret;
940b57cec5SDimitry Andric }
950b57cec5SDimitry Andric 
9606c3fb27SDimitry Andric // Opens a file. Path has to be resolved already. (used for def file)
9706c3fb27SDimitry Andric std::unique_ptr<MemoryBuffer> openFile(const Twine &Path) {
98*0fca6ea1SDimitry Andric   ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =
99*0fca6ea1SDimitry Andric       MemoryBuffer::getFile(Path, /*IsText=*/true);
10006c3fb27SDimitry Andric 
10106c3fb27SDimitry Andric   if (std::error_code EC = MB.getError()) {
10206c3fb27SDimitry Andric     llvm::errs() << "cannot open file " << Path << ": " << EC.message() << "\n";
10306c3fb27SDimitry Andric     return nullptr;
10406c3fb27SDimitry Andric   }
10506c3fb27SDimitry Andric 
10606c3fb27SDimitry Andric   return std::move(*MB);
10706c3fb27SDimitry Andric }
10806c3fb27SDimitry Andric 
1090b57cec5SDimitry Andric static std::string findInputFile(StringRef File, ArrayRef<StringRef> Paths) {
1100b57cec5SDimitry Andric   for (StringRef Dir : Paths) {
1110b57cec5SDimitry Andric     SmallString<128> Path = Dir;
1120b57cec5SDimitry Andric     sys::path::append(Path, File);
1130b57cec5SDimitry Andric     if (sys::fs::exists(Path))
1145ffd83dbSDimitry Andric       return std::string(Path);
1150b57cec5SDimitry Andric   }
1160b57cec5SDimitry Andric   return "";
1170b57cec5SDimitry Andric }
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric static void fatalOpenError(llvm::Error E, Twine File) {
1200b57cec5SDimitry Andric   if (!E)
1210b57cec5SDimitry Andric     return;
1220b57cec5SDimitry Andric   handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
1230b57cec5SDimitry Andric     llvm::errs() << "error opening '" << File << "': " << EIB.message() << '\n';
1240b57cec5SDimitry Andric     exit(1);
1250b57cec5SDimitry Andric   });
1260b57cec5SDimitry Andric }
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric static void doList(opt::InputArgList &Args) {
1290b57cec5SDimitry Andric   // lib.exe prints the contents of the first archive file.
1300b57cec5SDimitry Andric   std::unique_ptr<MemoryBuffer> B;
1310b57cec5SDimitry Andric   for (auto *Arg : Args.filtered(OPT_INPUT)) {
1320b57cec5SDimitry Andric     // Create or open the archive object.
133fe6060f1SDimitry Andric     ErrorOr<std::unique_ptr<MemoryBuffer>> MaybeBuf = MemoryBuffer::getFile(
134fe6060f1SDimitry Andric         Arg->getValue(), /*IsText=*/false, /*RequiresNullTerminator=*/false);
1350b57cec5SDimitry Andric     fatalOpenError(errorCodeToError(MaybeBuf.getError()), Arg->getValue());
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric     if (identify_magic(MaybeBuf.get()->getBuffer()) == file_magic::archive) {
1380b57cec5SDimitry Andric       B = std::move(MaybeBuf.get());
1390b57cec5SDimitry Andric       break;
1400b57cec5SDimitry Andric     }
1410b57cec5SDimitry Andric   }
1420b57cec5SDimitry Andric 
1430b57cec5SDimitry Andric   // lib.exe doesn't print an error if no .lib files are passed.
1440b57cec5SDimitry Andric   if (!B)
1450b57cec5SDimitry Andric     return;
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric   Error Err = Error::success();
148*0fca6ea1SDimitry Andric   object::Archive Archive(B->getMemBufferRef(), Err);
1490b57cec5SDimitry Andric   fatalOpenError(std::move(Err), B->getBufferIdentifier());
1500b57cec5SDimitry Andric 
15106c3fb27SDimitry Andric   std::vector<StringRef> Names;
1520b57cec5SDimitry Andric   for (auto &C : Archive.children(Err)) {
1530b57cec5SDimitry Andric     Expected<StringRef> NameOrErr = C.getName();
1540b57cec5SDimitry Andric     fatalOpenError(NameOrErr.takeError(), B->getBufferIdentifier());
15506c3fb27SDimitry Andric     Names.push_back(NameOrErr.get());
1560b57cec5SDimitry Andric   }
15706c3fb27SDimitry Andric   for (auto Name : reverse(Names))
15806c3fb27SDimitry Andric     llvm::outs() << Name << '\n';
1590b57cec5SDimitry Andric   fatalOpenError(std::move(Err), B->getBufferIdentifier());
1600b57cec5SDimitry Andric }
1610b57cec5SDimitry Andric 
162e8d8bef9SDimitry Andric static Expected<COFF::MachineTypes> getCOFFFileMachine(MemoryBufferRef MB) {
1638bcb0991SDimitry Andric   std::error_code EC;
1645ffd83dbSDimitry Andric   auto Obj = object::COFFObjectFile::create(MB);
165e8d8bef9SDimitry Andric   if (!Obj)
166e8d8bef9SDimitry Andric     return Obj.takeError();
1678bcb0991SDimitry Andric 
1685ffd83dbSDimitry Andric   uint16_t Machine = (*Obj)->getMachine();
1698bcb0991SDimitry Andric   if (Machine != COFF::IMAGE_FILE_MACHINE_I386 &&
1708bcb0991SDimitry Andric       Machine != COFF::IMAGE_FILE_MACHINE_AMD64 &&
17106c3fb27SDimitry Andric       Machine != COFF::IMAGE_FILE_MACHINE_ARMNT && !COFF::isAnyArm64(Machine)) {
172e8d8bef9SDimitry Andric     return createStringError(inconvertibleErrorCode(),
173e8d8bef9SDimitry Andric                              "unknown machine: " + std::to_string(Machine));
1748bcb0991SDimitry Andric   }
1758bcb0991SDimitry Andric 
1768bcb0991SDimitry Andric   return static_cast<COFF::MachineTypes>(Machine);
1778bcb0991SDimitry Andric }
1788bcb0991SDimitry Andric 
179e8d8bef9SDimitry Andric static Expected<COFF::MachineTypes> getBitcodeFileMachine(MemoryBufferRef MB) {
1808bcb0991SDimitry Andric   Expected<std::string> TripleStr = getBitcodeTargetTriple(MB);
181e8d8bef9SDimitry Andric   if (!TripleStr)
182e8d8bef9SDimitry Andric     return TripleStr.takeError();
1838bcb0991SDimitry Andric 
18406c3fb27SDimitry Andric   Triple T(*TripleStr);
18506c3fb27SDimitry Andric   switch (T.getArch()) {
1868bcb0991SDimitry Andric   case Triple::x86:
1878bcb0991SDimitry Andric     return COFF::IMAGE_FILE_MACHINE_I386;
1888bcb0991SDimitry Andric   case Triple::x86_64:
1898bcb0991SDimitry Andric     return COFF::IMAGE_FILE_MACHINE_AMD64;
1908bcb0991SDimitry Andric   case Triple::arm:
1918bcb0991SDimitry Andric     return COFF::IMAGE_FILE_MACHINE_ARMNT;
1928bcb0991SDimitry Andric   case Triple::aarch64:
19306c3fb27SDimitry Andric     return T.isWindowsArm64EC() ? COFF::IMAGE_FILE_MACHINE_ARM64EC
19406c3fb27SDimitry Andric                                 : COFF::IMAGE_FILE_MACHINE_ARM64;
1958bcb0991SDimitry Andric   default:
196e8d8bef9SDimitry Andric     return createStringError(inconvertibleErrorCode(),
197e8d8bef9SDimitry Andric                              "unknown arch in target triple: " + *TripleStr);
1988bcb0991SDimitry Andric   }
1998bcb0991SDimitry Andric }
2008bcb0991SDimitry Andric 
20106c3fb27SDimitry Andric static bool machineMatches(COFF::MachineTypes LibMachine,
20206c3fb27SDimitry Andric                            COFF::MachineTypes FileMachine) {
20306c3fb27SDimitry Andric   if (LibMachine == FileMachine)
20406c3fb27SDimitry Andric     return true;
20506c3fb27SDimitry Andric   // ARM64EC mode allows both pure ARM64, ARM64EC and X64 objects to be mixed in
20606c3fb27SDimitry Andric   // the archive.
20706c3fb27SDimitry Andric   switch (LibMachine) {
20806c3fb27SDimitry Andric   case COFF::IMAGE_FILE_MACHINE_ARM64:
20906c3fb27SDimitry Andric     return FileMachine == COFF::IMAGE_FILE_MACHINE_ARM64X;
21006c3fb27SDimitry Andric   case COFF::IMAGE_FILE_MACHINE_ARM64EC:
21106c3fb27SDimitry Andric   case COFF::IMAGE_FILE_MACHINE_ARM64X:
21206c3fb27SDimitry Andric     return COFF::isAnyArm64(FileMachine) ||
21306c3fb27SDimitry Andric            FileMachine == COFF::IMAGE_FILE_MACHINE_AMD64;
21406c3fb27SDimitry Andric   default:
21506c3fb27SDimitry Andric     return false;
21606c3fb27SDimitry Andric   }
21706c3fb27SDimitry Andric }
21806c3fb27SDimitry Andric 
2198bcb0991SDimitry Andric static void appendFile(std::vector<NewArchiveMember> &Members,
2208bcb0991SDimitry Andric                        COFF::MachineTypes &LibMachine,
2218bcb0991SDimitry Andric                        std::string &LibMachineSource, MemoryBufferRef MB) {
2228bcb0991SDimitry Andric   file_magic Magic = identify_magic(MB.getBuffer());
2238bcb0991SDimitry Andric 
2248bcb0991SDimitry Andric   if (Magic != file_magic::coff_object && Magic != file_magic::bitcode &&
225979e22ffSDimitry Andric       Magic != file_magic::archive && Magic != file_magic::windows_resource &&
226979e22ffSDimitry Andric       Magic != file_magic::coff_import_library) {
2278bcb0991SDimitry Andric     llvm::errs() << MB.getBufferIdentifier()
228979e22ffSDimitry Andric                  << ": not a COFF object, bitcode, archive, import library or "
229979e22ffSDimitry Andric                     "resource file\n";
2308bcb0991SDimitry Andric     exit(1);
2318bcb0991SDimitry Andric   }
2328bcb0991SDimitry Andric 
2338bcb0991SDimitry Andric   // If a user attempts to add an archive to another archive, llvm-lib doesn't
2348bcb0991SDimitry Andric   // handle the first archive file as a single file. Instead, it extracts all
235e8d8bef9SDimitry Andric   // members from the archive and add them to the second archive. This behavior
2368bcb0991SDimitry Andric   // is for compatibility with Microsoft's lib command.
2378bcb0991SDimitry Andric   if (Magic == file_magic::archive) {
2388bcb0991SDimitry Andric     Error Err = Error::success();
2398bcb0991SDimitry Andric     object::Archive Archive(MB, Err);
2408bcb0991SDimitry Andric     fatalOpenError(std::move(Err), MB.getBufferIdentifier());
2418bcb0991SDimitry Andric 
2428bcb0991SDimitry Andric     for (auto &C : Archive.children(Err)) {
2438bcb0991SDimitry Andric       Expected<MemoryBufferRef> ChildMB = C.getMemoryBufferRef();
2448bcb0991SDimitry Andric       if (!ChildMB) {
2458bcb0991SDimitry Andric         handleAllErrors(ChildMB.takeError(), [&](const ErrorInfoBase &EIB) {
2468bcb0991SDimitry Andric           llvm::errs() << MB.getBufferIdentifier() << ": " << EIB.message()
2478bcb0991SDimitry Andric                        << "\n";
2488bcb0991SDimitry Andric         });
2498bcb0991SDimitry Andric         exit(1);
2508bcb0991SDimitry Andric       }
2518bcb0991SDimitry Andric 
2528bcb0991SDimitry Andric       appendFile(Members, LibMachine, LibMachineSource, *ChildMB);
2538bcb0991SDimitry Andric     }
2548bcb0991SDimitry Andric 
2558bcb0991SDimitry Andric     fatalOpenError(std::move(Err), MB.getBufferIdentifier());
2568bcb0991SDimitry Andric     return;
2578bcb0991SDimitry Andric   }
2588bcb0991SDimitry Andric 
2598bcb0991SDimitry Andric   // Check that all input files have the same machine type.
2608bcb0991SDimitry Andric   // Mixing normal objects and LTO bitcode files is fine as long as they
2618bcb0991SDimitry Andric   // have the same machine type.
2628bcb0991SDimitry Andric   // Doing this here duplicates the header parsing work that writeArchive()
2638bcb0991SDimitry Andric   // below does, but it's not a lot of work and it's a bit awkward to do
2648bcb0991SDimitry Andric   // in writeArchive() which needs to support many tools, can't assume the
2658bcb0991SDimitry Andric   // input is COFF, and doesn't have a good way to report errors.
2668bcb0991SDimitry Andric   if (Magic == file_magic::coff_object || Magic == file_magic::bitcode) {
267e8d8bef9SDimitry Andric     Expected<COFF::MachineTypes> MaybeFileMachine =
268e8d8bef9SDimitry Andric         (Magic == file_magic::coff_object) ? getCOFFFileMachine(MB)
2698bcb0991SDimitry Andric                                            : getBitcodeFileMachine(MB);
270e8d8bef9SDimitry Andric     if (!MaybeFileMachine) {
27181ad6265SDimitry Andric       handleAllErrors(MaybeFileMachine.takeError(),
27281ad6265SDimitry Andric                       [&](const ErrorInfoBase &EIB) {
27381ad6265SDimitry Andric                         llvm::errs() << MB.getBufferIdentifier() << ": "
27481ad6265SDimitry Andric                                      << EIB.message() << "\n";
275e8d8bef9SDimitry Andric                       });
276e8d8bef9SDimitry Andric       exit(1);
277e8d8bef9SDimitry Andric     }
278e8d8bef9SDimitry Andric     COFF::MachineTypes FileMachine = *MaybeFileMachine;
2798bcb0991SDimitry Andric 
2808bcb0991SDimitry Andric     // FIXME: Once lld-link rejects multiple resource .obj files:
2818bcb0991SDimitry Andric     // Call convertResToCOFF() on .res files and add the resulting
2828bcb0991SDimitry Andric     // COFF file to the .lib output instead of adding the .res file, and remove
2838bcb0991SDimitry Andric     // this check. See PR42180.
2848bcb0991SDimitry Andric     if (FileMachine != COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
2858bcb0991SDimitry Andric       if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
28606c3fb27SDimitry Andric         if (FileMachine == COFF::IMAGE_FILE_MACHINE_ARM64EC) {
28706c3fb27SDimitry Andric             llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
28806c3fb27SDimitry Andric                          << machineToStr(FileMachine)
28906c3fb27SDimitry Andric                          << " conflicts with inferred library machine type,"
29006c3fb27SDimitry Andric                          << " use /machine:arm64ec or /machine:arm64x\n";
29106c3fb27SDimitry Andric             exit(1);
29206c3fb27SDimitry Andric         }
2938bcb0991SDimitry Andric         LibMachine = FileMachine;
2948bcb0991SDimitry Andric         LibMachineSource =
2958bcb0991SDimitry Andric             (" (inferred from earlier file '" + MB.getBufferIdentifier() + "')")
2968bcb0991SDimitry Andric                 .str();
29706c3fb27SDimitry Andric       } else if (!machineMatches(LibMachine, FileMachine)) {
2988bcb0991SDimitry Andric         llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
2998bcb0991SDimitry Andric                      << machineToStr(FileMachine)
3008bcb0991SDimitry Andric                      << " conflicts with library machine type "
3018bcb0991SDimitry Andric                      << machineToStr(LibMachine) << LibMachineSource << '\n';
3028bcb0991SDimitry Andric         exit(1);
3038bcb0991SDimitry Andric       }
3048bcb0991SDimitry Andric     }
3058bcb0991SDimitry Andric   }
3068bcb0991SDimitry Andric 
3078bcb0991SDimitry Andric   Members.emplace_back(MB);
3088bcb0991SDimitry Andric }
3098bcb0991SDimitry Andric 
3100b57cec5SDimitry Andric int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {
3110b57cec5SDimitry Andric   BumpPtrAllocator Alloc;
3120b57cec5SDimitry Andric   StringSaver Saver(Alloc);
3130b57cec5SDimitry Andric 
3140b57cec5SDimitry Andric   // Parse command line arguments.
3150b57cec5SDimitry Andric   SmallVector<const char *, 20> NewArgs(ArgsArr.begin(), ArgsArr.end());
3160b57cec5SDimitry Andric   cl::ExpandResponseFiles(Saver, cl::TokenizeWindowsCommandLine, NewArgs);
3170b57cec5SDimitry Andric   ArgsArr = NewArgs;
3180b57cec5SDimitry Andric 
3190b57cec5SDimitry Andric   LibOptTable Table;
3200b57cec5SDimitry Andric   unsigned MissingIndex;
3210b57cec5SDimitry Andric   unsigned MissingCount;
3220b57cec5SDimitry Andric   opt::InputArgList Args =
3230b57cec5SDimitry Andric       Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount);
3240b57cec5SDimitry Andric   if (MissingCount) {
3250b57cec5SDimitry Andric     llvm::errs() << "missing arg value for \""
3260b57cec5SDimitry Andric                  << Args.getArgString(MissingIndex) << "\", expected "
3270b57cec5SDimitry Andric                  << MissingCount
3280b57cec5SDimitry Andric                  << (MissingCount == 1 ? " argument.\n" : " arguments.\n");
3290b57cec5SDimitry Andric     return 1;
3300b57cec5SDimitry Andric   }
3310b57cec5SDimitry Andric   for (auto *Arg : Args.filtered(OPT_UNKNOWN))
3320b57cec5SDimitry Andric     llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args)
3330b57cec5SDimitry Andric                  << "\n";
3340b57cec5SDimitry Andric 
3350b57cec5SDimitry Andric   // Handle /help
3360b57cec5SDimitry Andric   if (Args.hasArg(OPT_help)) {
337fe6060f1SDimitry Andric     Table.printHelp(outs(), "llvm-lib [options] file...", "LLVM Lib");
3380b57cec5SDimitry Andric     return 0;
3390b57cec5SDimitry Andric   }
3400b57cec5SDimitry Andric 
34181ad6265SDimitry Andric   // Parse /ignore:
34281ad6265SDimitry Andric   llvm::StringSet<> IgnoredWarnings;
34381ad6265SDimitry Andric   for (auto *Arg : Args.filtered(OPT_ignore))
34481ad6265SDimitry Andric     IgnoredWarnings.insert(Arg->getValue());
34581ad6265SDimitry Andric 
34606c3fb27SDimitry Andric   // get output library path, if any
34706c3fb27SDimitry Andric   std::string OutputPath;
34806c3fb27SDimitry Andric   if (auto *Arg = Args.getLastArg(OPT_out)) {
34906c3fb27SDimitry Andric     OutputPath = Arg->getValue();
35006c3fb27SDimitry Andric   }
35106c3fb27SDimitry Andric 
35206c3fb27SDimitry Andric   COFF::MachineTypes LibMachine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;
35306c3fb27SDimitry Andric   std::string LibMachineSource;
35406c3fb27SDimitry Andric   if (auto *Arg = Args.getLastArg(OPT_machine)) {
35506c3fb27SDimitry Andric     LibMachine = getMachineType(Arg->getValue());
35606c3fb27SDimitry Andric     if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
35706c3fb27SDimitry Andric       llvm::errs() << "unknown /machine: arg " << Arg->getValue() << '\n';
35806c3fb27SDimitry Andric       return 1;
35906c3fb27SDimitry Andric     }
36006c3fb27SDimitry Andric     LibMachineSource =
36106c3fb27SDimitry Andric         std::string(" (from '/machine:") + Arg->getValue() + "' flag)";
36206c3fb27SDimitry Andric   }
36306c3fb27SDimitry Andric 
36406c3fb27SDimitry Andric   // create an import library
36506c3fb27SDimitry Andric   if (Args.hasArg(OPT_deffile)) {
36606c3fb27SDimitry Andric 
36706c3fb27SDimitry Andric     if (OutputPath.empty()) {
36806c3fb27SDimitry Andric       llvm::errs() << "no output path given\n";
36906c3fb27SDimitry Andric       return 1;
37006c3fb27SDimitry Andric     }
37106c3fb27SDimitry Andric 
37206c3fb27SDimitry Andric     if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
37306c3fb27SDimitry Andric       llvm::errs() << "/def option requires /machine to be specified" << '\n';
37406c3fb27SDimitry Andric       return 1;
37506c3fb27SDimitry Andric     }
37606c3fb27SDimitry Andric 
37706c3fb27SDimitry Andric     std::unique_ptr<MemoryBuffer> MB =
37806c3fb27SDimitry Andric         openFile(Args.getLastArg(OPT_deffile)->getValue());
37906c3fb27SDimitry Andric     if (!MB)
38006c3fb27SDimitry Andric       return 1;
38106c3fb27SDimitry Andric 
38206c3fb27SDimitry Andric     if (!MB->getBufferSize()) {
38306c3fb27SDimitry Andric       llvm::errs() << "definition file empty\n";
38406c3fb27SDimitry Andric       return 1;
38506c3fb27SDimitry Andric     }
38606c3fb27SDimitry Andric 
38706c3fb27SDimitry Andric     Expected<COFFModuleDefinition> Def =
38806c3fb27SDimitry Andric         parseCOFFModuleDefinition(*MB, LibMachine, /*MingwDef=*/false);
38906c3fb27SDimitry Andric 
39006c3fb27SDimitry Andric     if (!Def) {
39106c3fb27SDimitry Andric       llvm::errs() << "error parsing definition\n"
39206c3fb27SDimitry Andric                    << errorToErrorCode(Def.takeError()).message();
39306c3fb27SDimitry Andric       return 1;
39406c3fb27SDimitry Andric     }
39506c3fb27SDimitry Andric 
396*0fca6ea1SDimitry Andric     std::vector<COFFShortExport> NativeExports;
397*0fca6ea1SDimitry Andric     std::string OutputFile = Def->OutputFile;
398*0fca6ea1SDimitry Andric 
399*0fca6ea1SDimitry Andric     if (isArm64EC(LibMachine) && Args.hasArg(OPT_nativedeffile)) {
400*0fca6ea1SDimitry Andric       std::unique_ptr<MemoryBuffer> NativeMB =
401*0fca6ea1SDimitry Andric           openFile(Args.getLastArg(OPT_nativedeffile)->getValue());
402*0fca6ea1SDimitry Andric       if (!NativeMB)
403*0fca6ea1SDimitry Andric         return 1;
404*0fca6ea1SDimitry Andric 
405*0fca6ea1SDimitry Andric       if (!NativeMB->getBufferSize()) {
406*0fca6ea1SDimitry Andric         llvm::errs() << "native definition file empty\n";
407*0fca6ea1SDimitry Andric         return 1;
408*0fca6ea1SDimitry Andric       }
409*0fca6ea1SDimitry Andric 
410*0fca6ea1SDimitry Andric       Expected<COFFModuleDefinition> NativeDef =
411*0fca6ea1SDimitry Andric           parseCOFFModuleDefinition(*NativeMB, COFF::IMAGE_FILE_MACHINE_ARM64);
412*0fca6ea1SDimitry Andric 
413*0fca6ea1SDimitry Andric       if (!NativeDef) {
414*0fca6ea1SDimitry Andric         llvm::errs() << "error parsing native definition\n"
415*0fca6ea1SDimitry Andric                      << errorToErrorCode(NativeDef.takeError()).message();
416*0fca6ea1SDimitry Andric         return 1;
417*0fca6ea1SDimitry Andric       }
418*0fca6ea1SDimitry Andric       NativeExports = std::move(NativeDef->Exports);
419*0fca6ea1SDimitry Andric       OutputFile = std::move(NativeDef->OutputFile);
420*0fca6ea1SDimitry Andric     }
421*0fca6ea1SDimitry Andric 
422*0fca6ea1SDimitry Andric     return writeImportLibrary(OutputFile, OutputPath, Def->Exports, LibMachine,
423*0fca6ea1SDimitry Andric                               /*MinGW=*/false, NativeExports)
42406c3fb27SDimitry Andric                ? 1
42506c3fb27SDimitry Andric                : 0;
42606c3fb27SDimitry Andric   }
42706c3fb27SDimitry Andric 
4285ffd83dbSDimitry Andric   // If no input files and not told otherwise, silently do nothing to match
4295ffd83dbSDimitry Andric   // lib.exe
43081ad6265SDimitry Andric   if (!Args.hasArgNoClaim(OPT_INPUT) && !Args.hasArg(OPT_llvmlibempty)) {
43181ad6265SDimitry Andric     if (!IgnoredWarnings.contains("emptyoutput")) {
43281ad6265SDimitry Andric       llvm::errs() << "warning: no input files, not writing output file\n";
43381ad6265SDimitry Andric       llvm::errs() << "         pass /llvmlibempty to write empty .lib file,\n";
43481ad6265SDimitry Andric       llvm::errs() << "         pass /ignore:emptyoutput to suppress warning\n";
43581ad6265SDimitry Andric       if (Args.hasFlag(OPT_WX, OPT_WX_no, false)) {
43681ad6265SDimitry Andric         llvm::errs() << "treating warning as error due to /WX\n";
43781ad6265SDimitry Andric         return 1;
43881ad6265SDimitry Andric       }
43981ad6265SDimitry Andric     }
4400b57cec5SDimitry Andric     return 0;
44181ad6265SDimitry Andric   }
4420b57cec5SDimitry Andric 
4430b57cec5SDimitry Andric   if (Args.hasArg(OPT_lst)) {
4440b57cec5SDimitry Andric     doList(Args);
4450b57cec5SDimitry Andric     return 0;
4460b57cec5SDimitry Andric   }
4470b57cec5SDimitry Andric 
4480b57cec5SDimitry Andric   std::vector<StringRef> SearchPaths = getSearchPaths(&Args, Saver);
4490b57cec5SDimitry Andric 
4508bcb0991SDimitry Andric   std::vector<std::unique_ptr<MemoryBuffer>> MBs;
4518bcb0991SDimitry Andric   StringSet<> Seen;
4520b57cec5SDimitry Andric   std::vector<NewArchiveMember> Members;
4538bcb0991SDimitry Andric 
4548bcb0991SDimitry Andric   // Create a NewArchiveMember for each input file.
4550b57cec5SDimitry Andric   for (auto *Arg : Args.filtered(OPT_INPUT)) {
4568bcb0991SDimitry Andric     // Find a file
4570b57cec5SDimitry Andric     std::string Path = findInputFile(Arg->getValue(), SearchPaths);
4580b57cec5SDimitry Andric     if (Path.empty()) {
4590b57cec5SDimitry Andric       llvm::errs() << Arg->getValue() << ": no such file or directory\n";
4600b57cec5SDimitry Andric       return 1;
4610b57cec5SDimitry Andric     }
4620b57cec5SDimitry Andric 
4638bcb0991SDimitry Andric     // Input files are uniquified by pathname. If you specify the exact same
4648bcb0991SDimitry Andric     // path more than once, all but the first one are ignored.
4658bcb0991SDimitry Andric     //
4668bcb0991SDimitry Andric     // Note that there's a loophole in the rule; you can prepend `.\` or
4678bcb0991SDimitry Andric     // something like that to a path to make it look different, and they are
4688bcb0991SDimitry Andric     // handled as if they were different files. This behavior is compatible with
4698bcb0991SDimitry Andric     // Microsoft lib.exe.
4708bcb0991SDimitry Andric     if (!Seen.insert(Path).second)
4718bcb0991SDimitry Andric       continue;
4720b57cec5SDimitry Andric 
4738bcb0991SDimitry Andric     // Open a file.
474fe6060f1SDimitry Andric     ErrorOr<std::unique_ptr<MemoryBuffer>> MOrErr = MemoryBuffer::getFile(
475fe6060f1SDimitry Andric         Path, /*IsText=*/false, /*RequiresNullTerminator=*/false);
4768bcb0991SDimitry Andric     fatalOpenError(errorCodeToError(MOrErr.getError()), Path);
4778bcb0991SDimitry Andric     MemoryBufferRef MBRef = (*MOrErr)->getMemBufferRef();
4780b57cec5SDimitry Andric 
4798bcb0991SDimitry Andric     // Append a file.
4808bcb0991SDimitry Andric     appendFile(Members, LibMachine, LibMachineSource, MBRef);
4810b57cec5SDimitry Andric 
4828bcb0991SDimitry Andric     // Take the ownership of the file buffer to keep the file open.
4838bcb0991SDimitry Andric     MBs.push_back(std::move(*MOrErr));
4840b57cec5SDimitry Andric   }
4850b57cec5SDimitry Andric 
4860b57cec5SDimitry Andric   // Create an archive file.
48706c3fb27SDimitry Andric   if (OutputPath.empty()) {
48806c3fb27SDimitry Andric     if (!Members.empty()) {
4895ffd83dbSDimitry Andric       OutputPath = getDefaultOutputPath(Members[0]);
4905ffd83dbSDimitry Andric     } else {
4915ffd83dbSDimitry Andric       llvm::errs() << "no output path given, and cannot infer with no inputs\n";
4925ffd83dbSDimitry Andric       return 1;
4935ffd83dbSDimitry Andric     }
49406c3fb27SDimitry Andric   }
4950b57cec5SDimitry Andric   // llvm-lib uses relative paths for both regular and thin archives, unlike
4960b57cec5SDimitry Andric   // standard GNU ar, which only uses relative paths for thin archives and
4970b57cec5SDimitry Andric   // basenames for regular archives.
4980b57cec5SDimitry Andric   for (NewArchiveMember &Member : Members) {
4990b57cec5SDimitry Andric     if (sys::path::is_relative(Member.MemberName)) {
5000b57cec5SDimitry Andric       Expected<std::string> PathOrErr =
5010b57cec5SDimitry Andric           computeArchiveRelativePath(OutputPath, Member.MemberName);
5020b57cec5SDimitry Andric       if (PathOrErr)
5030b57cec5SDimitry Andric         Member.MemberName = Saver.save(*PathOrErr);
5040b57cec5SDimitry Andric     }
5050b57cec5SDimitry Andric   }
5060b57cec5SDimitry Andric 
50706c3fb27SDimitry Andric   // For compatibility with MSVC, reverse member vector after de-duplication.
50806c3fb27SDimitry Andric   std::reverse(Members.begin(), Members.end());
50906c3fb27SDimitry Andric 
51006c3fb27SDimitry Andric   bool Thin = Args.hasArg(OPT_llvmlibthin);
5115f757f3fSDimitry Andric   if (Error E = writeArchive(
5125f757f3fSDimitry Andric           OutputPath, Members, SymtabWritingMode::NormalSymtab,
51306c3fb27SDimitry Andric           Thin ? object::Archive::K_GNU : object::Archive::K_COFF,
5145f757f3fSDimitry Andric           /*Deterministic=*/true, Thin, nullptr, COFF::isArm64EC(LibMachine))) {
5150b57cec5SDimitry Andric     handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
5160b57cec5SDimitry Andric       llvm::errs() << OutputPath << ": " << EI.message() << "\n";
5170b57cec5SDimitry Andric     });
5180b57cec5SDimitry Andric     return 1;
5190b57cec5SDimitry Andric   }
5200b57cec5SDimitry Andric 
5210b57cec5SDimitry Andric   return 0;
5220b57cec5SDimitry Andric }
523