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/BinaryFormat/Magic.h" 17 #include "llvm/Object/ArchiveWriter.h" 18 #include "llvm/Option/Arg.h" 19 #include "llvm/Option/ArgList.h" 20 #include "llvm/Option/Option.h" 21 #include "llvm/Support/CommandLine.h" 22 #include "llvm/Support/Path.h" 23 #include "llvm/Support/Process.h" 24 #include "llvm/Support/StringSaver.h" 25 #include "llvm/Support/raw_ostream.h" 26 27 using namespace llvm; 28 29 namespace { 30 31 enum { 32 OPT_INVALID = 0, 33 #define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, 34 #include "Options.inc" 35 #undef OPTION 36 }; 37 38 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; 39 #include "Options.inc" 40 #undef PREFIX 41 42 static const opt::OptTable::Info InfoTable[] = { 43 #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ 44 {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \ 45 X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, 46 #include "Options.inc" 47 #undef OPTION 48 }; 49 50 class LibOptTable : public opt::OptTable { 51 public: 52 LibOptTable() : OptTable(InfoTable, true) {} 53 }; 54 55 } 56 57 static std::string getOutputPath(opt::InputArgList *Args, 58 const NewArchiveMember &FirstMember) { 59 if (auto *Arg = Args->getLastArg(OPT_out)) 60 return Arg->getValue(); 61 SmallString<128> Val = StringRef(FirstMember.Buf->getBufferIdentifier()); 62 sys::path::replace_extension(Val, ".lib"); 63 return Val.str(); 64 } 65 66 static std::vector<StringRef> getSearchPaths(opt::InputArgList *Args, 67 StringSaver &Saver) { 68 std::vector<StringRef> Ret; 69 // Add current directory as first item of the search path. 70 Ret.push_back(""); 71 72 // Add /libpath flags. 73 for (auto *Arg : Args->filtered(OPT_libpath)) 74 Ret.push_back(Arg->getValue()); 75 76 // Add $LIB. 77 Optional<std::string> EnvOpt = sys::Process::GetEnv("LIB"); 78 if (!EnvOpt.hasValue()) 79 return Ret; 80 StringRef Env = Saver.save(*EnvOpt); 81 while (!Env.empty()) { 82 StringRef Path; 83 std::tie(Path, Env) = Env.split(';'); 84 Ret.push_back(Path); 85 } 86 return Ret; 87 } 88 89 static std::string findInputFile(StringRef File, ArrayRef<StringRef> Paths) { 90 for (StringRef Dir : Paths) { 91 SmallString<128> Path = Dir; 92 sys::path::append(Path, File); 93 if (sys::fs::exists(Path)) 94 return Path.str().str(); 95 } 96 return ""; 97 } 98 99 static void fatalOpenError(llvm::Error E, Twine File) { 100 if (!E) 101 return; 102 handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) { 103 llvm::errs() << "error opening '" << File << "': " << EIB.message() << '\n'; 104 exit(1); 105 }); 106 } 107 108 static void doList(opt::InputArgList& Args) { 109 // lib.exe prints the contents of the first archive file. 110 std::unique_ptr<MemoryBuffer> B; 111 for (auto *Arg : Args.filtered(OPT_INPUT)) { 112 // Create or open the archive object. 113 ErrorOr<std::unique_ptr<MemoryBuffer>> MaybeBuf = 114 MemoryBuffer::getFile(Arg->getValue(), -1, false); 115 fatalOpenError(errorCodeToError(MaybeBuf.getError()), Arg->getValue()); 116 117 if (identify_magic(MaybeBuf.get()->getBuffer()) == file_magic::archive) { 118 B = std::move(MaybeBuf.get()); 119 break; 120 } 121 } 122 123 // lib.exe doesn't print an error if no .lib files are passed. 124 if (!B) 125 return; 126 127 Error Err = Error::success(); 128 object::Archive Archive(B.get()->getMemBufferRef(), Err); 129 fatalOpenError(std::move(Err), B->getBufferIdentifier()); 130 131 for (auto &C : Archive.children(Err)) { 132 Expected<StringRef> NameOrErr = C.getName(); 133 fatalOpenError(NameOrErr.takeError(), B->getBufferIdentifier()); 134 StringRef Name = NameOrErr.get(); 135 llvm::outs() << Name << '\n'; 136 } 137 fatalOpenError(std::move(Err), B->getBufferIdentifier()); 138 } 139 140 int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) { 141 BumpPtrAllocator Alloc; 142 StringSaver Saver(Alloc); 143 144 // Parse command line arguments. 145 SmallVector<const char *, 20> NewArgs(ArgsArr.begin(), ArgsArr.end()); 146 cl::ExpandResponseFiles(Saver, cl::TokenizeWindowsCommandLine, NewArgs); 147 ArgsArr = NewArgs; 148 149 LibOptTable Table; 150 unsigned MissingIndex; 151 unsigned MissingCount; 152 opt::InputArgList Args = 153 Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount); 154 if (MissingCount) { 155 llvm::errs() << "missing arg value for \"" 156 << Args.getArgString(MissingIndex) << "\", expected " 157 << MissingCount 158 << (MissingCount == 1 ? " argument.\n" : " arguments.\n"); 159 return 1; 160 } 161 for (auto *Arg : Args.filtered(OPT_UNKNOWN)) 162 llvm::errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n"; 163 164 // Handle /help 165 if (Args.hasArg(OPT_help)) { 166 Table.PrintHelp(outs(), "llvm-lib [options] file...", "LLVM Lib"); 167 return 0; 168 } 169 170 // If no input files, silently do nothing to match lib.exe. 171 if (!Args.hasArgNoClaim(OPT_INPUT)) 172 return 0; 173 174 if (Args.hasArg(OPT_lst)) { 175 doList(Args); 176 return 0; 177 } 178 179 std::vector<StringRef> SearchPaths = getSearchPaths(&Args, Saver); 180 181 // Create a NewArchiveMember for each input file. 182 std::vector<NewArchiveMember> Members; 183 for (auto *Arg : Args.filtered(OPT_INPUT)) { 184 std::string Path = findInputFile(Arg->getValue(), SearchPaths); 185 if (Path.empty()) { 186 llvm::errs() << Arg->getValue() << ": no such file or directory\n"; 187 return 1; 188 } 189 190 Expected<NewArchiveMember> MOrErr = 191 NewArchiveMember::getFile(Saver.save(Path), /*Deterministic=*/true); 192 if (!MOrErr) { 193 handleAllErrors(MOrErr.takeError(), [&](const ErrorInfoBase &EIB) { 194 llvm::errs() << Arg->getValue() << ": " << EIB.message() << "\n"; 195 }); 196 return 1; 197 } 198 199 file_magic Magic = identify_magic(MOrErr->Buf->getBuffer()); 200 if (Magic != file_magic::coff_object && Magic != file_magic::bitcode && 201 Magic != file_magic::windows_resource) { 202 llvm::errs() << Arg->getValue() 203 << ": not a COFF object, bitcode or resource file\n"; 204 return 1; 205 } 206 Members.emplace_back(std::move(*MOrErr)); 207 } 208 209 // Create an archive file. 210 std::string OutputPath = getOutputPath(&Args, Members[0]); 211 // llvm-lib uses relative paths for both regular and thin archives, unlike 212 // standard GNU ar, which only uses relative paths for thin archives and 213 // basenames for regular archives. 214 for (NewArchiveMember &Member : Members) { 215 if (sys::path::is_relative(Member.MemberName)) { 216 Expected<std::string> PathOrErr = 217 computeArchiveRelativePath(OutputPath, Member.MemberName); 218 if (PathOrErr) 219 Member.MemberName = Saver.save(*PathOrErr); 220 } 221 } 222 223 if (Error E = 224 writeArchive(OutputPath, Members, 225 /*WriteSymtab=*/true, object::Archive::K_GNU, 226 /*Deterministic*/ true, Args.hasArg(OPT_llvmlibthin))) { 227 handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) { 228 llvm::errs() << OutputPath << ": " << EI.message() << "\n"; 229 }); 230 return 1; 231 } 232 233 return 0; 234 } 235