diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-07-28 11:53:25 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-07-28 11:53:25 -0700 |
| commit | adb4a953021aadcfd539589eeccc2fe4e6ab827a (patch) | |
| tree | 0c3fd514812e38fdaec5927825861858e8bfd8f4 /src/zig_llvm-ar.cpp | |
| parent | e863292fe2f280945d914e7e98fbc704b68f1004 (diff) | |
| download | zig-adb4a953021aadcfd539589eeccc2fe4e6ab827a.tar.gz zig-adb4a953021aadcfd539589eeccc2fe4e6ab827a.zip | |
update to LLVM 15
release/15.x 37007475ca1b345b4c5d340e228bcd7a62732d81
Diffstat (limited to 'src/zig_llvm-ar.cpp')
| -rw-r--r-- | src/zig_llvm-ar.cpp | 327 |
1 files changed, 249 insertions, 78 deletions
diff --git a/src/zig_llvm-ar.cpp b/src/zig_llvm-ar.cpp index c959f339a2..6e5f9d36d8 100644 --- a/src/zig_llvm-ar.cpp +++ b/src/zig_llvm-ar.cpp @@ -18,10 +18,15 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/COFFImportFile.h" +#include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/IRObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" +#include "llvm/Object/TapiFile.h" +#include "llvm/Object/Wasm.h" +#include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ConvertUTF.h" @@ -54,6 +59,7 @@ #endif using namespace llvm; +using namespace llvm::object; // The name this program was invoked as. static StringRef ToolName; @@ -61,37 +67,36 @@ static StringRef ToolName; // The basename of this program. static StringRef Stem; -const char RanlibHelp[] = R"(OVERVIEW: LLVM Ranlib (llvm-ranlib) - - This program generates an index to speed access to archives - -USAGE: llvm-ranlib <archive-file> - -OPTIONS: - -h --help - Display available options - -v --version - Display the version of this program - -D - Use zero for timestamps and uids/gids (default) - -U - Use actual timestamps and uids/gids -)"; - -const char ArHelp[] = R"(OVERVIEW: LLVM Archiver - -USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] [count] <archive> [files] - llvm-ar -M [<mri-script] +static void printRanLibHelp(StringRef ToolName) { + outs() << "OVERVIEW: LLVM Ranlib\n\n" + << "This program generates an index to speed access to archives\n\n" + << "USAGE: " + ToolName + " <archive-file>\n\n" + << "OPTIONS:\n" + << " -h --help - Display available options\n" + << " -v --version - Display the version of this program\n" + << " -D - Use zero for timestamps and uids/gids " + "(default)\n" + << " -U - Use actual timestamps and uids/gids\n"; +} -OPTIONS: +static void printArHelp(StringRef ToolName) { + const char ArOptions[] = + R"(OPTIONS: --format - archive format to create =default - default =gnu - gnu =darwin - darwin =bsd - bsd + =bigarchive - big archive (AIX OS) --plugin=<string> - ignored for compatibility -h --help - display this help and exit + --output - the directory to extract archive members to --rsp-quoting - quoting style for response files =posix - posix =windows - windows --thin - create a thin archive --version - print the version and exit + -X{32|64|32_64|any} - object mode (only for AIX OS) @<file> - read options from <file> OPERATIONS: @@ -126,11 +131,20 @@ MODIFIERS: [V] - display the version and exit )"; + outs() << "OVERVIEW: LLVM Archiver\n\n" + << "USAGE: " + ToolName + + " [options] [-]<operation>[modifiers] [relpos] " + "[count] <archive> [files]\n" + << " " + ToolName + " -M [<mri-script]\n\n"; + + outs() << ArOptions; +} + static void printHelpMessage() { if (Stem.contains_insensitive("ranlib")) - outs() << RanlibHelp; + printRanLibHelp(Stem); else if (Stem.contains_insensitive("ar")) - outs() << ArHelp; + printArHelp(Stem); } static unsigned MRILineNumber; @@ -176,12 +190,16 @@ static void failIfError(Error E, Twine Context = "") { }); } +static void warn(Twine Message) { + WithColor::warning(errs(), ToolName) << Message << "\n"; +} + static SmallVector<const char *, 256> PositionalArgs; static bool MRI; namespace { -enum Format { Default, GNU, BSD, DARWIN, Unknown }; +enum Format { Default, GNU, BSD, DARWIN, BIGARCHIVE, Unknown }; } static Format FormatType = Default; @@ -201,6 +219,10 @@ enum ArchiveOperation { CreateSymTab ///< Create a symbol table in an existing archive }; +enum class BitModeTy { Bit32, Bit64, Bit32_64, Any, Unknown }; + +static BitModeTy BitMode = BitModeTy::Bit32; + // Modifiers to follow operation to vary behavior static bool AddAfter = false; ///< 'a' modifier static bool AddBefore = false; ///< 'b' modifier @@ -230,6 +252,9 @@ static int CountParam = 0; // command line. static std::string ArchiveName; +// Output directory specified by --output. +static std::string OutputDir; + static std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; static std::vector<std::unique_ptr<object::Archive>> Archives; @@ -447,6 +472,19 @@ static ArchiveOperation parseCommandLine() { if (AddLibrary && Operation != QuickAppend) badUsage("the 'L' modifier is only applicable to the 'q' operation"); + if (!OutputDir.empty()) { + if (Operation != Extract) + badUsage("--output is only applicable to the 'x' operation"); + bool IsDir = false; + // If OutputDir is not a directory, create_directories may still succeed if + // all components of the path prefix are directories. Test is_directory as + // well. + if (!sys::fs::create_directories(OutputDir)) + sys::fs::is_directory(OutputDir, IsDir); + if (!IsDir) + fail("'" + OutputDir + "' is not a directory"); + } + // Return the parsed operation to the caller return Operation; } @@ -547,7 +585,15 @@ static void doExtract(StringRef Name, const object::Archive::Child &C) { failIfError(ModeOrErr.takeError()); sys::fs::perms Mode = ModeOrErr.get(); - llvm::StringRef outputFilePath = sys::path::filename(Name); + StringRef outputFilePath; + SmallString<128> path; + if (OutputDir.empty()) { + outputFilePath = sys::path::filename(Name); + } else { + sys::path::append(path, OutputDir, sys::path::filename(Name)); + outputFilePath = path.str(); + } + if (Verbose) outs() << "x - " << outputFilePath << '\n'; @@ -600,6 +646,71 @@ static bool shouldCreateArchive(ArchiveOperation Op) { llvm_unreachable("Missing entry in covered switch."); } +static bool is64BitSymbolicFile(SymbolicFile &Obj) { + if (auto *IRObj = dyn_cast<IRObjectFile>(&Obj)) + return Triple(IRObj->getTargetTriple()).isArch64Bit(); + if (isa<COFFObjectFile>(Obj) || isa<COFFImportFile>(Obj)) + return false; + if (XCOFFObjectFile *XCOFFObj = dyn_cast<XCOFFObjectFile>(&Obj)) + return XCOFFObj->is64Bit(); + if (isa<WasmObjectFile>(Obj)) + return false; + if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj)) + return Tapi->is64Bit(); + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) + return MachO->is64Bit(); + if (ELFObjectFileBase *ElfO = dyn_cast<ELFObjectFileBase>(&Obj)) + return ElfO->getBytesInAddress() == 8; + + fail("unsupported file format"); +} + +static bool isValidInBitMode(Binary &Bin) { + if (BitMode == BitModeTy::Bit32_64 || BitMode == BitModeTy::Any) + return true; + + if (SymbolicFile *SymFile = dyn_cast<SymbolicFile>(&Bin)) { + bool Is64Bit = is64BitSymbolicFile(*SymFile); + if ((Is64Bit && (BitMode == BitModeTy::Bit32)) || + (!Is64Bit && (BitMode == BitModeTy::Bit64))) + return false; + } + // In AIX "ar", non-object files are always considered to have a valid bit + // mode. + return true; +} + +Expected<std::unique_ptr<Binary>> getAsBinary(const NewArchiveMember &NM, + LLVMContext *Context) { + auto BinaryOrErr = createBinary(NM.Buf->getMemBufferRef(), Context); + if (BinaryOrErr) + return std::move(*BinaryOrErr); + return BinaryOrErr.takeError(); +} + +Expected<std::unique_ptr<Binary>> getAsBinary(const Archive::Child &C, + LLVMContext *Context) { + return C.getAsBinary(Context); +} + +template <class A> static bool isValidInBitMode(const A &Member) { + if (object::Archive::getDefaultKindForHost() != object::Archive::K_AIXBIG) + return true; + LLVMContext Context; + Expected<std::unique_ptr<Binary>> BinOrErr = getAsBinary(Member, &Context); + // In AIX "ar", if there is a non-object file member, it is never ignored due + // to the bit mode setting. + if (!BinOrErr) { + consumeError(BinOrErr.takeError()); + return true; + } + return isValidInBitMode(*BinOrErr.get()); +} + +static void warnInvalidObjectForFileMode(Twine Name) { + warn("'" + Name + "' is not valid with the current object file mode"); +} + static void performReadOperation(ArchiveOperation Operation, object::Archive *OldArchive) { if (Operation == Extract && OldArchive->isThin()) @@ -614,6 +725,10 @@ static void performReadOperation(ArchiveOperation Operation, failIfError(NameOrErr.takeError()); StringRef Name = NameOrErr.get(); + // Check whether to ignore this object due to its bitness. + if (!isValidInBitMode(C)) + continue; + if (Filter) { auto I = find_if(Members, [Name](StringRef Path) { return comparePaths(Name, Path); @@ -652,8 +767,6 @@ static void performReadOperation(ArchiveOperation Operation, static void addChildMember(std::vector<NewArchiveMember> &Members, const object::Archive::Child &M, bool FlattenArchive = false) { - if (Thin && !M.getParent()->isThin()) - fail("cannot convert a regular archive to a thin one"); Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getOldMember(M, Deterministic); failIfError(NMOrErr.takeError()); @@ -692,8 +805,7 @@ static void addChildMember(std::vector<NewArchiveMember> &Members, Members.push_back(std::move(*NMOrErr)); } -static void addMember(std::vector<NewArchiveMember> &Members, - StringRef FileName, bool FlattenArchive = false) { +static NewArchiveMember getArchiveMember(StringRef FileName) { Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getFile(FileName, Deterministic); failIfError(NMOrErr.takeError(), FileName); @@ -713,9 +825,24 @@ static void addMember(std::vector<NewArchiveMember> &Members, PathOrErr ? *PathOrErr : sys::path::convert_to_slash(FileName)); } } + return std::move(*NMOrErr); +} + +static void addMember(std::vector<NewArchiveMember> &Members, + NewArchiveMember &NM) { + Members.push_back(std::move(NM)); +} + +static void addMember(std::vector<NewArchiveMember> &Members, + StringRef FileName, bool FlattenArchive = false) { + NewArchiveMember NM = getArchiveMember(FileName); + if (!isValidInBitMode(NM)) { + warnInvalidObjectForFileMode(FileName); + return; + } if (FlattenArchive && - identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { + identify_magic(NM.Buf->getBuffer()) == file_magic::archive) { object::Archive &Lib = readLibrary(FileName); // When creating thin archives, only flatten if the member is also thin. if (!Thin || Lib.isThin()) { @@ -727,7 +854,7 @@ static void addMember(std::vector<NewArchiveMember> &Members, return; } } - Members.push_back(std::move(*NMOrErr)); + Members.push_back(std::move(NM)); } enum InsertAction { @@ -743,6 +870,9 @@ static InsertAction computeInsertAction(ArchiveOperation Operation, StringRef Name, std::vector<StringRef>::iterator &Pos, StringMap<int> &MemberCount) { + if (!isValidInBitMode(Member)) + return IA_AddOldMember; + if (Operation == QuickAppend || Members.empty()) return IA_AddOldMember; auto MI = find_if( @@ -804,7 +934,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, Expected<StringRef> NameOrErr = Child.getName(); failIfError(NameOrErr.takeError()); std::string Name = std::string(NameOrErr.get()); - if (comparePaths(Name, RelPos)) { + if (comparePaths(Name, RelPos) && isValidInBitMode(Child)) { assert(AddAfter || AddBefore); if (AddBefore) InsertPos = Pos; @@ -815,12 +945,25 @@ computeNewArchiveMembers(ArchiveOperation Operation, std::vector<StringRef>::iterator MemberI = Members.end(); InsertAction Action = computeInsertAction(Operation, Child, Name, MemberI, MemberCount); + + auto HandleNewMember = [](auto Member, auto &Members, auto &Child) { + NewArchiveMember NM = getArchiveMember(*Member); + if (isValidInBitMode(NM)) + addMember(Members, NM); + else { + // If a new member is not a valid object for the bit mode, add + // the old member back. + warnInvalidObjectForFileMode(*Member); + addChildMember(Members, Child, /*FlattenArchive=*/Thin); + } + }; + switch (Action) { case IA_AddOldMember: addChildMember(Ret, Child, /*FlattenArchive=*/Thin); break; case IA_AddNewMember: - addMember(Ret, *MemberI); + HandleNewMember(MemberI, Ret, Child); break; case IA_Delete: break; @@ -828,7 +971,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, addChildMember(Moved, Child, /*FlattenArchive=*/Thin); break; case IA_MoveNewMember: - addMember(Moved, *MemberI); + HandleNewMember(MemberI, Moved, Child); break; } // When processing elements with the count param, we need to preserve the @@ -875,48 +1018,18 @@ computeNewArchiveMembers(ArchiveOperation Operation, return Ret; } -static object::Archive::Kind getDefaultForHost() { - return Triple(sys::getProcessTriple()).isOSDarwin() - ? object::Archive::K_DARWIN - : object::Archive::K_GNU; -} - -static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) { - auto MemBufferRef = Member.Buf->getMemBufferRef(); - Expected<std::unique_ptr<object::ObjectFile>> OptionalObject = - object::ObjectFile::createObjectFile(MemBufferRef); - - if (OptionalObject) - return isa<object::MachOObjectFile>(**OptionalObject) - ? object::Archive::K_DARWIN - : object::Archive::K_GNU; - - // squelch the error in case we had a non-object file - consumeError(OptionalObject.takeError()); - - // If we're adding a bitcode file to the archive, detect the Archive kind - // based on the target triple. - LLVMContext Context; - if (identify_magic(MemBufferRef.getBuffer()) == file_magic::bitcode) { - if (auto ObjOrErr = object::SymbolicFile::createSymbolicFile( - MemBufferRef, file_magic::bitcode, &Context)) { - auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr); - return Triple(IRObject.getTargetTriple()).isOSDarwin() - ? object::Archive::K_DARWIN - : object::Archive::K_GNU; - } else { - // Squelch the error in case this was not a SymbolicFile. - consumeError(ObjOrErr.takeError()); - } - } - - return getDefaultForHost(); -} - static void performWriteOperation(ArchiveOperation Operation, object::Archive *OldArchive, std::unique_ptr<MemoryBuffer> OldArchiveBuf, std::vector<NewArchiveMember> *NewMembersP) { + if (OldArchive) { + if (Thin && !OldArchive->isThin()) + fail("cannot convert a regular archive to a thin one"); + + if (OldArchive->isThin()) + Thin = true; + } + std::vector<NewArchiveMember> NewMembers; if (!NewMembersP) NewMembers = computeNewArchiveMembers(Operation, OldArchive); @@ -926,14 +1039,23 @@ static void performWriteOperation(ArchiveOperation Operation, case Default: if (Thin) Kind = object::Archive::K_GNU; - else if (OldArchive) + else if (OldArchive) { Kind = OldArchive->kind(); - else if (NewMembersP) - Kind = !NewMembersP->empty() ? getKindFromMember(NewMembersP->front()) - : getDefaultForHost(); + if (Kind == object::Archive::K_BSD) { + auto InferredKind = object::Archive::K_BSD; + if (NewMembersP && !NewMembersP->empty()) + InferredKind = NewMembersP->front().detectKindFromObject(); + else if (!NewMembers.empty()) + InferredKind = NewMembers.front().detectKindFromObject(); + if (InferredKind == object::Archive::K_DARWIN) + Kind = object::Archive::K_DARWIN; + } + } else if (NewMembersP) + Kind = !NewMembersP->empty() ? NewMembersP->front().detectKindFromObject() + : object::Archive::getDefaultKindForHost(); else - Kind = !NewMembers.empty() ? getKindFromMember(NewMembers.front()) - : getDefaultForHost(); + Kind = !NewMembers.empty() ? NewMembers.front().detectKindFromObject() + : object::Archive::getDefaultKindForHost(); break; case GNU: Kind = object::Archive::K_GNU; @@ -948,6 +1070,11 @@ static void performWriteOperation(ArchiveOperation Operation, fail("only the gnu format has a thin mode"); Kind = object::Archive::K_DARWIN; break; + case BIGARCHIVE: + if (Thin) + fail("only the gnu format has a thin mode"); + Kind = object::Archive::K_AIXBIG; + break; case Unknown: llvm_unreachable(""); } @@ -1029,8 +1156,7 @@ static int performOperation(ArchiveOperation Operation, } else { if (!Create) { // Produce a warning if we should and we're creating the archive - WithColor::warning(errs(), ToolName) - << "creating " << ArchiveName << "\n"; + warn("creating " + ArchiveName); } } @@ -1073,8 +1199,12 @@ static void runMRIScript() { switch (Command) { case MRICommand::AddLib: { + if (!Create) + fail("no output archive has been opened"); object::Archive &Lib = readLibrary(Rest); { + if (Thin && !Lib.isThin()) + fail("cannot add a regular archive's contents to a thin archive"); Error Err = Error::success(); for (auto &Member : Lib.children(Err)) addChildMember(NewMembers, Member, /*FlattenArchive=*/Thin); @@ -1083,6 +1213,8 @@ static void runMRIScript() { break; } case MRICommand::AddMod: + if (!Create) + fail("no output archive has been opened"); addMember(NewMembers, Rest); break; case MRICommand::CreateThin: @@ -1095,6 +1227,8 @@ static void runMRIScript() { if (Saved) fail("file already saved"); ArchiveName = std::string(Rest); + if (ArchiveName.empty()) + fail("missing archive name"); break; case MRICommand::Delete: { llvm::erase_if(NewMembers, [=](NewArchiveMember &M) { @@ -1116,7 +1250,8 @@ static void runMRIScript() { // Nothing to do if not saved. if (Saved) - performOperation(ReplaceOrInsert, &NewMembers); + performOperation(ReplaceOrInsert, /*OldArchive=*/nullptr, + /*OldArchiveBuf=*/nullptr, &NewMembers); exit(0); } @@ -1132,6 +1267,15 @@ static bool handleGenericOption(StringRef arg) { return false; } +static BitModeTy getBitMode(const char *RawBitMode) { + return StringSwitch<BitModeTy>(RawBitMode) + .Case("32", BitModeTy::Bit32) + .Case("64", BitModeTy::Bit64) + .Case("32_64", BitModeTy::Bit32_64) + .Case("any", BitModeTy::Any) + .Default(BitModeTy::Unknown); +} + static const char *matchFlagWithArg(StringRef Expected, ArrayRef<const char *>::iterator &ArgIt, ArrayRef<const char *> Args) { @@ -1181,6 +1325,14 @@ static int ar_main(int argc, char **argv) { cl::ExpandResponseFiles(Saver, getRspQuoting(makeArrayRef(argv, argc)), Argv); + // Get BitMode from enviorment variable "OBJECT_MODE" for AIX OS, if + // specified. + if (object::Archive::getDefaultKindForHost() == object::Archive::K_AIXBIG) { + BitMode = getBitMode(getenv("OBJECT_MODE")); + if (BitMode == BitModeTy::Unknown) + BitMode = BitModeTy::Bit32; + } + for (ArrayRef<const char *>::iterator ArgIt = Argv.begin(); ArgIt != Argv.end(); ++ArgIt) { const char *Match = nullptr; @@ -1219,16 +1371,35 @@ static int ar_main(int argc, char **argv) { .Case("gnu", GNU) .Case("darwin", DARWIN) .Case("bsd", BSD) + .Case("bigarchive", BIGARCHIVE) .Default(Unknown); if (FormatType == Unknown) fail(std::string("Invalid format ") + Match); continue; } + if ((Match = matchFlagWithArg("output", ArgIt, Argv))) { + OutputDir = Match; + continue; + } + if (matchFlagWithArg("plugin", ArgIt, Argv) || matchFlagWithArg("rsp-quoting", ArgIt, Argv)) continue; + if (strncmp(*ArgIt, "-X", 2) == 0) { + if (object::Archive::getDefaultKindForHost() == + object::Archive::K_AIXBIG) { + Match = *(*ArgIt + 2) != '\0' ? *ArgIt + 2 : *(++ArgIt); + BitMode = getBitMode(Match); + if (BitMode == BitModeTy::Unknown) + fail(Twine("invalid bit mode: ") + Match); + continue; + } else { + fail(Twine(*ArgIt) + " option not supported on non AIX OS"); + } + } + Options += *ArgIt + 1; } |
