xref: /freebsd/contrib/llvm-project/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp (revision 76cc4c20473495c839c4df9cfe0bf46632738f03)
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/WindowsMachineFlag.h"
23  #include "llvm/Option/Arg.h"
24  #include "llvm/Option/ArgList.h"
25  #include "llvm/Option/Option.h"
26  #include "llvm/Support/CommandLine.h"
27  #include "llvm/Support/Path.h"
28  #include "llvm/Support/Process.h"
29  #include "llvm/Support/StringSaver.h"
30  #include "llvm/Support/raw_ostream.h"
31  
32  using namespace llvm;
33  
34  namespace {
35  
36  enum {
37    OPT_INVALID = 0,
38  #define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
39  #include "Options.inc"
40  #undef OPTION
41  };
42  
43  #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
44  #include "Options.inc"
45  #undef PREFIX
46  
47  static const opt::OptTable::Info InfoTable[] = {
48  #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12)      \
49    {X1, X2, X10,         X11,         OPT_##ID, opt::Option::KIND##Class,       \
50     X9, X8, OPT_##GROUP, OPT_##ALIAS, X7,       X12},
51  #include "Options.inc"
52  #undef OPTION
53  };
54  
55  class LibOptTable : public opt::OptTable {
56  public:
57    LibOptTable() : OptTable(InfoTable, true) {}
58  };
59  
60  }
61  
62  static std::string getDefaultOutputPath(const NewArchiveMember &FirstMember) {
63    SmallString<128> Val = StringRef(FirstMember.Buf->getBufferIdentifier());
64    sys::path::replace_extension(Val, ".lib");
65    return std::string(Val.str());
66  }
67  
68  static std::vector<StringRef> getSearchPaths(opt::InputArgList *Args,
69                                               StringSaver &Saver) {
70    std::vector<StringRef> Ret;
71    // Add current directory as first item of the search path.
72    Ret.push_back("");
73  
74    // Add /libpath flags.
75    for (auto *Arg : Args->filtered(OPT_libpath))
76      Ret.push_back(Arg->getValue());
77  
78    // Add $LIB.
79    Optional<std::string> EnvOpt = sys::Process::GetEnv("LIB");
80    if (!EnvOpt.hasValue())
81      return Ret;
82    StringRef Env = Saver.save(*EnvOpt);
83    while (!Env.empty()) {
84      StringRef Path;
85      std::tie(Path, Env) = Env.split(';');
86      Ret.push_back(Path);
87    }
88    return Ret;
89  }
90  
91  static std::string findInputFile(StringRef File, ArrayRef<StringRef> Paths) {
92    for (StringRef Dir : Paths) {
93      SmallString<128> Path = Dir;
94      sys::path::append(Path, File);
95      if (sys::fs::exists(Path))
96        return std::string(Path);
97    }
98    return "";
99  }
100  
101  static void fatalOpenError(llvm::Error E, Twine File) {
102    if (!E)
103      return;
104    handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {
105      llvm::errs() << "error opening '" << File << "': " << EIB.message() << '\n';
106      exit(1);
107    });
108  }
109  
110  static void doList(opt::InputArgList& Args) {
111    // lib.exe prints the contents of the first archive file.
112    std::unique_ptr<MemoryBuffer> B;
113    for (auto *Arg : Args.filtered(OPT_INPUT)) {
114      // Create or open the archive object.
115      ErrorOr<std::unique_ptr<MemoryBuffer>> MaybeBuf =
116          MemoryBuffer::getFile(Arg->getValue(), -1, false);
117      fatalOpenError(errorCodeToError(MaybeBuf.getError()), Arg->getValue());
118  
119      if (identify_magic(MaybeBuf.get()->getBuffer()) == file_magic::archive) {
120        B = std::move(MaybeBuf.get());
121        break;
122      }
123    }
124  
125    // lib.exe doesn't print an error if no .lib files are passed.
126    if (!B)
127      return;
128  
129    Error Err = Error::success();
130    object::Archive Archive(B.get()->getMemBufferRef(), Err);
131    fatalOpenError(std::move(Err), B->getBufferIdentifier());
132  
133    for (auto &C : Archive.children(Err)) {
134      Expected<StringRef> NameOrErr = C.getName();
135      fatalOpenError(NameOrErr.takeError(), B->getBufferIdentifier());
136      StringRef Name = NameOrErr.get();
137      llvm::outs() << Name << '\n';
138    }
139    fatalOpenError(std::move(Err), B->getBufferIdentifier());
140  }
141  
142  static COFF::MachineTypes getCOFFFileMachine(MemoryBufferRef MB) {
143    std::error_code EC;
144    auto Obj = object::COFFObjectFile::create(MB);
145    if (!Obj) {
146      llvm::errs() << MB.getBufferIdentifier()
147                   << ": failed to open: " << Obj.takeError() << '\n';
148      exit(1);
149    }
150  
151    uint16_t Machine = (*Obj)->getMachine();
152    if (Machine != COFF::IMAGE_FILE_MACHINE_I386 &&
153        Machine != COFF::IMAGE_FILE_MACHINE_AMD64 &&
154        Machine != COFF::IMAGE_FILE_MACHINE_ARMNT &&
155        Machine != COFF::IMAGE_FILE_MACHINE_ARM64) {
156      llvm::errs() << MB.getBufferIdentifier() << ": unknown machine: " << Machine
157                   << '\n';
158      exit(1);
159    }
160  
161    return static_cast<COFF::MachineTypes>(Machine);
162  }
163  
164  static COFF::MachineTypes getBitcodeFileMachine(MemoryBufferRef MB) {
165    Expected<std::string> TripleStr = getBitcodeTargetTriple(MB);
166    if (!TripleStr) {
167      llvm::errs() << MB.getBufferIdentifier()
168                   << ": failed to get target triple from bitcode\n";
169      exit(1);
170    }
171  
172    switch (Triple(*TripleStr).getArch()) {
173    case Triple::x86:
174      return COFF::IMAGE_FILE_MACHINE_I386;
175    case Triple::x86_64:
176      return COFF::IMAGE_FILE_MACHINE_AMD64;
177    case Triple::arm:
178      return COFF::IMAGE_FILE_MACHINE_ARMNT;
179    case Triple::aarch64:
180      return COFF::IMAGE_FILE_MACHINE_ARM64;
181    default:
182      llvm::errs() << MB.getBufferIdentifier()
183                   << ": unknown arch in target triple " << *TripleStr << '\n';
184      exit(1);
185    }
186  }
187  
188  static void appendFile(std::vector<NewArchiveMember> &Members,
189                         COFF::MachineTypes &LibMachine,
190                         std::string &LibMachineSource, MemoryBufferRef MB) {
191    file_magic Magic = identify_magic(MB.getBuffer());
192  
193    if (Magic != file_magic::coff_object && Magic != file_magic::bitcode &&
194        Magic != file_magic::archive && Magic != file_magic::windows_resource &&
195        Magic != file_magic::coff_import_library) {
196      llvm::errs() << MB.getBufferIdentifier()
197                   << ": not a COFF object, bitcode, archive, import library or "
198                      "resource file\n";
199      exit(1);
200    }
201  
202    // If a user attempts to add an archive to another archive, llvm-lib doesn't
203    // handle the first archive file as a single file. Instead, it extracts all
204    // members from the archive and add them to the second archive. This beahvior
205    // is for compatibility with Microsoft's lib command.
206    if (Magic == file_magic::archive) {
207      Error Err = Error::success();
208      object::Archive Archive(MB, Err);
209      fatalOpenError(std::move(Err), MB.getBufferIdentifier());
210  
211      for (auto &C : Archive.children(Err)) {
212        Expected<MemoryBufferRef> ChildMB = C.getMemoryBufferRef();
213        if (!ChildMB) {
214          handleAllErrors(ChildMB.takeError(), [&](const ErrorInfoBase &EIB) {
215            llvm::errs() << MB.getBufferIdentifier() << ": " << EIB.message()
216                         << "\n";
217          });
218          exit(1);
219        }
220  
221        appendFile(Members, LibMachine, LibMachineSource, *ChildMB);
222      }
223  
224      fatalOpenError(std::move(Err), MB.getBufferIdentifier());
225      return;
226    }
227  
228    // Check that all input files have the same machine type.
229    // Mixing normal objects and LTO bitcode files is fine as long as they
230    // have the same machine type.
231    // Doing this here duplicates the header parsing work that writeArchive()
232    // below does, but it's not a lot of work and it's a bit awkward to do
233    // in writeArchive() which needs to support many tools, can't assume the
234    // input is COFF, and doesn't have a good way to report errors.
235    if (Magic == file_magic::coff_object || Magic == file_magic::bitcode) {
236      COFF::MachineTypes FileMachine = (Magic == file_magic::coff_object)
237                                           ? getCOFFFileMachine(MB)
238                                           : getBitcodeFileMachine(MB);
239  
240      // FIXME: Once lld-link rejects multiple resource .obj files:
241      // Call convertResToCOFF() on .res files and add the resulting
242      // COFF file to the .lib output instead of adding the .res file, and remove
243      // this check. See PR42180.
244      if (FileMachine != COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
245        if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
246          LibMachine = FileMachine;
247          LibMachineSource =
248              (" (inferred from earlier file '" + MB.getBufferIdentifier() + "')")
249                  .str();
250        } else if (LibMachine != FileMachine) {
251          llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
252                       << machineToStr(FileMachine)
253                       << " conflicts with library machine type "
254                       << machineToStr(LibMachine) << LibMachineSource << '\n';
255          exit(1);
256        }
257      }
258    }
259  
260    Members.emplace_back(MB);
261  }
262  
263  int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {
264    BumpPtrAllocator Alloc;
265    StringSaver Saver(Alloc);
266  
267    // Parse command line arguments.
268    SmallVector<const char *, 20> NewArgs(ArgsArr.begin(), ArgsArr.end());
269    cl::ExpandResponseFiles(Saver, cl::TokenizeWindowsCommandLine, NewArgs);
270    ArgsArr = NewArgs;
271  
272    LibOptTable Table;
273    unsigned MissingIndex;
274    unsigned MissingCount;
275    opt::InputArgList Args =
276        Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount);
277    if (MissingCount) {
278      llvm::errs() << "missing arg value for \""
279                   << Args.getArgString(MissingIndex) << "\", expected "
280                   << MissingCount
281                   << (MissingCount == 1 ? " argument.\n" : " arguments.\n");
282      return 1;
283    }
284    for (auto *Arg : Args.filtered(OPT_UNKNOWN))
285      llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args)
286                   << "\n";
287  
288    // Handle /help
289    if (Args.hasArg(OPT_help)) {
290      Table.PrintHelp(outs(), "llvm-lib [options] file...", "LLVM Lib");
291      return 0;
292    }
293  
294    // If no input files and not told otherwise, silently do nothing to match
295    // lib.exe
296    if (!Args.hasArgNoClaim(OPT_INPUT) && !Args.hasArg(OPT_llvmlibempty))
297      return 0;
298  
299    if (Args.hasArg(OPT_lst)) {
300      doList(Args);
301      return 0;
302    }
303  
304    std::vector<StringRef> SearchPaths = getSearchPaths(&Args, Saver);
305  
306    COFF::MachineTypes LibMachine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;
307    std::string LibMachineSource;
308    if (auto *Arg = Args.getLastArg(OPT_machine)) {
309      LibMachine = getMachineType(Arg->getValue());
310      if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
311        llvm::errs() << "unknown /machine: arg " << Arg->getValue() << '\n';
312        return 1;
313      }
314      LibMachineSource =
315          std::string(" (from '/machine:") + Arg->getValue() + "' flag)";
316    }
317  
318    std::vector<std::unique_ptr<MemoryBuffer>> MBs;
319    StringSet<> Seen;
320    std::vector<NewArchiveMember> Members;
321  
322    // Create a NewArchiveMember for each input file.
323    for (auto *Arg : Args.filtered(OPT_INPUT)) {
324      // Find a file
325      std::string Path = findInputFile(Arg->getValue(), SearchPaths);
326      if (Path.empty()) {
327        llvm::errs() << Arg->getValue() << ": no such file or directory\n";
328        return 1;
329      }
330  
331      // Input files are uniquified by pathname. If you specify the exact same
332      // path more than once, all but the first one are ignored.
333      //
334      // Note that there's a loophole in the rule; you can prepend `.\` or
335      // something like that to a path to make it look different, and they are
336      // handled as if they were different files. This behavior is compatible with
337      // Microsoft lib.exe.
338      if (!Seen.insert(Path).second)
339        continue;
340  
341      // Open a file.
342      ErrorOr<std::unique_ptr<MemoryBuffer>> MOrErr =
343          MemoryBuffer::getFile(Path, -1, false);
344      fatalOpenError(errorCodeToError(MOrErr.getError()), Path);
345      MemoryBufferRef MBRef = (*MOrErr)->getMemBufferRef();
346  
347      // Append a file.
348      appendFile(Members, LibMachine, LibMachineSource, MBRef);
349  
350      // Take the ownership of the file buffer to keep the file open.
351      MBs.push_back(std::move(*MOrErr));
352    }
353  
354    // Create an archive file.
355    std::string OutputPath;
356    if (auto *Arg = Args.getLastArg(OPT_out)) {
357      OutputPath = Arg->getValue();
358    } else if (!Members.empty()) {
359      OutputPath = getDefaultOutputPath(Members[0]);
360    } else {
361      llvm::errs() << "no output path given, and cannot infer with no inputs\n";
362      return 1;
363    }
364    // llvm-lib uses relative paths for both regular and thin archives, unlike
365    // standard GNU ar, which only uses relative paths for thin archives and
366    // basenames for regular archives.
367    for (NewArchiveMember &Member : Members) {
368      if (sys::path::is_relative(Member.MemberName)) {
369        Expected<std::string> PathOrErr =
370            computeArchiveRelativePath(OutputPath, Member.MemberName);
371        if (PathOrErr)
372          Member.MemberName = Saver.save(*PathOrErr);
373      }
374    }
375  
376    if (Error E =
377            writeArchive(OutputPath, Members,
378                         /*WriteSymtab=*/true, object::Archive::K_GNU,
379                         /*Deterministic*/ true, Args.hasArg(OPT_llvmlibthin))) {
380      handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
381        llvm::errs() << OutputPath << ": " << EI.message() << "\n";
382      });
383      return 1;
384    }
385  
386    return 0;
387  }
388