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