Path: blob/main/contrib/llvm-project/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp
35271 views
//===- LibDriver.cpp - lib.exe-compatible driver --------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Defines an interface to a lib.exe-compatible driver that also understands9// bitcode files. Used by llvm-lib and lld-link /lib.10//11//===----------------------------------------------------------------------===//1213#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"14#include "llvm/ADT/STLExtras.h"15#include "llvm/ADT/StringSet.h"16#include "llvm/BinaryFormat/COFF.h"17#include "llvm/BinaryFormat/Magic.h"18#include "llvm/Bitcode/BitcodeReader.h"19#include "llvm/Object/ArchiveWriter.h"20#include "llvm/Object/COFF.h"21#include "llvm/Object/COFFModuleDefinition.h"22#include "llvm/Object/WindowsMachineFlag.h"23#include "llvm/Option/Arg.h"24#include "llvm/Option/ArgList.h"25#include "llvm/Option/OptTable.h"26#include "llvm/Option/Option.h"27#include "llvm/Support/CommandLine.h"28#include "llvm/Support/Path.h"29#include "llvm/Support/Process.h"30#include "llvm/Support/StringSaver.h"31#include "llvm/Support/raw_ostream.h"32#include <optional>3334using namespace llvm;35using namespace llvm::object;3637namespace {3839enum {40OPT_INVALID = 0,41#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),42#include "Options.inc"43#undef OPTION44};4546#define PREFIX(NAME, VALUE) \47static constexpr StringLiteral NAME##_init[] = VALUE; \48static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \49std::size(NAME##_init) - 1);50#include "Options.inc"51#undef PREFIX5253using namespace llvm::opt;54static constexpr opt::OptTable::Info InfoTable[] = {55#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),56#include "Options.inc"57#undef OPTION58};5960class LibOptTable : public opt::GenericOptTable {61public:62LibOptTable() : opt::GenericOptTable(InfoTable, true) {}63};64} // namespace6566static std::string getDefaultOutputPath(const NewArchiveMember &FirstMember) {67SmallString<128> Val = StringRef(FirstMember.Buf->getBufferIdentifier());68sys::path::replace_extension(Val, ".lib");69return std::string(Val);70}7172static std::vector<StringRef> getSearchPaths(opt::InputArgList *Args,73StringSaver &Saver) {74std::vector<StringRef> Ret;75// Add current directory as first item of the search path.76Ret.push_back("");7778// Add /libpath flags.79for (auto *Arg : Args->filtered(OPT_libpath))80Ret.push_back(Arg->getValue());8182// Add $LIB.83std::optional<std::string> EnvOpt = sys::Process::GetEnv("LIB");84if (!EnvOpt)85return Ret;86StringRef Env = Saver.save(*EnvOpt);87while (!Env.empty()) {88StringRef Path;89std::tie(Path, Env) = Env.split(';');90Ret.push_back(Path);91}92return Ret;93}9495// Opens a file. Path has to be resolved already. (used for def file)96std::unique_ptr<MemoryBuffer> openFile(const Twine &Path) {97ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =98MemoryBuffer::getFile(Path, /*IsText=*/true);99100if (std::error_code EC = MB.getError()) {101llvm::errs() << "cannot open file " << Path << ": " << EC.message() << "\n";102return nullptr;103}104105return std::move(*MB);106}107108static std::string findInputFile(StringRef File, ArrayRef<StringRef> Paths) {109for (StringRef Dir : Paths) {110SmallString<128> Path = Dir;111sys::path::append(Path, File);112if (sys::fs::exists(Path))113return std::string(Path);114}115return "";116}117118static void fatalOpenError(llvm::Error E, Twine File) {119if (!E)120return;121handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {122llvm::errs() << "error opening '" << File << "': " << EIB.message() << '\n';123exit(1);124});125}126127static void doList(opt::InputArgList &Args) {128// lib.exe prints the contents of the first archive file.129std::unique_ptr<MemoryBuffer> B;130for (auto *Arg : Args.filtered(OPT_INPUT)) {131// Create or open the archive object.132ErrorOr<std::unique_ptr<MemoryBuffer>> MaybeBuf = MemoryBuffer::getFile(133Arg->getValue(), /*IsText=*/false, /*RequiresNullTerminator=*/false);134fatalOpenError(errorCodeToError(MaybeBuf.getError()), Arg->getValue());135136if (identify_magic(MaybeBuf.get()->getBuffer()) == file_magic::archive) {137B = std::move(MaybeBuf.get());138break;139}140}141142// lib.exe doesn't print an error if no .lib files are passed.143if (!B)144return;145146Error Err = Error::success();147object::Archive Archive(B->getMemBufferRef(), Err);148fatalOpenError(std::move(Err), B->getBufferIdentifier());149150std::vector<StringRef> Names;151for (auto &C : Archive.children(Err)) {152Expected<StringRef> NameOrErr = C.getName();153fatalOpenError(NameOrErr.takeError(), B->getBufferIdentifier());154Names.push_back(NameOrErr.get());155}156for (auto Name : reverse(Names))157llvm::outs() << Name << '\n';158fatalOpenError(std::move(Err), B->getBufferIdentifier());159}160161static Expected<COFF::MachineTypes> getCOFFFileMachine(MemoryBufferRef MB) {162std::error_code EC;163auto Obj = object::COFFObjectFile::create(MB);164if (!Obj)165return Obj.takeError();166167uint16_t Machine = (*Obj)->getMachine();168if (Machine != COFF::IMAGE_FILE_MACHINE_I386 &&169Machine != COFF::IMAGE_FILE_MACHINE_AMD64 &&170Machine != COFF::IMAGE_FILE_MACHINE_ARMNT && !COFF::isAnyArm64(Machine)) {171return createStringError(inconvertibleErrorCode(),172"unknown machine: " + std::to_string(Machine));173}174175return static_cast<COFF::MachineTypes>(Machine);176}177178static Expected<COFF::MachineTypes> getBitcodeFileMachine(MemoryBufferRef MB) {179Expected<std::string> TripleStr = getBitcodeTargetTriple(MB);180if (!TripleStr)181return TripleStr.takeError();182183Triple T(*TripleStr);184switch (T.getArch()) {185case Triple::x86:186return COFF::IMAGE_FILE_MACHINE_I386;187case Triple::x86_64:188return COFF::IMAGE_FILE_MACHINE_AMD64;189case Triple::arm:190return COFF::IMAGE_FILE_MACHINE_ARMNT;191case Triple::aarch64:192return T.isWindowsArm64EC() ? COFF::IMAGE_FILE_MACHINE_ARM64EC193: COFF::IMAGE_FILE_MACHINE_ARM64;194default:195return createStringError(inconvertibleErrorCode(),196"unknown arch in target triple: " + *TripleStr);197}198}199200static bool machineMatches(COFF::MachineTypes LibMachine,201COFF::MachineTypes FileMachine) {202if (LibMachine == FileMachine)203return true;204// ARM64EC mode allows both pure ARM64, ARM64EC and X64 objects to be mixed in205// the archive.206switch (LibMachine) {207case COFF::IMAGE_FILE_MACHINE_ARM64:208return FileMachine == COFF::IMAGE_FILE_MACHINE_ARM64X;209case COFF::IMAGE_FILE_MACHINE_ARM64EC:210case COFF::IMAGE_FILE_MACHINE_ARM64X:211return COFF::isAnyArm64(FileMachine) ||212FileMachine == COFF::IMAGE_FILE_MACHINE_AMD64;213default:214return false;215}216}217218static void appendFile(std::vector<NewArchiveMember> &Members,219COFF::MachineTypes &LibMachine,220std::string &LibMachineSource, MemoryBufferRef MB) {221file_magic Magic = identify_magic(MB.getBuffer());222223if (Magic != file_magic::coff_object && Magic != file_magic::bitcode &&224Magic != file_magic::archive && Magic != file_magic::windows_resource &&225Magic != file_magic::coff_import_library) {226llvm::errs() << MB.getBufferIdentifier()227<< ": not a COFF object, bitcode, archive, import library or "228"resource file\n";229exit(1);230}231232// If a user attempts to add an archive to another archive, llvm-lib doesn't233// handle the first archive file as a single file. Instead, it extracts all234// members from the archive and add them to the second archive. This behavior235// is for compatibility with Microsoft's lib command.236if (Magic == file_magic::archive) {237Error Err = Error::success();238object::Archive Archive(MB, Err);239fatalOpenError(std::move(Err), MB.getBufferIdentifier());240241for (auto &C : Archive.children(Err)) {242Expected<MemoryBufferRef> ChildMB = C.getMemoryBufferRef();243if (!ChildMB) {244handleAllErrors(ChildMB.takeError(), [&](const ErrorInfoBase &EIB) {245llvm::errs() << MB.getBufferIdentifier() << ": " << EIB.message()246<< "\n";247});248exit(1);249}250251appendFile(Members, LibMachine, LibMachineSource, *ChildMB);252}253254fatalOpenError(std::move(Err), MB.getBufferIdentifier());255return;256}257258// Check that all input files have the same machine type.259// Mixing normal objects and LTO bitcode files is fine as long as they260// have the same machine type.261// Doing this here duplicates the header parsing work that writeArchive()262// below does, but it's not a lot of work and it's a bit awkward to do263// in writeArchive() which needs to support many tools, can't assume the264// input is COFF, and doesn't have a good way to report errors.265if (Magic == file_magic::coff_object || Magic == file_magic::bitcode) {266Expected<COFF::MachineTypes> MaybeFileMachine =267(Magic == file_magic::coff_object) ? getCOFFFileMachine(MB)268: getBitcodeFileMachine(MB);269if (!MaybeFileMachine) {270handleAllErrors(MaybeFileMachine.takeError(),271[&](const ErrorInfoBase &EIB) {272llvm::errs() << MB.getBufferIdentifier() << ": "273<< EIB.message() << "\n";274});275exit(1);276}277COFF::MachineTypes FileMachine = *MaybeFileMachine;278279// FIXME: Once lld-link rejects multiple resource .obj files:280// Call convertResToCOFF() on .res files and add the resulting281// COFF file to the .lib output instead of adding the .res file, and remove282// this check. See PR42180.283if (FileMachine != COFF::IMAGE_FILE_MACHINE_UNKNOWN) {284if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {285if (FileMachine == COFF::IMAGE_FILE_MACHINE_ARM64EC) {286llvm::errs() << MB.getBufferIdentifier() << ": file machine type "287<< machineToStr(FileMachine)288<< " conflicts with inferred library machine type,"289<< " use /machine:arm64ec or /machine:arm64x\n";290exit(1);291}292LibMachine = FileMachine;293LibMachineSource =294(" (inferred from earlier file '" + MB.getBufferIdentifier() + "')")295.str();296} else if (!machineMatches(LibMachine, FileMachine)) {297llvm::errs() << MB.getBufferIdentifier() << ": file machine type "298<< machineToStr(FileMachine)299<< " conflicts with library machine type "300<< machineToStr(LibMachine) << LibMachineSource << '\n';301exit(1);302}303}304}305306Members.emplace_back(MB);307}308309int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {310BumpPtrAllocator Alloc;311StringSaver Saver(Alloc);312313// Parse command line arguments.314SmallVector<const char *, 20> NewArgs(ArgsArr.begin(), ArgsArr.end());315cl::ExpandResponseFiles(Saver, cl::TokenizeWindowsCommandLine, NewArgs);316ArgsArr = NewArgs;317318LibOptTable Table;319unsigned MissingIndex;320unsigned MissingCount;321opt::InputArgList Args =322Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount);323if (MissingCount) {324llvm::errs() << "missing arg value for \""325<< Args.getArgString(MissingIndex) << "\", expected "326<< MissingCount327<< (MissingCount == 1 ? " argument.\n" : " arguments.\n");328return 1;329}330for (auto *Arg : Args.filtered(OPT_UNKNOWN))331llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args)332<< "\n";333334// Handle /help335if (Args.hasArg(OPT_help)) {336Table.printHelp(outs(), "llvm-lib [options] file...", "LLVM Lib");337return 0;338}339340// Parse /ignore:341llvm::StringSet<> IgnoredWarnings;342for (auto *Arg : Args.filtered(OPT_ignore))343IgnoredWarnings.insert(Arg->getValue());344345// get output library path, if any346std::string OutputPath;347if (auto *Arg = Args.getLastArg(OPT_out)) {348OutputPath = Arg->getValue();349}350351COFF::MachineTypes LibMachine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;352std::string LibMachineSource;353if (auto *Arg = Args.getLastArg(OPT_machine)) {354LibMachine = getMachineType(Arg->getValue());355if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {356llvm::errs() << "unknown /machine: arg " << Arg->getValue() << '\n';357return 1;358}359LibMachineSource =360std::string(" (from '/machine:") + Arg->getValue() + "' flag)";361}362363// create an import library364if (Args.hasArg(OPT_deffile)) {365366if (OutputPath.empty()) {367llvm::errs() << "no output path given\n";368return 1;369}370371if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {372llvm::errs() << "/def option requires /machine to be specified" << '\n';373return 1;374}375376std::unique_ptr<MemoryBuffer> MB =377openFile(Args.getLastArg(OPT_deffile)->getValue());378if (!MB)379return 1;380381if (!MB->getBufferSize()) {382llvm::errs() << "definition file empty\n";383return 1;384}385386Expected<COFFModuleDefinition> Def =387parseCOFFModuleDefinition(*MB, LibMachine, /*MingwDef=*/false);388389if (!Def) {390llvm::errs() << "error parsing definition\n"391<< errorToErrorCode(Def.takeError()).message();392return 1;393}394395std::vector<COFFShortExport> NativeExports;396std::string OutputFile = Def->OutputFile;397398if (isArm64EC(LibMachine) && Args.hasArg(OPT_nativedeffile)) {399std::unique_ptr<MemoryBuffer> NativeMB =400openFile(Args.getLastArg(OPT_nativedeffile)->getValue());401if (!NativeMB)402return 1;403404if (!NativeMB->getBufferSize()) {405llvm::errs() << "native definition file empty\n";406return 1;407}408409Expected<COFFModuleDefinition> NativeDef =410parseCOFFModuleDefinition(*NativeMB, COFF::IMAGE_FILE_MACHINE_ARM64);411412if (!NativeDef) {413llvm::errs() << "error parsing native definition\n"414<< errorToErrorCode(NativeDef.takeError()).message();415return 1;416}417NativeExports = std::move(NativeDef->Exports);418OutputFile = std::move(NativeDef->OutputFile);419}420421return writeImportLibrary(OutputFile, OutputPath, Def->Exports, LibMachine,422/*MinGW=*/false, NativeExports)423? 1424: 0;425}426427// If no input files and not told otherwise, silently do nothing to match428// lib.exe429if (!Args.hasArgNoClaim(OPT_INPUT) && !Args.hasArg(OPT_llvmlibempty)) {430if (!IgnoredWarnings.contains("emptyoutput")) {431llvm::errs() << "warning: no input files, not writing output file\n";432llvm::errs() << " pass /llvmlibempty to write empty .lib file,\n";433llvm::errs() << " pass /ignore:emptyoutput to suppress warning\n";434if (Args.hasFlag(OPT_WX, OPT_WX_no, false)) {435llvm::errs() << "treating warning as error due to /WX\n";436return 1;437}438}439return 0;440}441442if (Args.hasArg(OPT_lst)) {443doList(Args);444return 0;445}446447std::vector<StringRef> SearchPaths = getSearchPaths(&Args, Saver);448449std::vector<std::unique_ptr<MemoryBuffer>> MBs;450StringSet<> Seen;451std::vector<NewArchiveMember> Members;452453// Create a NewArchiveMember for each input file.454for (auto *Arg : Args.filtered(OPT_INPUT)) {455// Find a file456std::string Path = findInputFile(Arg->getValue(), SearchPaths);457if (Path.empty()) {458llvm::errs() << Arg->getValue() << ": no such file or directory\n";459return 1;460}461462// Input files are uniquified by pathname. If you specify the exact same463// path more than once, all but the first one are ignored.464//465// Note that there's a loophole in the rule; you can prepend `.\` or466// something like that to a path to make it look different, and they are467// handled as if they were different files. This behavior is compatible with468// Microsoft lib.exe.469if (!Seen.insert(Path).second)470continue;471472// Open a file.473ErrorOr<std::unique_ptr<MemoryBuffer>> MOrErr = MemoryBuffer::getFile(474Path, /*IsText=*/false, /*RequiresNullTerminator=*/false);475fatalOpenError(errorCodeToError(MOrErr.getError()), Path);476MemoryBufferRef MBRef = (*MOrErr)->getMemBufferRef();477478// Append a file.479appendFile(Members, LibMachine, LibMachineSource, MBRef);480481// Take the ownership of the file buffer to keep the file open.482MBs.push_back(std::move(*MOrErr));483}484485// Create an archive file.486if (OutputPath.empty()) {487if (!Members.empty()) {488OutputPath = getDefaultOutputPath(Members[0]);489} else {490llvm::errs() << "no output path given, and cannot infer with no inputs\n";491return 1;492}493}494// llvm-lib uses relative paths for both regular and thin archives, unlike495// standard GNU ar, which only uses relative paths for thin archives and496// basenames for regular archives.497for (NewArchiveMember &Member : Members) {498if (sys::path::is_relative(Member.MemberName)) {499Expected<std::string> PathOrErr =500computeArchiveRelativePath(OutputPath, Member.MemberName);501if (PathOrErr)502Member.MemberName = Saver.save(*PathOrErr);503}504}505506// For compatibility with MSVC, reverse member vector after de-duplication.507std::reverse(Members.begin(), Members.end());508509bool Thin = Args.hasArg(OPT_llvmlibthin);510if (Error E = writeArchive(511OutputPath, Members, SymtabWritingMode::NormalSymtab,512Thin ? object::Archive::K_GNU : object::Archive::K_COFF,513/*Deterministic=*/true, Thin, nullptr, COFF::isArm64EC(LibMachine))) {514handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {515llvm::errs() << OutputPath << ": " << EI.message() << "\n";516});517return 1;518}519520return 0;521}522523524