150
|
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
|
173
|
62 static std::string getDefaultOutputPath(const NewArchiveMember &FirstMember) {
|
150
|
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;
|
173
|
144 auto Obj = object::COFFObjectFile::create(MB);
|
|
145 if (!Obj) {
|
150
|
146 llvm::errs() << MB.getBufferIdentifier()
|
173
|
147 << ": failed to open: " << Obj.takeError() << '\n';
|
150
|
148 exit(1);
|
|
149 }
|
|
150
|
173
|
151 uint16_t Machine = (*Obj)->getMachine();
|
150
|
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 llvm::errs() << MB.getBufferIdentifier()
|
|
196 << ": not a COFF object, bitcode, archive or resource file\n";
|
|
197 exit(1);
|
|
198 }
|
|
199
|
|
200 // If a user attempts to add an archive to another archive, llvm-lib doesn't
|
|
201 // handle the first archive file as a single file. Instead, it extracts all
|
|
202 // members from the archive and add them to the second archive. This beahvior
|
|
203 // is for compatibility with Microsoft's lib command.
|
|
204 if (Magic == file_magic::archive) {
|
|
205 Error Err = Error::success();
|
|
206 object::Archive Archive(MB, Err);
|
|
207 fatalOpenError(std::move(Err), MB.getBufferIdentifier());
|
|
208
|
|
209 for (auto &C : Archive.children(Err)) {
|
|
210 Expected<MemoryBufferRef> ChildMB = C.getMemoryBufferRef();
|
|
211 if (!ChildMB) {
|
|
212 handleAllErrors(ChildMB.takeError(), [&](const ErrorInfoBase &EIB) {
|
|
213 llvm::errs() << MB.getBufferIdentifier() << ": " << EIB.message()
|
|
214 << "\n";
|
|
215 });
|
|
216 exit(1);
|
|
217 }
|
|
218
|
|
219 appendFile(Members, LibMachine, LibMachineSource, *ChildMB);
|
|
220 }
|
|
221
|
|
222 fatalOpenError(std::move(Err), MB.getBufferIdentifier());
|
|
223 return;
|
|
224 }
|
|
225
|
|
226 // Check that all input files have the same machine type.
|
|
227 // Mixing normal objects and LTO bitcode files is fine as long as they
|
|
228 // have the same machine type.
|
|
229 // Doing this here duplicates the header parsing work that writeArchive()
|
|
230 // below does, but it's not a lot of work and it's a bit awkward to do
|
|
231 // in writeArchive() which needs to support many tools, can't assume the
|
|
232 // input is COFF, and doesn't have a good way to report errors.
|
|
233 if (Magic == file_magic::coff_object || Magic == file_magic::bitcode) {
|
|
234 COFF::MachineTypes FileMachine = (Magic == file_magic::coff_object)
|
|
235 ? getCOFFFileMachine(MB)
|
|
236 : getBitcodeFileMachine(MB);
|
|
237
|
|
238 // FIXME: Once lld-link rejects multiple resource .obj files:
|
|
239 // Call convertResToCOFF() on .res files and add the resulting
|
|
240 // COFF file to the .lib output instead of adding the .res file, and remove
|
|
241 // this check. See PR42180.
|
|
242 if (FileMachine != COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
|
|
243 if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
|
|
244 LibMachine = FileMachine;
|
|
245 LibMachineSource =
|
|
246 (" (inferred from earlier file '" + MB.getBufferIdentifier() + "')")
|
|
247 .str();
|
|
248 } else if (LibMachine != FileMachine) {
|
|
249 llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
|
|
250 << machineToStr(FileMachine)
|
|
251 << " conflicts with library machine type "
|
|
252 << machineToStr(LibMachine) << LibMachineSource << '\n';
|
|
253 exit(1);
|
|
254 }
|
|
255 }
|
|
256 }
|
|
257
|
|
258 Members.emplace_back(MB);
|
|
259 }
|
|
260
|
|
261 int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {
|
|
262 BumpPtrAllocator Alloc;
|
|
263 StringSaver Saver(Alloc);
|
|
264
|
|
265 // Parse command line arguments.
|
|
266 SmallVector<const char *, 20> NewArgs(ArgsArr.begin(), ArgsArr.end());
|
|
267 cl::ExpandResponseFiles(Saver, cl::TokenizeWindowsCommandLine, NewArgs);
|
|
268 ArgsArr = NewArgs;
|
|
269
|
|
270 LibOptTable Table;
|
|
271 unsigned MissingIndex;
|
|
272 unsigned MissingCount;
|
|
273 opt::InputArgList Args =
|
|
274 Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount);
|
|
275 if (MissingCount) {
|
|
276 llvm::errs() << "missing arg value for \""
|
|
277 << Args.getArgString(MissingIndex) << "\", expected "
|
|
278 << MissingCount
|
|
279 << (MissingCount == 1 ? " argument.\n" : " arguments.\n");
|
|
280 return 1;
|
|
281 }
|
|
282 for (auto *Arg : Args.filtered(OPT_UNKNOWN))
|
|
283 llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args)
|
|
284 << "\n";
|
|
285
|
|
286 // Handle /help
|
|
287 if (Args.hasArg(OPT_help)) {
|
|
288 Table.PrintHelp(outs(), "llvm-lib [options] file...", "LLVM Lib");
|
|
289 return 0;
|
|
290 }
|
|
291
|
173
|
292 // If no input files and not told otherwise, silently do nothing to match
|
|
293 // lib.exe
|
|
294 if (!Args.hasArgNoClaim(OPT_INPUT) && !Args.hasArg(OPT_llvmlibempty))
|
150
|
295 return 0;
|
|
296
|
|
297 if (Args.hasArg(OPT_lst)) {
|
|
298 doList(Args);
|
|
299 return 0;
|
|
300 }
|
|
301
|
|
302 std::vector<StringRef> SearchPaths = getSearchPaths(&Args, Saver);
|
|
303
|
|
304 COFF::MachineTypes LibMachine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;
|
|
305 std::string LibMachineSource;
|
|
306 if (auto *Arg = Args.getLastArg(OPT_machine)) {
|
|
307 LibMachine = getMachineType(Arg->getValue());
|
|
308 if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
|
|
309 llvm::errs() << "unknown /machine: arg " << Arg->getValue() << '\n';
|
|
310 return 1;
|
|
311 }
|
|
312 LibMachineSource =
|
|
313 std::string(" (from '/machine:") + Arg->getValue() + "' flag)";
|
|
314 }
|
|
315
|
|
316 std::vector<std::unique_ptr<MemoryBuffer>> MBs;
|
|
317 StringSet<> Seen;
|
|
318 std::vector<NewArchiveMember> Members;
|
|
319
|
|
320 // Create a NewArchiveMember for each input file.
|
|
321 for (auto *Arg : Args.filtered(OPT_INPUT)) {
|
|
322 // Find a file
|
|
323 std::string Path = findInputFile(Arg->getValue(), SearchPaths);
|
|
324 if (Path.empty()) {
|
|
325 llvm::errs() << Arg->getValue() << ": no such file or directory\n";
|
|
326 return 1;
|
|
327 }
|
|
328
|
|
329 // Input files are uniquified by pathname. If you specify the exact same
|
|
330 // path more than once, all but the first one are ignored.
|
|
331 //
|
|
332 // Note that there's a loophole in the rule; you can prepend `.\` or
|
|
333 // something like that to a path to make it look different, and they are
|
|
334 // handled as if they were different files. This behavior is compatible with
|
|
335 // Microsoft lib.exe.
|
|
336 if (!Seen.insert(Path).second)
|
|
337 continue;
|
|
338
|
|
339 // Open a file.
|
|
340 ErrorOr<std::unique_ptr<MemoryBuffer>> MOrErr =
|
|
341 MemoryBuffer::getFile(Path, -1, false);
|
|
342 fatalOpenError(errorCodeToError(MOrErr.getError()), Path);
|
|
343 MemoryBufferRef MBRef = (*MOrErr)->getMemBufferRef();
|
|
344
|
|
345 // Append a file.
|
|
346 appendFile(Members, LibMachine, LibMachineSource, MBRef);
|
|
347
|
|
348 // Take the ownership of the file buffer to keep the file open.
|
|
349 MBs.push_back(std::move(*MOrErr));
|
|
350 }
|
|
351
|
|
352 // Create an archive file.
|
173
|
353 std::string OutputPath;
|
|
354 if (auto *Arg = Args.getLastArg(OPT_out)) {
|
|
355 OutputPath = Arg->getValue();
|
|
356 } else if (!Members.empty()) {
|
|
357 OutputPath = getDefaultOutputPath(Members[0]);
|
|
358 } else {
|
|
359 llvm::errs() << "no output path given, and cannot infer with no inputs\n";
|
|
360 return 1;
|
|
361 }
|
150
|
362 // llvm-lib uses relative paths for both regular and thin archives, unlike
|
|
363 // standard GNU ar, which only uses relative paths for thin archives and
|
|
364 // basenames for regular archives.
|
|
365 for (NewArchiveMember &Member : Members) {
|
|
366 if (sys::path::is_relative(Member.MemberName)) {
|
|
367 Expected<std::string> PathOrErr =
|
|
368 computeArchiveRelativePath(OutputPath, Member.MemberName);
|
|
369 if (PathOrErr)
|
|
370 Member.MemberName = Saver.save(*PathOrErr);
|
|
371 }
|
|
372 }
|
|
373
|
|
374 if (Error E =
|
|
375 writeArchive(OutputPath, Members,
|
|
376 /*WriteSymtab=*/true, object::Archive::K_GNU,
|
|
377 /*Deterministic*/ true, Args.hasArg(OPT_llvmlibthin))) {
|
|
378 handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
|
|
379 llvm::errs() << OutputPath << ": " << EI.message() << "\n";
|
|
380 });
|
|
381 return 1;
|
|
382 }
|
|
383
|
|
384 return 0;
|
|
385 }
|