xref: /freebsd/contrib/llvm-project/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===- LibDriver.cpp - lib.exe-compatible driver --------------------------===//
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 // Defines an interface to a lib.exe-compatible driver that also understands
10 // bitcode files. Used by llvm-lib and lld-link /lib.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/StringSet.h"
17 #include "llvm/BinaryFormat/COFF.h"
18 #include "llvm/BinaryFormat/Magic.h"
19 #include "llvm/Bitcode/BitcodeReader.h"
20 #include "llvm/Object/ArchiveWriter.h"
21 #include "llvm/Object/COFF.h"
22 #include "llvm/Object/COFFModuleDefinition.h"
23 #include "llvm/Object/WindowsMachineFlag.h"
24 #include "llvm/Option/Arg.h"
25 #include "llvm/Option/ArgList.h"
26 #include "llvm/Option/OptTable.h"
27 #include "llvm/Option/Option.h"
28 #include "llvm/Support/CommandLine.h"
29 #include "llvm/Support/Path.h"
30 #include "llvm/Support/Process.h"
31 #include "llvm/Support/StringSaver.h"
32 #include "llvm/Support/raw_ostream.h"
33 #include <optional>
34 
35 using namespace llvm;
36 using namespace llvm::object;
37 
38 namespace {
39 
40 enum {
41   OPT_INVALID = 0,
42 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
43 #include "Options.inc"
44 #undef OPTION
45 };
46 
47 #define PREFIX(NAME, VALUE)                                                    \
48   static constexpr StringLiteral NAME##_init[] = VALUE;                        \
49   static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
50                                                 std::size(NAME##_init) - 1);
51 #include "Options.inc"
52 #undef PREFIX
53 
54 using namespace llvm::opt;
55 static constexpr opt::OptTable::Info InfoTable[] = {
56 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
57 #include "Options.inc"
58 #undef OPTION
59 };
60 
61 class LibOptTable : public opt::GenericOptTable {
62 public:
LibOptTable()63   LibOptTable() : opt::GenericOptTable(InfoTable, true) {}
64 };
65 } // namespace
66 
getDefaultOutputPath(const NewArchiveMember & FirstMember)67 static std::string getDefaultOutputPath(const NewArchiveMember &FirstMember) {
68   SmallString<128> Val = StringRef(FirstMember.Buf->getBufferIdentifier());
69   sys::path::replace_extension(Val, ".lib");
70   return std::string(Val);
71 }
72 
getSearchPaths(opt::InputArgList * Args,StringSaver & Saver)73 static std::vector<StringRef> getSearchPaths(opt::InputArgList *Args,
74                                              StringSaver &Saver) {
75   std::vector<StringRef> Ret;
76   // Add current directory as first item of the search path.
77   Ret.push_back("");
78 
79   // Add /libpath flags.
80   for (auto *Arg : Args->filtered(OPT_libpath))
81     Ret.push_back(Arg->getValue());
82 
83   // Add $LIB.
84   std::optional<std::string> EnvOpt = sys::Process::GetEnv("LIB");
85   if (!EnvOpt)
86     return Ret;
87   StringRef Env = Saver.save(*EnvOpt);
88   while (!Env.empty()) {
89     StringRef Path;
90     std::tie(Path, Env) = Env.split(';');
91     Ret.push_back(Path);
92   }
93   return Ret;
94 }
95 
96 // Opens a file. Path has to be resolved already. (used for def file)
openFile(const Twine & Path)97 std::unique_ptr<MemoryBuffer> openFile(const Twine &Path) {
98   ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =
99       MemoryBuffer::getFile(Path, /*IsText=*/true);
100 
101   if (std::error_code EC = MB.getError()) {
102     llvm::errs() << "cannot open file " << Path << ": " << EC.message() << "\n";
103     return nullptr;
104   }
105 
106   return std::move(*MB);
107 }
108 
findInputFile(StringRef File,ArrayRef<StringRef> Paths)109 static std::string findInputFile(StringRef File, ArrayRef<StringRef> Paths) {
110   for (StringRef Dir : Paths) {
111     SmallString<128> Path = Dir;
112     sys::path::append(Path, File);
113     if (sys::fs::exists(Path))
114       return std::string(Path);
115   }
116   return "";
117 }
118 
fatalOpenError(llvm::Error E,Twine File)119 static void fatalOpenError(llvm::Error E, Twine File) {
120   if (!E)
121     return;
122   handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
123     llvm::errs() << "error opening '" << File << "': " << EIB.message() << '\n';
124     exit(1);
125   });
126 }
127 
doList(opt::InputArgList & Args)128 static void doList(opt::InputArgList &Args) {
129   // lib.exe prints the contents of the first archive file.
130   std::unique_ptr<MemoryBuffer> B;
131   for (auto *Arg : Args.filtered(OPT_INPUT)) {
132     // Create or open the archive object.
133     ErrorOr<std::unique_ptr<MemoryBuffer>> MaybeBuf = MemoryBuffer::getFile(
134         Arg->getValue(), /*IsText=*/false, /*RequiresNullTerminator=*/false);
135     fatalOpenError(errorCodeToError(MaybeBuf.getError()), Arg->getValue());
136 
137     if (identify_magic(MaybeBuf.get()->getBuffer()) == file_magic::archive) {
138       B = std::move(MaybeBuf.get());
139       break;
140     }
141   }
142 
143   // lib.exe doesn't print an error if no .lib files are passed.
144   if (!B)
145     return;
146 
147   Error Err = Error::success();
148   object::Archive Archive(B->getMemBufferRef(), Err);
149   fatalOpenError(std::move(Err), B->getBufferIdentifier());
150 
151   std::vector<StringRef> Names;
152   for (auto &C : Archive.children(Err)) {
153     Expected<StringRef> NameOrErr = C.getName();
154     fatalOpenError(NameOrErr.takeError(), B->getBufferIdentifier());
155     Names.push_back(NameOrErr.get());
156   }
157   for (auto Name : reverse(Names))
158     llvm::outs() << Name << '\n';
159   fatalOpenError(std::move(Err), B->getBufferIdentifier());
160 }
161 
getCOFFFileMachine(MemoryBufferRef MB)162 static Expected<COFF::MachineTypes> getCOFFFileMachine(MemoryBufferRef MB) {
163   std::error_code EC;
164   auto Obj = object::COFFObjectFile::create(MB);
165   if (!Obj)
166     return Obj.takeError();
167 
168   uint16_t Machine = (*Obj)->getMachine();
169   if (Machine != COFF::IMAGE_FILE_MACHINE_I386 &&
170       Machine != COFF::IMAGE_FILE_MACHINE_AMD64 &&
171       Machine != COFF::IMAGE_FILE_MACHINE_ARMNT && !COFF::isAnyArm64(Machine)) {
172     return createStringError(inconvertibleErrorCode(),
173                              "unknown machine: " + std::to_string(Machine));
174   }
175 
176   return static_cast<COFF::MachineTypes>(Machine);
177 }
178 
getBitcodeFileMachine(MemoryBufferRef MB)179 static Expected<COFF::MachineTypes> getBitcodeFileMachine(MemoryBufferRef MB) {
180   Expected<std::string> TripleStr = getBitcodeTargetTriple(MB);
181   if (!TripleStr)
182     return TripleStr.takeError();
183 
184   Triple T(*TripleStr);
185   switch (T.getArch()) {
186   case Triple::x86:
187     return COFF::IMAGE_FILE_MACHINE_I386;
188   case Triple::x86_64:
189     return COFF::IMAGE_FILE_MACHINE_AMD64;
190   case Triple::arm:
191     return COFF::IMAGE_FILE_MACHINE_ARMNT;
192   case Triple::aarch64:
193     return T.isWindowsArm64EC() ? COFF::IMAGE_FILE_MACHINE_ARM64EC
194                                 : COFF::IMAGE_FILE_MACHINE_ARM64;
195   default:
196     return createStringError(inconvertibleErrorCode(),
197                              "unknown arch in target triple: " + *TripleStr);
198   }
199 }
200 
machineMatches(COFF::MachineTypes LibMachine,COFF::MachineTypes FileMachine)201 static bool machineMatches(COFF::MachineTypes LibMachine,
202                            COFF::MachineTypes FileMachine) {
203   if (LibMachine == FileMachine)
204     return true;
205   // ARM64EC mode allows both pure ARM64, ARM64EC and X64 objects to be mixed in
206   // the archive.
207   switch (LibMachine) {
208   case COFF::IMAGE_FILE_MACHINE_ARM64:
209     return FileMachine == COFF::IMAGE_FILE_MACHINE_ARM64X;
210   case COFF::IMAGE_FILE_MACHINE_ARM64EC:
211   case COFF::IMAGE_FILE_MACHINE_ARM64X:
212     return COFF::isAnyArm64(FileMachine) ||
213            FileMachine == COFF::IMAGE_FILE_MACHINE_AMD64;
214   default:
215     return false;
216   }
217 }
218 
appendFile(std::vector<NewArchiveMember> & Members,COFF::MachineTypes & LibMachine,std::string & LibMachineSource,MemoryBufferRef MB)219 static void appendFile(std::vector<NewArchiveMember> &Members,
220                        COFF::MachineTypes &LibMachine,
221                        std::string &LibMachineSource, MemoryBufferRef MB) {
222   file_magic Magic = identify_magic(MB.getBuffer());
223 
224   if (Magic != file_magic::coff_object && Magic != file_magic::bitcode &&
225       Magic != file_magic::archive && Magic != file_magic::windows_resource &&
226       Magic != file_magic::coff_import_library) {
227     llvm::errs() << MB.getBufferIdentifier()
228                  << ": not a COFF object, bitcode, archive, import library or "
229                     "resource file\n";
230     exit(1);
231   }
232 
233   // If a user attempts to add an archive to another archive, llvm-lib doesn't
234   // handle the first archive file as a single file. Instead, it extracts all
235   // members from the archive and add them to the second archive. This behavior
236   // is for compatibility with Microsoft's lib command.
237   if (Magic == file_magic::archive) {
238     Error Err = Error::success();
239     object::Archive Archive(MB, Err);
240     fatalOpenError(std::move(Err), MB.getBufferIdentifier());
241 
242     for (auto &C : Archive.children(Err)) {
243       Expected<MemoryBufferRef> ChildMB = C.getMemoryBufferRef();
244       if (!ChildMB) {
245         handleAllErrors(ChildMB.takeError(), [&](const ErrorInfoBase &EIB) {
246           llvm::errs() << MB.getBufferIdentifier() << ": " << EIB.message()
247                        << "\n";
248         });
249         exit(1);
250       }
251 
252       appendFile(Members, LibMachine, LibMachineSource, *ChildMB);
253     }
254 
255     fatalOpenError(std::move(Err), MB.getBufferIdentifier());
256     return;
257   }
258 
259   // Check that all input files have the same machine type.
260   // Mixing normal objects and LTO bitcode files is fine as long as they
261   // have the same machine type.
262   // Doing this here duplicates the header parsing work that writeArchive()
263   // below does, but it's not a lot of work and it's a bit awkward to do
264   // in writeArchive() which needs to support many tools, can't assume the
265   // input is COFF, and doesn't have a good way to report errors.
266   if (Magic == file_magic::coff_object || Magic == file_magic::bitcode) {
267     Expected<COFF::MachineTypes> MaybeFileMachine =
268         (Magic == file_magic::coff_object) ? getCOFFFileMachine(MB)
269                                            : getBitcodeFileMachine(MB);
270     if (!MaybeFileMachine) {
271       handleAllErrors(MaybeFileMachine.takeError(),
272                       [&](const ErrorInfoBase &EIB) {
273                         llvm::errs() << MB.getBufferIdentifier() << ": "
274                                      << EIB.message() << "\n";
275                       });
276       exit(1);
277     }
278     COFF::MachineTypes FileMachine = *MaybeFileMachine;
279 
280     // FIXME: Once lld-link rejects multiple resource .obj files:
281     // Call convertResToCOFF() on .res files and add the resulting
282     // COFF file to the .lib output instead of adding the .res file, and remove
283     // this check. See PR42180.
284     if (FileMachine != COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
285       if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
286         if (FileMachine == COFF::IMAGE_FILE_MACHINE_ARM64EC) {
287             llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
288                          << machineToStr(FileMachine)
289                          << " conflicts with inferred library machine type,"
290                          << " use /machine:arm64ec or /machine:arm64x\n";
291             exit(1);
292         }
293         LibMachine = FileMachine;
294         LibMachineSource =
295             (" (inferred from earlier file '" + MB.getBufferIdentifier() + "')")
296                 .str();
297       } else if (!machineMatches(LibMachine, FileMachine)) {
298         llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
299                      << machineToStr(FileMachine)
300                      << " conflicts with library machine type "
301                      << machineToStr(LibMachine) << LibMachineSource << '\n';
302         exit(1);
303       }
304     }
305   }
306 
307   Members.emplace_back(MB);
308 }
309 
libDriverMain(ArrayRef<const char * > ArgsArr)310 int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {
311   BumpPtrAllocator Alloc;
312   StringSaver Saver(Alloc);
313 
314   // Parse command line arguments.
315   SmallVector<const char *, 20> NewArgs(ArgsArr.begin(), ArgsArr.end());
316   cl::ExpandResponseFiles(Saver, cl::TokenizeWindowsCommandLine, NewArgs);
317   ArgsArr = NewArgs;
318 
319   LibOptTable Table;
320   unsigned MissingIndex;
321   unsigned MissingCount;
322   opt::InputArgList Args =
323       Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount);
324   if (MissingCount) {
325     llvm::errs() << "missing arg value for \""
326                  << Args.getArgString(MissingIndex) << "\", expected "
327                  << MissingCount
328                  << (MissingCount == 1 ? " argument.\n" : " arguments.\n");
329     return 1;
330   }
331   for (auto *Arg : Args.filtered(OPT_UNKNOWN))
332     llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args)
333                  << "\n";
334 
335   // Handle /help
336   if (Args.hasArg(OPT_help)) {
337     Table.printHelp(outs(), "llvm-lib [options] file...", "LLVM Lib");
338     return 0;
339   }
340 
341   // Parse /ignore:
342   llvm::StringSet<> IgnoredWarnings;
343   for (auto *Arg : Args.filtered(OPT_ignore))
344     IgnoredWarnings.insert(Arg->getValue());
345 
346   // get output library path, if any
347   std::string OutputPath;
348   if (auto *Arg = Args.getLastArg(OPT_out)) {
349     OutputPath = Arg->getValue();
350   }
351 
352   COFF::MachineTypes LibMachine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;
353   std::string LibMachineSource;
354   if (auto *Arg = Args.getLastArg(OPT_machine)) {
355     LibMachine = getMachineType(Arg->getValue());
356     if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
357       llvm::errs() << "unknown /machine: arg " << Arg->getValue() << '\n';
358       return 1;
359     }
360     LibMachineSource =
361         std::string(" (from '/machine:") + Arg->getValue() + "' flag)";
362   }
363 
364   // create an import library
365   if (Args.hasArg(OPT_deffile)) {
366 
367     if (OutputPath.empty()) {
368       llvm::errs() << "no output path given\n";
369       return 1;
370     }
371 
372     if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
373       llvm::errs() << "/def option requires /machine to be specified" << '\n';
374       return 1;
375     }
376 
377     std::unique_ptr<MemoryBuffer> MB =
378         openFile(Args.getLastArg(OPT_deffile)->getValue());
379     if (!MB)
380       return 1;
381 
382     if (!MB->getBufferSize()) {
383       llvm::errs() << "definition file empty\n";
384       return 1;
385     }
386 
387     Expected<COFFModuleDefinition> Def =
388         parseCOFFModuleDefinition(*MB, LibMachine, /*MingwDef=*/false);
389 
390     if (!Def) {
391       llvm::errs() << "error parsing definition\n"
392                    << errorToErrorCode(Def.takeError()).message();
393       return 1;
394     }
395 
396     std::vector<COFFShortExport> NativeExports;
397     std::string OutputFile = Def->OutputFile;
398 
399     if (isArm64EC(LibMachine) && Args.hasArg(OPT_nativedeffile)) {
400       std::unique_ptr<MemoryBuffer> NativeMB =
401           openFile(Args.getLastArg(OPT_nativedeffile)->getValue());
402       if (!NativeMB)
403         return 1;
404 
405       if (!NativeMB->getBufferSize()) {
406         llvm::errs() << "native definition file empty\n";
407         return 1;
408       }
409 
410       Expected<COFFModuleDefinition> NativeDef =
411           parseCOFFModuleDefinition(*NativeMB, COFF::IMAGE_FILE_MACHINE_ARM64);
412 
413       if (!NativeDef) {
414         llvm::errs() << "error parsing native definition\n"
415                      << errorToErrorCode(NativeDef.takeError()).message();
416         return 1;
417       }
418       NativeExports = std::move(NativeDef->Exports);
419       OutputFile = std::move(NativeDef->OutputFile);
420     }
421 
422     return writeImportLibrary(OutputFile, OutputPath, Def->Exports, LibMachine,
423                               /*MinGW=*/false, NativeExports)
424                ? 1
425                : 0;
426   }
427 
428   // If no input files and not told otherwise, silently do nothing to match
429   // lib.exe
430   if (!Args.hasArgNoClaim(OPT_INPUT) && !Args.hasArg(OPT_llvmlibempty)) {
431     if (!IgnoredWarnings.contains("emptyoutput")) {
432       llvm::errs() << "warning: no input files, not writing output file\n";
433       llvm::errs() << "         pass /llvmlibempty to write empty .lib file,\n";
434       llvm::errs() << "         pass /ignore:emptyoutput to suppress warning\n";
435       if (Args.hasFlag(OPT_WX, OPT_WX_no, false)) {
436         llvm::errs() << "treating warning as error due to /WX\n";
437         return 1;
438       }
439     }
440     return 0;
441   }
442 
443   if (Args.hasArg(OPT_lst)) {
444     doList(Args);
445     return 0;
446   }
447 
448   std::vector<StringRef> SearchPaths = getSearchPaths(&Args, Saver);
449 
450   std::vector<std::unique_ptr<MemoryBuffer>> MBs;
451   StringSet<> Seen;
452   std::vector<NewArchiveMember> Members;
453 
454   // Create a NewArchiveMember for each input file.
455   for (auto *Arg : Args.filtered(OPT_INPUT)) {
456     // Find a file
457     std::string Path = findInputFile(Arg->getValue(), SearchPaths);
458     if (Path.empty()) {
459       llvm::errs() << Arg->getValue() << ": no such file or directory\n";
460       return 1;
461     }
462 
463     // Input files are uniquified by pathname. If you specify the exact same
464     // path more than once, all but the first one are ignored.
465     //
466     // Note that there's a loophole in the rule; you can prepend `.\` or
467     // something like that to a path to make it look different, and they are
468     // handled as if they were different files. This behavior is compatible with
469     // Microsoft lib.exe.
470     if (!Seen.insert(Path).second)
471       continue;
472 
473     // Open a file.
474     ErrorOr<std::unique_ptr<MemoryBuffer>> MOrErr = MemoryBuffer::getFile(
475         Path, /*IsText=*/false, /*RequiresNullTerminator=*/false);
476     fatalOpenError(errorCodeToError(MOrErr.getError()), Path);
477     MemoryBufferRef MBRef = (*MOrErr)->getMemBufferRef();
478 
479     // Append a file.
480     appendFile(Members, LibMachine, LibMachineSource, MBRef);
481 
482     // Take the ownership of the file buffer to keep the file open.
483     MBs.push_back(std::move(*MOrErr));
484   }
485 
486   // Create an archive file.
487   if (OutputPath.empty()) {
488     if (!Members.empty()) {
489       OutputPath = getDefaultOutputPath(Members[0]);
490     } else {
491       llvm::errs() << "no output path given, and cannot infer with no inputs\n";
492       return 1;
493     }
494   }
495   // llvm-lib uses relative paths for both regular and thin archives, unlike
496   // standard GNU ar, which only uses relative paths for thin archives and
497   // basenames for regular archives.
498   for (NewArchiveMember &Member : Members) {
499     if (sys::path::is_relative(Member.MemberName)) {
500       Expected<std::string> PathOrErr =
501           computeArchiveRelativePath(OutputPath, Member.MemberName);
502       if (PathOrErr)
503         Member.MemberName = Saver.save(*PathOrErr);
504     }
505   }
506 
507   // For compatibility with MSVC, reverse member vector after de-duplication.
508   std::reverse(Members.begin(), Members.end());
509 
510   bool Thin = Args.hasArg(OPT_llvmlibthin);
511   if (Error E = writeArchive(
512           OutputPath, Members, SymtabWritingMode::NormalSymtab,
513           Thin ? object::Archive::K_GNU : object::Archive::K_COFF,
514           /*Deterministic=*/true, Thin, nullptr, COFF::isArm64EC(LibMachine))) {
515     handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
516       llvm::errs() << OutputPath << ": " << EI.message() << "\n";
517     });
518     return 1;
519   }
520 
521   return 0;
522 }
523