Path: blob/main/contrib/llvm-project/llvm/tools/llvm-ar/llvm-ar.cpp
35231 views
//===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===//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// Builds up (relatively) standard unix archive files (.a) containing LLVM9// bitcode or other files.10//11//===----------------------------------------------------------------------===//1213#include "llvm/ADT/StringExtras.h"14#include "llvm/ADT/StringSwitch.h"15#include "llvm/BinaryFormat/Magic.h"16#include "llvm/IR/LLVMContext.h"17#include "llvm/Object/Archive.h"18#include "llvm/Object/ArchiveWriter.h"19#include "llvm/Object/SymbolicFile.h"20#include "llvm/Support/Chrono.h"21#include "llvm/Support/CommandLine.h"22#include "llvm/Support/ConvertUTF.h"23#include "llvm/Support/Errc.h"24#include "llvm/Support/FileSystem.h"25#include "llvm/Support/Format.h"26#include "llvm/Support/FormatVariadic.h"27#include "llvm/Support/LLVMDriver.h"28#include "llvm/Support/LineIterator.h"29#include "llvm/Support/MemoryBuffer.h"30#include "llvm/Support/Path.h"31#include "llvm/Support/Process.h"32#include "llvm/Support/StringSaver.h"33#include "llvm/Support/TargetSelect.h"34#include "llvm/Support/ToolOutputFile.h"35#include "llvm/Support/WithColor.h"36#include "llvm/Support/raw_ostream.h"37#include "llvm/TargetParser/Host.h"38#include "llvm/TargetParser/Triple.h"39#include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"40#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"4142#if !defined(_MSC_VER) && !defined(__MINGW32__)43#include <unistd.h>44#else45#include <io.h>46#endif4748#ifdef _WIN3249#include "llvm/Support/Windows/WindowsSupport.h"50#endif5152using namespace llvm;53using namespace llvm::object;5455// The name this program was invoked as.56static StringRef ToolName;5758// The basename of this program.59static StringRef Stem;6061static void printRanLibHelp(StringRef ToolName) {62outs() << "OVERVIEW: LLVM ranlib\n\n"63<< "Generate an index for archives\n\n"64<< "USAGE: " + ToolName + " archive...\n\n"65<< "OPTIONS:\n"66<< " -h --help - Display available options\n"67<< " -V --version - Display the version of this program\n"68<< " -D - Use zero for timestamps and uids/gids "69"(default)\n"70<< " -U - Use actual timestamps and uids/gids\n"71<< " -X{32|64|32_64|any} - Specify which archive symbol tables "72"should be generated if they do not already exist (AIX OS only)\n";73}7475static void printArHelp(StringRef ToolName) {76const char ArOptions[] =77R"(OPTIONS:78--format - archive format to create79=default - default80=gnu - gnu81=darwin - darwin82=bsd - bsd83=bigarchive - big archive (AIX OS)84=coff - coff85--plugin=<string> - ignored for compatibility86-h --help - display this help and exit87--output - the directory to extract archive members to88--rsp-quoting - quoting style for response files89=posix - posix90=windows - windows91--thin - create a thin archive92--version - print the version and exit93-X{32|64|32_64|any} - object mode (only for AIX OS)94@<file> - read options from <file>9596OPERATIONS:97d - delete [files] from the archive98m - move [files] in the archive99p - print contents of [files] found in the archive100q - quick append [files] to the archive101r - replace or insert [files] into the archive102s - act as ranlib103t - display list of files in archive104x - extract [files] from the archive105106MODIFIERS:107[a] - put [files] after [relpos]108[b] - put [files] before [relpos] (same as [i])109[c] - do not warn if archive had to be created110[D] - use zero for timestamps and uids/gids (default)111[h] - display this help and exit112[i] - put [files] before [relpos] (same as [b])113[l] - ignored for compatibility114[L] - add archive's contents115[N] - use instance [count] of name116[o] - preserve original dates117[O] - display member offsets118[P] - use full names when matching (implied for thin archives)119[s] - create an archive index (cf. ranlib)120[S] - do not build a symbol table121[T] - deprecated, use --thin instead122[u] - update only [files] newer than archive contents123[U] - use actual timestamps and uids/gids124[v] - be verbose about actions taken125[V] - display the version and exit126)";127128outs() << "OVERVIEW: LLVM Archiver\n\n"129<< "USAGE: " + ToolName +130" [options] [-]<operation>[modifiers] [relpos] "131"[count] <archive> [files]\n"132<< " " + ToolName + " -M [<mri-script]\n\n";133134outs() << ArOptions;135}136137static void printHelpMessage() {138if (Stem.contains_insensitive("ranlib"))139printRanLibHelp(Stem);140else if (Stem.contains_insensitive("ar"))141printArHelp(Stem);142}143144static unsigned MRILineNumber;145static bool ParsingMRIScript;146147// Show the error plus the usage message, and exit.148[[noreturn]] static void badUsage(Twine Error) {149WithColor::error(errs(), ToolName) << Error << "\n";150printHelpMessage();151exit(1);152}153154// Show the error message and exit.155[[noreturn]] static void fail(Twine Error) {156if (ParsingMRIScript) {157WithColor::error(errs(), ToolName)158<< "script line " << MRILineNumber << ": " << Error << "\n";159} else {160WithColor::error(errs(), ToolName) << Error << "\n";161}162exit(1);163}164165static void failIfError(std::error_code EC, Twine Context = "") {166if (!EC)167return;168169std::string ContextStr = Context.str();170if (ContextStr.empty())171fail(EC.message());172fail(Context + ": " + EC.message());173}174175static void failIfError(Error E, Twine Context = "") {176if (!E)177return;178179handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {180std::string ContextStr = Context.str();181if (ContextStr.empty())182fail(EIB.message());183fail(Context + ": " + EIB.message());184});185}186187static void warn(Twine Message) {188WithColor::warning(errs(), ToolName) << Message << "\n";189}190191static SmallVector<const char *, 256> PositionalArgs;192193static bool MRI;194195namespace {196enum Format { Default, GNU, COFF, BSD, DARWIN, BIGARCHIVE, Unknown };197}198199static Format FormatType = Default;200201static std::string Options;202203// This enumeration delineates the kinds of operations on an archive204// that are permitted.205enum ArchiveOperation {206Print, ///< Print the contents of the archive207Delete, ///< Delete the specified members208Move, ///< Move members to end or as given by {a,b,i} modifiers209QuickAppend, ///< Quickly append to end of archive210ReplaceOrInsert, ///< Replace or Insert members211DisplayTable, ///< Display the table of contents212Extract, ///< Extract files back to file system213CreateSymTab ///< Create a symbol table in an existing archive214};215216enum class BitModeTy { Bit32, Bit64, Bit32_64, Any, Unknown };217218static BitModeTy BitMode = BitModeTy::Bit32;219220// Modifiers to follow operation to vary behavior221static bool AddAfter = false; ///< 'a' modifier222static bool AddBefore = false; ///< 'b' modifier223static bool Create = false; ///< 'c' modifier224static bool OriginalDates = false; ///< 'o' modifier225static bool DisplayMemberOffsets = false; ///< 'O' modifier226static bool CompareFullPath = false; ///< 'P' modifier227static bool OnlyUpdate = false; ///< 'u' modifier228static bool Verbose = false; ///< 'v' modifier229static SymtabWritingMode Symtab =230SymtabWritingMode::NormalSymtab; ///< 's' modifier231static bool Deterministic = true; ///< 'D' and 'U' modifiers232static bool Thin = false; ///< 'T' modifier233static bool AddLibrary = false; ///< 'L' modifier234235// Relative Positional Argument (for insert/move). This variable holds236// the name of the archive member to which the 'a', 'b' or 'i' modifier237// refers. Only one of 'a', 'b' or 'i' can be specified so we only need238// one variable.239static std::string RelPos;240241// Count parameter for 'N' modifier. This variable specifies which file should242// match for extract/delete operations when there are multiple matches. This is243// 1-indexed. A value of 0 is invalid, and implies 'N' is not used.244static int CountParam = 0;245246// This variable holds the name of the archive file as given on the247// command line.248static std::string ArchiveName;249250// Output directory specified by --output.251static std::string OutputDir;252253static std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers;254static std::vector<std::unique_ptr<object::Archive>> Archives;255256// This variable holds the list of member files to proecess, as given257// on the command line.258static std::vector<StringRef> Members;259260// Static buffer to hold StringRefs.261static BumpPtrAllocator Alloc;262263// Extract the member filename from the command line for the [relpos] argument264// associated with a, b, and i modifiers265static void getRelPos() {266if (PositionalArgs.empty())267fail("expected [relpos] for 'a', 'b', or 'i' modifier");268RelPos = PositionalArgs[0];269PositionalArgs.erase(PositionalArgs.begin());270}271272// Extract the parameter from the command line for the [count] argument273// associated with the N modifier274static void getCountParam() {275if (PositionalArgs.empty())276badUsage("expected [count] for 'N' modifier");277auto CountParamArg = StringRef(PositionalArgs[0]);278if (CountParamArg.getAsInteger(10, CountParam))279badUsage("value for [count] must be numeric, got: " + CountParamArg);280if (CountParam < 1)281badUsage("value for [count] must be positive, got: " + CountParamArg);282PositionalArgs.erase(PositionalArgs.begin());283}284285// Get the archive file name from the command line286static void getArchive() {287if (PositionalArgs.empty())288badUsage("an archive name must be specified");289ArchiveName = PositionalArgs[0];290PositionalArgs.erase(PositionalArgs.begin());291}292293static object::Archive &readLibrary(const Twine &Library) {294auto BufOrErr = MemoryBuffer::getFile(Library, /*IsText=*/false,295/*RequiresNullTerminator=*/false);296failIfError(BufOrErr.getError(), "could not open library " + Library);297ArchiveBuffers.push_back(std::move(*BufOrErr));298auto LibOrErr =299object::Archive::create(ArchiveBuffers.back()->getMemBufferRef());300failIfError(errorToErrorCode(LibOrErr.takeError()),301"could not parse library");302Archives.push_back(std::move(*LibOrErr));303return *Archives.back();304}305306static void runMRIScript();307308// Parse the command line options as presented and return the operation309// specified. Process all modifiers and check to make sure that constraints on310// modifier/operation pairs have not been violated.311static ArchiveOperation parseCommandLine() {312if (MRI) {313if (!PositionalArgs.empty() || !Options.empty())314badUsage("cannot mix -M and other options");315runMRIScript();316}317318// Keep track of number of operations. We can only specify one319// per execution.320unsigned NumOperations = 0;321322// Keep track of the number of positional modifiers (a,b,i). Only323// one can be specified.324unsigned NumPositional = 0;325326// Keep track of which operation was requested327ArchiveOperation Operation;328329bool MaybeJustCreateSymTab = false;330331for (unsigned i = 0; i < Options.size(); ++i) {332switch (Options[i]) {333case 'd':334++NumOperations;335Operation = Delete;336break;337case 'm':338++NumOperations;339Operation = Move;340break;341case 'p':342++NumOperations;343Operation = Print;344break;345case 'q':346++NumOperations;347Operation = QuickAppend;348break;349case 'r':350++NumOperations;351Operation = ReplaceOrInsert;352break;353case 't':354++NumOperations;355Operation = DisplayTable;356break;357case 'x':358++NumOperations;359Operation = Extract;360break;361case 'c':362Create = true;363break;364case 'l': /* accepted but unused */365break;366case 'o':367OriginalDates = true;368break;369case 'O':370DisplayMemberOffsets = true;371break;372case 'P':373CompareFullPath = true;374break;375case 's':376Symtab = SymtabWritingMode::NormalSymtab;377MaybeJustCreateSymTab = true;378break;379case 'S':380Symtab = SymtabWritingMode::NoSymtab;381break;382case 'u':383OnlyUpdate = true;384break;385case 'v':386Verbose = true;387break;388case 'a':389getRelPos();390AddAfter = true;391NumPositional++;392break;393case 'b':394getRelPos();395AddBefore = true;396NumPositional++;397break;398case 'i':399getRelPos();400AddBefore = true;401NumPositional++;402break;403case 'D':404Deterministic = true;405break;406case 'U':407Deterministic = false;408break;409case 'N':410getCountParam();411break;412case 'T':413Thin = true;414break;415case 'L':416AddLibrary = true;417break;418case 'V':419cl::PrintVersionMessage();420exit(0);421case 'h':422printHelpMessage();423exit(0);424default:425badUsage(std::string("unknown option ") + Options[i]);426}427}428429// Thin archives store path names, so P should be forced.430if (Thin)431CompareFullPath = true;432433// At this point, the next thing on the command line must be434// the archive name.435getArchive();436437// Everything on the command line at this point is a member.438Members.assign(PositionalArgs.begin(), PositionalArgs.end());439440if (NumOperations == 0 && MaybeJustCreateSymTab) {441NumOperations = 1;442Operation = CreateSymTab;443if (!Members.empty())444badUsage("the 's' operation takes only an archive as argument");445}446447// Perform various checks on the operation/modifier specification448// to make sure we are dealing with a legal request.449if (NumOperations == 0)450badUsage("you must specify at least one of the operations");451if (NumOperations > 1)452badUsage("only one operation may be specified");453if (NumPositional > 1)454badUsage("you may only specify one of 'a', 'b', and 'i' modifiers");455if (AddAfter || AddBefore)456if (Operation != Move && Operation != ReplaceOrInsert)457badUsage("the 'a', 'b' and 'i' modifiers can only be specified with "458"the 'm' or 'r' operations");459if (CountParam)460if (Operation != Extract && Operation != Delete)461badUsage("the 'N' modifier can only be specified with the 'x' or 'd' "462"operations");463if (OriginalDates && Operation != Extract)464badUsage("the 'o' modifier is only applicable to the 'x' operation");465if (OnlyUpdate && Operation != ReplaceOrInsert)466badUsage("the 'u' modifier is only applicable to the 'r' operation");467if (AddLibrary && Operation != QuickAppend)468badUsage("the 'L' modifier is only applicable to the 'q' operation");469470if (!OutputDir.empty()) {471if (Operation != Extract)472badUsage("--output is only applicable to the 'x' operation");473bool IsDir = false;474// If OutputDir is not a directory, create_directories may still succeed if475// all components of the path prefix are directories. Test is_directory as476// well.477if (!sys::fs::create_directories(OutputDir))478sys::fs::is_directory(OutputDir, IsDir);479if (!IsDir)480fail("'" + OutputDir + "' is not a directory");481}482483// Return the parsed operation to the caller484return Operation;485}486487// Implements the 'p' operation. This function traverses the archive488// looking for members that match the path list.489static void doPrint(StringRef Name, const object::Archive::Child &C) {490if (Verbose)491outs() << "Printing " << Name << "\n";492493Expected<StringRef> DataOrErr = C.getBuffer();494failIfError(DataOrErr.takeError());495StringRef Data = *DataOrErr;496outs().write(Data.data(), Data.size());497}498499// Utility function for printing out the file mode when the 't' operation is in500// verbose mode.501static void printMode(unsigned mode) {502outs() << ((mode & 004) ? "r" : "-");503outs() << ((mode & 002) ? "w" : "-");504outs() << ((mode & 001) ? "x" : "-");505}506507// Implement the 't' operation. This function prints out just508// the file names of each of the members. However, if verbose mode is requested509// ('v' modifier) then the file type, permission mode, user, group, size, and510// modification time are also printed.511static void doDisplayTable(StringRef Name, const object::Archive::Child &C) {512if (Verbose) {513Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();514failIfError(ModeOrErr.takeError());515sys::fs::perms Mode = ModeOrErr.get();516printMode((Mode >> 6) & 007);517printMode((Mode >> 3) & 007);518printMode(Mode & 007);519Expected<unsigned> UIDOrErr = C.getUID();520failIfError(UIDOrErr.takeError());521outs() << ' ' << UIDOrErr.get();522Expected<unsigned> GIDOrErr = C.getGID();523failIfError(GIDOrErr.takeError());524outs() << '/' << GIDOrErr.get();525Expected<uint64_t> Size = C.getSize();526failIfError(Size.takeError());527outs() << ' ' << format("%6llu", Size.get());528auto ModTimeOrErr = C.getLastModified();529failIfError(ModTimeOrErr.takeError());530// Note: formatv() only handles the default TimePoint<>, which is in531// nanoseconds.532// TODO: fix format_provider<TimePoint<>> to allow other units.533sys::TimePoint<> ModTimeInNs = ModTimeOrErr.get();534outs() << ' ' << formatv("{0:%b %e %H:%M %Y}", ModTimeInNs);535outs() << ' ';536}537538if (C.getParent()->isThin()) {539if (!sys::path::is_absolute(Name)) {540StringRef ParentDir = sys::path::parent_path(ArchiveName);541if (!ParentDir.empty())542outs() << sys::path::convert_to_slash(ParentDir) << '/';543}544outs() << Name;545} else {546outs() << Name;547if (DisplayMemberOffsets)548outs() << " 0x" << utohexstr(C.getDataOffset(), true);549}550outs() << '\n';551}552553static std::string normalizePath(StringRef Path) {554return CompareFullPath ? sys::path::convert_to_slash(Path)555: std::string(sys::path::filename(Path));556}557558static bool comparePaths(StringRef Path1, StringRef Path2) {559// When on Windows this function calls CompareStringOrdinal560// as Windows file paths are case-insensitive.561// CompareStringOrdinal compares two Unicode strings for562// binary equivalence and allows for case insensitivity.563#ifdef _WIN32564SmallVector<wchar_t, 128> WPath1, WPath2;565failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path1), WPath1));566failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path2), WPath2));567568return CompareStringOrdinal(WPath1.data(), WPath1.size(), WPath2.data(),569WPath2.size(), true) == CSTR_EQUAL;570#else571return normalizePath(Path1) == normalizePath(Path2);572#endif573}574575// Implement the 'x' operation. This function extracts files back to the file576// system.577static void doExtract(StringRef Name, const object::Archive::Child &C) {578// Retain the original mode.579Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();580failIfError(ModeOrErr.takeError());581sys::fs::perms Mode = ModeOrErr.get();582583StringRef outputFilePath;584SmallString<128> path;585if (OutputDir.empty()) {586outputFilePath = sys::path::filename(Name);587} else {588sys::path::append(path, OutputDir, sys::path::filename(Name));589outputFilePath = path.str();590}591592if (Verbose)593outs() << "x - " << outputFilePath << '\n';594595int FD;596failIfError(sys::fs::openFileForWrite(outputFilePath, FD,597sys::fs::CD_CreateAlways,598sys::fs::OF_None, Mode),599Name);600601{602raw_fd_ostream file(FD, false);603604// Get the data and its length605Expected<StringRef> BufOrErr = C.getBuffer();606failIfError(BufOrErr.takeError());607StringRef Data = BufOrErr.get();608609// Write the data.610file.write(Data.data(), Data.size());611}612613// If we're supposed to retain the original modification times, etc. do so614// now.615if (OriginalDates) {616auto ModTimeOrErr = C.getLastModified();617failIfError(ModTimeOrErr.takeError());618failIfError(619sys::fs::setLastAccessAndModificationTime(FD, ModTimeOrErr.get()));620}621622if (close(FD))623fail("Could not close the file");624}625626static bool shouldCreateArchive(ArchiveOperation Op) {627switch (Op) {628case Print:629case Delete:630case Move:631case DisplayTable:632case Extract:633case CreateSymTab:634return false;635636case QuickAppend:637case ReplaceOrInsert:638return true;639}640641llvm_unreachable("Missing entry in covered switch.");642}643644static bool isValidInBitMode(Binary &Bin) {645if (BitMode == BitModeTy::Bit32_64 || BitMode == BitModeTy::Any)646return true;647648if (SymbolicFile *SymFile = dyn_cast<SymbolicFile>(&Bin)) {649bool Is64Bit = SymFile->is64Bit();650if ((Is64Bit && (BitMode == BitModeTy::Bit32)) ||651(!Is64Bit && (BitMode == BitModeTy::Bit64)))652return false;653}654// In AIX "ar", non-object files are always considered to have a valid bit655// mode.656return true;657}658659Expected<std::unique_ptr<Binary>> getAsBinary(const NewArchiveMember &NM,660LLVMContext *Context) {661auto BinaryOrErr = createBinary(NM.Buf->getMemBufferRef(), Context);662if (BinaryOrErr)663return std::move(*BinaryOrErr);664return BinaryOrErr.takeError();665}666667Expected<std::unique_ptr<Binary>> getAsBinary(const Archive::Child &C,668LLVMContext *Context) {669return C.getAsBinary(Context);670}671672template <class A> static bool isValidInBitMode(const A &Member) {673if (object::Archive::getDefaultKind() != object::Archive::K_AIXBIG)674return true;675LLVMContext Context;676Expected<std::unique_ptr<Binary>> BinOrErr = getAsBinary(Member, &Context);677// In AIX "ar", if there is a non-object file member, it is never ignored due678// to the bit mode setting.679if (!BinOrErr) {680consumeError(BinOrErr.takeError());681return true;682}683return isValidInBitMode(*BinOrErr.get());684}685686static void warnInvalidObjectForFileMode(Twine Name) {687warn("'" + Name + "' is not valid with the current object file mode");688}689690static void performReadOperation(ArchiveOperation Operation,691object::Archive *OldArchive) {692if (Operation == Extract && OldArchive->isThin())693fail("extracting from a thin archive is not supported");694695bool Filter = !Members.empty();696StringMap<int> MemberCount;697{698Error Err = Error::success();699for (auto &C : OldArchive->children(Err)) {700Expected<StringRef> NameOrErr = C.getName();701failIfError(NameOrErr.takeError());702StringRef Name = NameOrErr.get();703704// Check whether to ignore this object due to its bitness.705if (!isValidInBitMode(C))706continue;707708if (Filter) {709auto I = find_if(Members, [Name](StringRef Path) {710return comparePaths(Name, Path);711});712if (I == Members.end())713continue;714if (CountParam && ++MemberCount[Name] != CountParam)715continue;716Members.erase(I);717}718719switch (Operation) {720default:721llvm_unreachable("Not a read operation");722case Print:723doPrint(Name, C);724break;725case DisplayTable:726doDisplayTable(Name, C);727break;728case Extract:729doExtract(Name, C);730break;731}732}733failIfError(std::move(Err));734}735736if (Members.empty())737return;738for (StringRef Name : Members)739WithColor::error(errs(), ToolName) << "'" << Name << "' was not found\n";740exit(1);741}742743static void addChildMember(std::vector<NewArchiveMember> &Members,744const object::Archive::Child &M,745bool FlattenArchive = false) {746Expected<NewArchiveMember> NMOrErr =747NewArchiveMember::getOldMember(M, Deterministic);748failIfError(NMOrErr.takeError());749// If the child member we're trying to add is thin, use the path relative to750// the archive it's in, so the file resolves correctly.751if (Thin && FlattenArchive) {752StringSaver Saver(Alloc);753Expected<std::string> FileNameOrErr(M.getName());754failIfError(FileNameOrErr.takeError());755if (sys::path::is_absolute(*FileNameOrErr)) {756NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(*FileNameOrErr));757} else {758FileNameOrErr = M.getFullName();759failIfError(FileNameOrErr.takeError());760Expected<std::string> PathOrErr =761computeArchiveRelativePath(ArchiveName, *FileNameOrErr);762NMOrErr->MemberName = Saver.save(763PathOrErr ? *PathOrErr : sys::path::convert_to_slash(*FileNameOrErr));764}765}766if (FlattenArchive &&767identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) {768Expected<std::string> FileNameOrErr = M.getFullName();769failIfError(FileNameOrErr.takeError());770object::Archive &Lib = readLibrary(*FileNameOrErr);771// When creating thin archives, only flatten if the member is also thin.772if (!Thin || Lib.isThin()) {773Error Err = Error::success();774// Only Thin archives are recursively flattened.775for (auto &Child : Lib.children(Err))776addChildMember(Members, Child, /*FlattenArchive=*/Thin);777failIfError(std::move(Err));778return;779}780}781Members.push_back(std::move(*NMOrErr));782}783784static NewArchiveMember getArchiveMember(StringRef FileName) {785Expected<NewArchiveMember> NMOrErr =786NewArchiveMember::getFile(FileName, Deterministic);787failIfError(NMOrErr.takeError(), FileName);788StringSaver Saver(Alloc);789// For regular archives, use the basename of the object path for the member790// name. For thin archives, use the full relative paths so the file resolves791// correctly.792if (!Thin) {793NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName);794} else {795if (sys::path::is_absolute(FileName))796NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(FileName));797else {798Expected<std::string> PathOrErr =799computeArchiveRelativePath(ArchiveName, FileName);800NMOrErr->MemberName = Saver.save(801PathOrErr ? *PathOrErr : sys::path::convert_to_slash(FileName));802}803}804return std::move(*NMOrErr);805}806807static void addMember(std::vector<NewArchiveMember> &Members,808NewArchiveMember &NM) {809Members.push_back(std::move(NM));810}811812static void addMember(std::vector<NewArchiveMember> &Members,813StringRef FileName, bool FlattenArchive = false) {814NewArchiveMember NM = getArchiveMember(FileName);815if (!isValidInBitMode(NM)) {816warnInvalidObjectForFileMode(FileName);817return;818}819820if (FlattenArchive &&821identify_magic(NM.Buf->getBuffer()) == file_magic::archive) {822object::Archive &Lib = readLibrary(FileName);823// When creating thin archives, only flatten if the member is also thin.824if (!Thin || Lib.isThin()) {825Error Err = Error::success();826// Only Thin archives are recursively flattened.827for (auto &Child : Lib.children(Err))828addChildMember(Members, Child, /*FlattenArchive=*/Thin);829failIfError(std::move(Err));830return;831}832}833Members.push_back(std::move(NM));834}835836enum InsertAction {837IA_AddOldMember,838IA_AddNewMember,839IA_Delete,840IA_MoveOldMember,841IA_MoveNewMember842};843844static InsertAction computeInsertAction(ArchiveOperation Operation,845const object::Archive::Child &Member,846StringRef Name,847std::vector<StringRef>::iterator &Pos,848StringMap<int> &MemberCount) {849if (!isValidInBitMode(Member))850return IA_AddOldMember;851852if (Operation == QuickAppend || Members.empty())853return IA_AddOldMember;854855auto MI = find_if(Members, [Name](StringRef Path) {856if (Thin && !sys::path::is_absolute(Path)) {857Expected<std::string> PathOrErr =858computeArchiveRelativePath(ArchiveName, Path);859return comparePaths(Name, PathOrErr ? *PathOrErr : Path);860} else {861return comparePaths(Name, Path);862}863});864865if (MI == Members.end())866return IA_AddOldMember;867868Pos = MI;869870if (Operation == Delete) {871if (CountParam && ++MemberCount[Name] != CountParam)872return IA_AddOldMember;873return IA_Delete;874}875876if (Operation == Move)877return IA_MoveOldMember;878879if (Operation == ReplaceOrInsert) {880if (!OnlyUpdate) {881if (RelPos.empty())882return IA_AddNewMember;883return IA_MoveNewMember;884}885886// We could try to optimize this to a fstat, but it is not a common887// operation.888sys::fs::file_status Status;889failIfError(sys::fs::status(*MI, Status), *MI);890auto ModTimeOrErr = Member.getLastModified();891failIfError(ModTimeOrErr.takeError());892if (Status.getLastModificationTime() < ModTimeOrErr.get()) {893if (RelPos.empty())894return IA_AddOldMember;895return IA_MoveOldMember;896}897898if (RelPos.empty())899return IA_AddNewMember;900return IA_MoveNewMember;901}902llvm_unreachable("No such operation");903}904905// We have to walk this twice and computing it is not trivial, so creating an906// explicit std::vector is actually fairly efficient.907static std::vector<NewArchiveMember>908computeNewArchiveMembers(ArchiveOperation Operation,909object::Archive *OldArchive) {910std::vector<NewArchiveMember> Ret;911std::vector<NewArchiveMember> Moved;912int InsertPos = -1;913if (OldArchive) {914Error Err = Error::success();915StringMap<int> MemberCount;916for (auto &Child : OldArchive->children(Err)) {917int Pos = Ret.size();918Expected<StringRef> NameOrErr = Child.getName();919failIfError(NameOrErr.takeError());920std::string Name = std::string(NameOrErr.get());921if (comparePaths(Name, RelPos) && isValidInBitMode(Child)) {922assert(AddAfter || AddBefore);923if (AddBefore)924InsertPos = Pos;925else926InsertPos = Pos + 1;927}928929std::vector<StringRef>::iterator MemberI = Members.end();930InsertAction Action =931computeInsertAction(Operation, Child, Name, MemberI, MemberCount);932933auto HandleNewMember = [](auto Member, auto &Members, auto &Child) {934NewArchiveMember NM = getArchiveMember(*Member);935if (isValidInBitMode(NM))936addMember(Members, NM);937else {938// If a new member is not a valid object for the bit mode, add939// the old member back.940warnInvalidObjectForFileMode(*Member);941addChildMember(Members, Child, /*FlattenArchive=*/Thin);942}943};944945switch (Action) {946case IA_AddOldMember:947addChildMember(Ret, Child, /*FlattenArchive=*/Thin);948break;949case IA_AddNewMember:950HandleNewMember(MemberI, Ret, Child);951break;952case IA_Delete:953break;954case IA_MoveOldMember:955addChildMember(Moved, Child, /*FlattenArchive=*/Thin);956break;957case IA_MoveNewMember:958HandleNewMember(MemberI, Moved, Child);959break;960}961// When processing elements with the count param, we need to preserve the962// full members list when iterating over all archive members. For963// instance, "llvm-ar dN 2 archive.a member.o" should delete the second964// file named member.o it sees; we are not done with member.o the first965// time we see it in the archive.966if (MemberI != Members.end() && !CountParam)967Members.erase(MemberI);968}969failIfError(std::move(Err));970}971972if (Operation == Delete)973return Ret;974975if (!RelPos.empty() && InsertPos == -1)976fail("insertion point not found");977978if (RelPos.empty())979InsertPos = Ret.size();980981assert(unsigned(InsertPos) <= Ret.size());982int Pos = InsertPos;983for (auto &M : Moved) {984Ret.insert(Ret.begin() + Pos, std::move(M));985++Pos;986}987988if (AddLibrary) {989assert(Operation == QuickAppend);990for (auto &Member : Members)991addMember(Ret, Member, /*FlattenArchive=*/true);992return Ret;993}994995std::vector<NewArchiveMember> NewMembers;996for (auto &Member : Members)997addMember(NewMembers, Member, /*FlattenArchive=*/Thin);998Ret.reserve(Ret.size() + NewMembers.size());999std::move(NewMembers.begin(), NewMembers.end(),1000std::inserter(Ret, std::next(Ret.begin(), InsertPos)));10011002return Ret;1003}10041005static void performWriteOperation(ArchiveOperation Operation,1006object::Archive *OldArchive,1007std::unique_ptr<MemoryBuffer> OldArchiveBuf,1008std::vector<NewArchiveMember> *NewMembersP) {1009if (OldArchive) {1010if (Thin && !OldArchive->isThin())1011fail("cannot convert a regular archive to a thin one");10121013if (OldArchive->isThin())1014Thin = true;1015}10161017std::vector<NewArchiveMember> NewMembers;1018if (!NewMembersP)1019NewMembers = computeNewArchiveMembers(Operation, OldArchive);10201021object::Archive::Kind Kind;1022switch (FormatType) {1023case Default:1024if (Thin)1025Kind = object::Archive::K_GNU;1026else if (OldArchive) {1027Kind = OldArchive->kind();1028std::optional<object::Archive::Kind> AltKind;1029if (Kind == object::Archive::K_BSD)1030AltKind = object::Archive::K_DARWIN;1031else if (Kind == object::Archive::K_GNU && !OldArchive->hasSymbolTable())1032// If there is no symbol table, we can't tell GNU from COFF format1033// from the old archive type.1034AltKind = object::Archive::K_COFF;1035if (AltKind) {1036auto InferredKind = Kind;1037if (NewMembersP && !NewMembersP->empty())1038InferredKind = NewMembersP->front().detectKindFromObject();1039else if (!NewMembers.empty())1040InferredKind = NewMembers.front().detectKindFromObject();1041if (InferredKind == AltKind)1042Kind = *AltKind;1043}1044} else if (NewMembersP)1045Kind = !NewMembersP->empty() ? NewMembersP->front().detectKindFromObject()1046: object::Archive::getDefaultKind();1047else1048Kind = !NewMembers.empty() ? NewMembers.front().detectKindFromObject()1049: object::Archive::getDefaultKind();1050break;1051case GNU:1052Kind = object::Archive::K_GNU;1053break;1054case COFF:1055Kind = object::Archive::K_COFF;1056break;1057case BSD:1058if (Thin)1059fail("only the gnu format has a thin mode");1060Kind = object::Archive::K_BSD;1061break;1062case DARWIN:1063if (Thin)1064fail("only the gnu format has a thin mode");1065Kind = object::Archive::K_DARWIN;1066break;1067case BIGARCHIVE:1068if (Thin)1069fail("only the gnu format has a thin mode");1070Kind = object::Archive::K_AIXBIG;1071break;1072case Unknown:1073llvm_unreachable("");1074}10751076Error E =1077writeArchive(ArchiveName, NewMembersP ? *NewMembersP : NewMembers, Symtab,1078Kind, Deterministic, Thin, std::move(OldArchiveBuf));1079failIfError(std::move(E), ArchiveName);1080}10811082static void createSymbolTable(object::Archive *OldArchive) {1083// When an archive is created or modified, if the s option is given, the1084// resulting archive will have a current symbol table. If the S option1085// is given, it will have no symbol table.1086// In summary, we only need to update the symbol table if we have none.1087// This is actually very common because of broken build systems that think1088// they have to run ranlib.1089if (OldArchive->hasSymbolTable()) {1090if (OldArchive->kind() != object::Archive::K_AIXBIG)1091return;10921093// For archives in the Big Archive format, the bit mode option specifies1094// which symbol table to generate. The presence of a symbol table that does1095// not match the specified bit mode does not prevent creation of the symbol1096// table that has been requested.1097if (OldArchive->kind() == object::Archive::K_AIXBIG) {1098BigArchive *BigArc = dyn_cast<BigArchive>(OldArchive);1099if (BigArc->has32BitGlobalSymtab() &&1100Symtab == SymtabWritingMode::BigArchive32)1101return;11021103if (BigArc->has64BitGlobalSymtab() &&1104Symtab == SymtabWritingMode::BigArchive64)1105return;11061107if (BigArc->has32BitGlobalSymtab() && BigArc->has64BitGlobalSymtab() &&1108Symtab == SymtabWritingMode::NormalSymtab)1109return;11101111Symtab = SymtabWritingMode::NormalSymtab;1112}1113}1114if (OldArchive->isThin())1115Thin = true;1116performWriteOperation(CreateSymTab, OldArchive, nullptr, nullptr);1117}11181119static void performOperation(ArchiveOperation Operation,1120object::Archive *OldArchive,1121std::unique_ptr<MemoryBuffer> OldArchiveBuf,1122std::vector<NewArchiveMember> *NewMembers) {1123switch (Operation) {1124case Print:1125case DisplayTable:1126case Extract:1127performReadOperation(Operation, OldArchive);1128return;11291130case Delete:1131case Move:1132case QuickAppend:1133case ReplaceOrInsert:1134performWriteOperation(Operation, OldArchive, std::move(OldArchiveBuf),1135NewMembers);1136return;1137case CreateSymTab:1138createSymbolTable(OldArchive);1139return;1140}1141llvm_unreachable("Unknown operation.");1142}11431144static int performOperation(ArchiveOperation Operation) {1145// Create or open the archive object.1146ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(1147ArchiveName, /*IsText=*/false, /*RequiresNullTerminator=*/false);1148std::error_code EC = Buf.getError();1149if (EC && EC != errc::no_such_file_or_directory)1150fail("unable to open '" + ArchiveName + "': " + EC.message());11511152if (!EC) {1153Expected<std::unique_ptr<object::Archive>> ArchiveOrError =1154object::Archive::create(Buf.get()->getMemBufferRef());1155if (!ArchiveOrError)1156failIfError(ArchiveOrError.takeError(),1157"unable to load '" + ArchiveName + "'");11581159std::unique_ptr<object::Archive> Archive = std::move(ArchiveOrError.get());1160if (Archive->isThin())1161CompareFullPath = true;1162performOperation(Operation, Archive.get(), std::move(Buf.get()),1163/*NewMembers=*/nullptr);1164return 0;1165}11661167assert(EC == errc::no_such_file_or_directory);11681169if (!shouldCreateArchive(Operation)) {1170failIfError(EC, Twine("unable to load '") + ArchiveName + "'");1171} else {1172if (!Create) {1173// Produce a warning if we should and we're creating the archive1174warn("creating " + ArchiveName);1175}1176}11771178performOperation(Operation, nullptr, nullptr, /*NewMembers=*/nullptr);1179return 0;1180}11811182static void runMRIScript() {1183enum class MRICommand { AddLib, AddMod, Create, CreateThin, Delete, Save, End, Invalid };11841185ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getSTDIN();1186failIfError(Buf.getError());1187const MemoryBuffer &Ref = *Buf.get();1188bool Saved = false;1189std::vector<NewArchiveMember> NewMembers;1190ParsingMRIScript = true;11911192for (line_iterator I(Ref, /*SkipBlanks*/ false), E; I != E; ++I) {1193++MRILineNumber;1194StringRef Line = *I;1195Line = Line.split(';').first;1196Line = Line.split('*').first;1197Line = Line.trim();1198if (Line.empty())1199continue;1200StringRef CommandStr, Rest;1201std::tie(CommandStr, Rest) = Line.split(' ');1202Rest = Rest.trim();1203if (!Rest.empty() && Rest.front() == '"' && Rest.back() == '"')1204Rest = Rest.drop_front().drop_back();1205auto Command = StringSwitch<MRICommand>(CommandStr.lower())1206.Case("addlib", MRICommand::AddLib)1207.Case("addmod", MRICommand::AddMod)1208.Case("create", MRICommand::Create)1209.Case("createthin", MRICommand::CreateThin)1210.Case("delete", MRICommand::Delete)1211.Case("save", MRICommand::Save)1212.Case("end", MRICommand::End)1213.Default(MRICommand::Invalid);12141215switch (Command) {1216case MRICommand::AddLib: {1217if (!Create)1218fail("no output archive has been opened");1219object::Archive &Lib = readLibrary(Rest);1220{1221if (Thin && !Lib.isThin())1222fail("cannot add a regular archive's contents to a thin archive");1223Error Err = Error::success();1224for (auto &Member : Lib.children(Err))1225addChildMember(NewMembers, Member, /*FlattenArchive=*/Thin);1226failIfError(std::move(Err));1227}1228break;1229}1230case MRICommand::AddMod:1231if (!Create)1232fail("no output archive has been opened");1233addMember(NewMembers, Rest);1234break;1235case MRICommand::CreateThin:1236Thin = true;1237[[fallthrough]];1238case MRICommand::Create:1239Create = true;1240if (!ArchiveName.empty())1241fail("editing multiple archives not supported");1242if (Saved)1243fail("file already saved");1244ArchiveName = std::string(Rest);1245if (ArchiveName.empty())1246fail("missing archive name");1247break;1248case MRICommand::Delete: {1249llvm::erase_if(NewMembers, [=](NewArchiveMember &M) {1250return comparePaths(M.MemberName, Rest);1251});1252break;1253}1254case MRICommand::Save:1255Saved = true;1256break;1257case MRICommand::End:1258break;1259case MRICommand::Invalid:1260fail("unknown command: " + CommandStr);1261}1262}12631264ParsingMRIScript = false;12651266// Nothing to do if not saved.1267if (Saved)1268performOperation(ReplaceOrInsert, /*OldArchive=*/nullptr,1269/*OldArchiveBuf=*/nullptr, &NewMembers);1270exit(0);1271}12721273static bool handleGenericOption(StringRef arg) {1274if (arg == "--help" || arg == "-h") {1275printHelpMessage();1276return true;1277}1278if (arg == "--version") {1279cl::PrintVersionMessage();1280return true;1281}1282return false;1283}12841285static BitModeTy getBitMode(const char *RawBitMode) {1286return StringSwitch<BitModeTy>(RawBitMode)1287.Case("32", BitModeTy::Bit32)1288.Case("64", BitModeTy::Bit64)1289.Case("32_64", BitModeTy::Bit32_64)1290.Case("any", BitModeTy::Any)1291.Default(BitModeTy::Unknown);1292}12931294static const char *matchFlagWithArg(StringRef Expected,1295ArrayRef<const char *>::iterator &ArgIt,1296ArrayRef<const char *> Args) {1297StringRef Arg = *ArgIt;12981299Arg.consume_front("--");13001301size_t len = Expected.size();1302if (Arg == Expected) {1303if (++ArgIt == Args.end())1304fail(std::string(Expected) + " requires an argument");13051306return *ArgIt;1307}1308if (Arg.starts_with(Expected) && Arg.size() > len && Arg[len] == '=')1309return Arg.data() + len + 1;13101311return nullptr;1312}13131314static cl::TokenizerCallback getRspQuoting(ArrayRef<const char *> ArgsArr) {1315cl::TokenizerCallback Ret =1316Triple(sys::getProcessTriple()).getOS() == Triple::Win321317? cl::TokenizeWindowsCommandLine1318: cl::TokenizeGNUCommandLine;13191320for (ArrayRef<const char *>::iterator ArgIt = ArgsArr.begin();1321ArgIt != ArgsArr.end(); ++ArgIt) {1322if (const char *Match = matchFlagWithArg("rsp-quoting", ArgIt, ArgsArr)) {1323StringRef MatchRef = Match;1324if (MatchRef == "posix")1325Ret = cl::TokenizeGNUCommandLine;1326else if (MatchRef == "windows")1327Ret = cl::TokenizeWindowsCommandLine;1328else1329fail(std::string("Invalid response file quoting style ") + Match);1330}1331}13321333return Ret;1334}13351336static int ar_main(int argc, char **argv) {1337SmallVector<const char *, 0> Argv(argv + 1, argv + argc);1338StringSaver Saver(Alloc);13391340cl::ExpandResponseFiles(Saver, getRspQuoting(ArrayRef(argv, argc)), Argv);13411342// Get BitMode from enviorment variable "OBJECT_MODE" for AIX OS, if1343// specified.1344if (object::Archive::getDefaultKind() == object::Archive::K_AIXBIG) {1345BitMode = getBitMode(getenv("OBJECT_MODE"));1346if (BitMode == BitModeTy::Unknown)1347BitMode = BitModeTy::Bit32;1348}13491350for (ArrayRef<const char *>::iterator ArgIt = Argv.begin();1351ArgIt != Argv.end(); ++ArgIt) {1352const char *Match = nullptr;13531354if (handleGenericOption(*ArgIt))1355return 0;1356if (strcmp(*ArgIt, "--") == 0) {1357++ArgIt;1358for (; ArgIt != Argv.end(); ++ArgIt)1359PositionalArgs.push_back(*ArgIt);1360break;1361}13621363if (*ArgIt[0] != '-') {1364if (Options.empty())1365Options += *ArgIt;1366else1367PositionalArgs.push_back(*ArgIt);1368continue;1369}13701371if (strcmp(*ArgIt, "-M") == 0) {1372MRI = true;1373continue;1374}13751376if (strcmp(*ArgIt, "--thin") == 0) {1377Thin = true;1378continue;1379}13801381Match = matchFlagWithArg("format", ArgIt, Argv);1382if (Match) {1383FormatType = StringSwitch<Format>(Match)1384.Case("default", Default)1385.Case("gnu", GNU)1386.Case("darwin", DARWIN)1387.Case("bsd", BSD)1388.Case("bigarchive", BIGARCHIVE)1389.Case("coff", COFF)1390.Default(Unknown);1391if (FormatType == Unknown)1392fail(std::string("Invalid format ") + Match);1393continue;1394}13951396if ((Match = matchFlagWithArg("output", ArgIt, Argv))) {1397OutputDir = Match;1398continue;1399}14001401if (matchFlagWithArg("plugin", ArgIt, Argv) ||1402matchFlagWithArg("rsp-quoting", ArgIt, Argv))1403continue;14041405if (strncmp(*ArgIt, "-X", 2) == 0) {1406if (object::Archive::getDefaultKind() == object::Archive::K_AIXBIG) {1407Match = *(*ArgIt + 2) != '\0' ? *ArgIt + 2 : *(++ArgIt);1408BitMode = getBitMode(Match);1409if (BitMode == BitModeTy::Unknown)1410fail(Twine("invalid bit mode: ") + Match);1411continue;1412} else {1413fail(Twine(*ArgIt) + " option not supported on non AIX OS");1414}1415}14161417Options += *ArgIt + 1;1418}14191420return performOperation(parseCommandLine());1421}14221423static int ranlib_main(int argc, char **argv) {1424std::vector<StringRef> Archives;1425bool HasAIXXOption = false;14261427for (int i = 1; i < argc; ++i) {1428StringRef arg(argv[i]);1429if (handleGenericOption(arg)) {1430return 0;1431} else if (arg.consume_front("-")) {1432// Handle the -D/-U flag1433while (!arg.empty()) {1434if (arg.front() == 'D') {1435Deterministic = true;1436} else if (arg.front() == 'U') {1437Deterministic = false;1438} else if (arg.front() == 'h') {1439printHelpMessage();1440return 0;1441} else if (arg.front() == 'V') {1442cl::PrintVersionMessage();1443return 0;1444} else if (arg.front() == 'X') {1445if (object::Archive::getDefaultKind() == object::Archive::K_AIXBIG) {1446HasAIXXOption = true;1447arg.consume_front("X");1448const char *Xarg = arg.data();1449if (Xarg[0] == '\0') {1450if (argv[i + 1][0] != '-')1451BitMode = getBitMode(argv[++i]);1452else1453BitMode = BitModeTy::Unknown;1454} else1455BitMode = getBitMode(arg.data());14561457if (BitMode == BitModeTy::Unknown)1458fail("the specified object mode is not valid. Specify -X32, "1459"-X64, -X32_64, or -Xany");1460} else {1461fail(Twine("-") + Twine(arg) +1462" option not supported on non AIX OS");1463}1464break;1465} else {1466// TODO: GNU ranlib also supports a -t flag1467fail("Invalid option: '-" + arg + "'");1468}1469arg = arg.drop_front(1);1470}1471} else {1472Archives.push_back(arg);1473}1474}14751476if (object::Archive::getDefaultKind() == object::Archive::K_AIXBIG) {1477// If not specify -X option, get BitMode from enviorment variable1478// "OBJECT_MODE" for AIX OS if specify.1479if (!HasAIXXOption) {1480if (char *EnvObjectMode = getenv("OBJECT_MODE")) {1481BitMode = getBitMode(EnvObjectMode);1482if (BitMode == BitModeTy::Unknown)1483fail("the OBJECT_MODE environment variable has an invalid value. "1484"OBJECT_MODE must be 32, 64, 32_64, or any");1485}1486}14871488switch (BitMode) {1489case BitModeTy::Bit32:1490Symtab = SymtabWritingMode::BigArchive32;1491break;1492case BitModeTy::Bit64:1493Symtab = SymtabWritingMode::BigArchive64;1494break;1495default:1496Symtab = SymtabWritingMode::NormalSymtab;1497break;1498}1499}15001501for (StringRef Archive : Archives) {1502ArchiveName = Archive.str();1503performOperation(CreateSymTab);1504}1505if (Archives.empty())1506badUsage("an archive name must be specified");1507return 0;1508}15091510int llvm_ar_main(int argc, char **argv, const llvm::ToolContext &) {1511ToolName = argv[0];15121513llvm::InitializeAllTargetInfos();1514llvm::InitializeAllTargetMCs();1515llvm::InitializeAllAsmParsers();15161517Stem = sys::path::stem(ToolName);1518auto Is = [](StringRef Tool) {1519// We need to recognize the following filenames.1520//1521// Lib.exe -> lib (see D44808, MSBuild runs Lib.exe)1522// dlltool.exe -> dlltool1523// arm-pokymllib32-linux-gnueabi-llvm-ar-10 -> ar1524auto I = Stem.rfind_insensitive(Tool);1525return I != StringRef::npos &&1526(I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));1527};15281529if (Is("dlltool"))1530return dlltoolDriverMain(ArrayRef(argv, argc));1531if (Is("ranlib"))1532return ranlib_main(argc, argv);1533if (Is("lib"))1534return libDriverMain(ArrayRef(argv, argc));1535if (Is("ar"))1536return ar_main(argc, argv);15371538fail("not ranlib, ar, lib or dlltool");1539}154015411542