From adb4a953021aadcfd539589eeccc2fe4e6ab827a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 28 Jul 2022 11:53:25 -0700 Subject: [PATCH] update to LLVM 15 release/15.x 37007475ca1b345b4c5d340e228bcd7a62732d81 --- CMakeLists.txt | 6 +- cmake/Findclang.cmake | 6 +- cmake/Findlld.cmake | 32 ++-- cmake/Findllvm.cmake | 12 +- src/zig_clang_cc1_main.cpp | 14 +- src/zig_clang_cc1as_main.cpp | 28 ++- src/zig_clang_driver.cpp | 66 ++++--- src/zig_llvm-ar.cpp | 327 ++++++++++++++++++++++++++--------- 8 files changed, 354 insertions(+), 137 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ffbf12dbc0..86cbdd6dad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,9 +108,9 @@ else() set(ZIG_USE_LLVM_CONFIG OFF CACHE BOOL "use llvm-config to find LLVM libraries") endif() -find_package(llvm 14) -find_package(clang 14) -find_package(lld 14) +find_package(llvm 15) +find_package(clang 15) +find_package(lld 15) if(ZIG_STATIC_ZLIB) list(REMOVE_ITEM LLVM_LIBRARIES "-lz") diff --git a/cmake/Findclang.cmake b/cmake/Findclang.cmake index 8fd1a202e1..c18a82f27a 100644 --- a/cmake/Findclang.cmake +++ b/cmake/Findclang.cmake @@ -17,9 +17,9 @@ find_path(CLANG_INCLUDE_DIRS NAMES clang/Frontend/ASTUnit.h if(${LLVM_LINK_MODE} STREQUAL "shared") find_library(CLANG_LIBRARIES NAMES - libclang-cpp.so.14 - clang-cpp-14.0 - clang-cpp140 + libclang-cpp.so.15 + clang-cpp-15.0 + clang-cpp150 clang-cpp NAMES_PER_DIR HINTS "${LLVM_LIBDIRS}" diff --git a/cmake/Findlld.cmake b/cmake/Findlld.cmake index 17e27e10f0..e612c3d9d0 100644 --- a/cmake/Findlld.cmake +++ b/cmake/Findlld.cmake @@ -9,21 +9,21 @@ find_path(LLD_INCLUDE_DIRS NAMES lld/Common/Driver.h HINTS ${LLVM_INCLUDE_DIRS} PATHS - /usr/lib/llvm-14/include - /usr/local/llvm140/include - /usr/local/llvm14/include - /usr/local/opt/llvm@14/include - /opt/homebrew/opt/llvm@14/include + /usr/lib/llvm-15/include + /usr/local/llvm150/include + /usr/local/llvm15/include + /usr/local/opt/llvm@15/include + /opt/homebrew/opt/llvm@15/include /mingw64/include) -find_library(LLD_LIBRARY NAMES lld-14.0 lld140 lld NAMES_PER_DIR +find_library(LLD_LIBRARY NAMES lld-15.0 lld150 lld NAMES_PER_DIR HINTS ${LLVM_LIBDIRS} PATHS - /usr/lib/llvm-14/lib - /usr/local/llvm140/lib - /usr/local/llvm14/lib - /usr/local/opt/llvm@14/lib - /opt/homebrew/opt/llvm@14/lib + /usr/lib/llvm-15/lib + /usr/local/llvm150/lib + /usr/local/llvm15/lib + /usr/local/opt/llvm@15/lib + /opt/homebrew/opt/llvm@15/lib ) if(EXISTS ${LLD_LIBRARY}) set(LLD_LIBRARIES ${LLD_LIBRARY}) @@ -34,11 +34,11 @@ else() HINTS ${LLVM_LIBDIRS} PATHS ${LLD_LIBDIRS} - /usr/lib/llvm-14/lib - /usr/local/llvm140/lib - /usr/local/llvm14/lib - /usr/local/opt/llvm@14/lib - /opt/homebrew/opt/llvm@14/lib + /usr/lib/llvm-15/lib + /usr/local/llvm150/lib + /usr/local/llvm15/lib + /usr/local/opt/llvm@15/lib + /opt/homebrew/opt/llvm@15/lib /mingw64/lib /c/msys64/mingw64/lib c:/msys64/mingw64/lib) diff --git a/cmake/Findllvm.cmake b/cmake/Findllvm.cmake index d62a154b84..d6535927d1 100644 --- a/cmake/Findllvm.cmake +++ b/cmake/Findllvm.cmake @@ -13,12 +13,12 @@ if(ZIG_USE_LLVM_CONFIG) while(1) unset(LLVM_CONFIG_EXE CACHE) find_program(LLVM_CONFIG_EXE - NAMES llvm-config-14 llvm-config-14.0 llvm-config140 llvm-config14 llvm-config NAMES_PER_DIR + NAMES llvm-config-15 llvm-config-15.0 llvm-config150 llvm-config15 llvm-config NAMES_PER_DIR PATHS "/mingw64/bin" "/c/msys64/mingw64/bin" "c:/msys64/mingw64/bin" - "C:/Libraries/llvm-14.0.0/bin") + "C:/Libraries/llvm-15.0.0/bin") if ("${LLVM_CONFIG_EXE}" STREQUAL "LLVM_CONFIG_EXE-NOTFOUND") if (DEFINED LLVM_CONFIG_ERROR_MESSAGE) @@ -35,9 +35,9 @@ if(ZIG_USE_LLVM_CONFIG) OUTPUT_STRIP_TRAILING_WHITESPACE) get_filename_component(LLVM_CONFIG_DIR "${LLVM_CONFIG_EXE}" DIRECTORY) - if("${LLVM_CONFIG_VERSION}" VERSION_LESS 14 OR "${LLVM_CONFIG_VERSION}" VERSION_EQUAL 15 OR "${LLVM_CONFIG_VERSION}" VERSION_GREATER 15) + if("${LLVM_CONFIG_VERSION}" VERSION_LESS 15 OR "${LLVM_CONFIG_VERSION}" VERSION_EQUAL 16 OR "${LLVM_CONFIG_VERSION}" VERSION_GREATER 16) # Save the error message, in case this is the last llvm-config we find - set(LLVM_CONFIG_ERROR_MESSAGE "expected LLVM 14.x but found ${LLVM_CONFIG_VERSION} using ${LLVM_CONFIG_EXE}") + set(LLVM_CONFIG_ERROR_MESSAGE "expected LLVM 15.x but found ${LLVM_CONFIG_VERSION} using ${LLVM_CONFIG_EXE}") # Ignore this directory and try the search again list(APPEND CMAKE_IGNORE_PATH "${LLVM_CONFIG_DIR}") @@ -61,9 +61,9 @@ if(ZIG_USE_LLVM_CONFIG) if (LLVM_CONFIG_ERROR) # Save the error message, in case this is the last llvm-config we find if (ZIG_SHARED_LLVM) - set(LLVM_CONFIG_ERROR_MESSAGE "LLVM 14.x found at ${LLVM_CONFIG_EXE} does not support linking as a shared library") + set(LLVM_CONFIG_ERROR_MESSAGE "LLVM 15.x found at ${LLVM_CONFIG_EXE} does not support linking as a shared library") else() - set(LLVM_CONFIG_ERROR_MESSAGE "LLVM 14.x found at ${LLVM_CONFIG_EXE} does not support linking as a static library") + set(LLVM_CONFIG_ERROR_MESSAGE "LLVM 15.x found at ${LLVM_CONFIG_EXE} does not support linking as a static library") endif() # Ignore this directory and try the search again diff --git a/src/zig_clang_cc1_main.cpp b/src/zig_clang_cc1_main.cpp index f648adeba4..de33aa9ea9 100644 --- a/src/zig_clang_cc1_main.cpp +++ b/src/zig_clang_cc1_main.cpp @@ -212,7 +212,9 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { bool Success = CompilerInvocation::CreateFromArgs(Clang->getInvocation(), Argv, Diags, Argv0); - if (Clang->getFrontendOpts().TimeTrace) { + if (Clang->getFrontendOpts().TimeTrace || + !Clang->getFrontendOpts().TimeTracePath.empty()) { + Clang->getFrontendOpts().TimeTrace = 1; llvm::timeTraceProfilerInitialize( Clang->getFrontendOpts().TimeTraceGranularity, Argv0); } @@ -256,12 +258,18 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { if (llvm::timeTraceProfilerEnabled()) { SmallString<128> Path(Clang->getFrontendOpts().OutputFile); llvm::sys::path::replace_extension(Path, "json"); + if (!Clang->getFrontendOpts().TimeTracePath.empty()) { + // replace the suffix to '.json' directly + SmallString<128> TracePath(Clang->getFrontendOpts().TimeTracePath); + if (llvm::sys::fs::is_directory(TracePath)) + llvm::sys::path::append(TracePath, llvm::sys::path::filename(Path)); + Path.assign(TracePath); + } if (auto profilerOutput = Clang->createOutputFile( Path.str(), /*Binary=*/false, /*RemoveFileOnSignal=*/false, /*useTemporary=*/false)) { llvm::timeTraceProfilerWrite(*profilerOutput); - // FIXME(ibiryukov): make profilerOutput flush in destructor instead. - profilerOutput->flush(); + profilerOutput.reset(); llvm::timeTraceProfilerCleanup(); Clang->clearOutputFiles(false); } diff --git a/src/zig_clang_cc1as_main.cpp b/src/zig_clang_cc1as_main.cpp index 6459d1534b..5498810d83 100644 --- a/src/zig_clang_cc1as_main.cpp +++ b/src/zig_clang_cc1as_main.cpp @@ -137,6 +137,9 @@ struct AssemblerInvocation { unsigned IncrementalLinkerCompatible : 1; unsigned EmbedBitcode : 1; + /// Whether to emit DWARF unwind info. + EmitDwarfUnwindType EmitDwarfUnwind; + /// The name of the relocation model to use. std::string RelocationModel; @@ -144,6 +147,9 @@ struct AssemblerInvocation { /// otherwise. std::string TargetABI; + /// Darwin target variant triple, the variant of the deployment target + /// for which the code is being compiled. + llvm::Optional DarwinTargetVariantTriple; /// @} public: @@ -164,6 +170,7 @@ public: Dwarf64 = 0; DwarfVersion = 0; EmbedBitcode = 0; + EmitDwarfUnwind = EmitDwarfUnwindType::Default; } static bool CreateFromArgs(AssemblerInvocation &Res, @@ -209,6 +216,9 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, // Target Options Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)); + if (Arg *A = Args.getLastArg(options::OPT_darwin_target_variant_triple)) + Opts.DarwinTargetVariantTriple = llvm::Triple(A->getValue()); + Opts.CPU = std::string(Args.getLastArgValue(OPT_target_cpu)); Opts.Features = Args.getAllArgValues(OPT_target_feature); @@ -311,6 +321,14 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, .Default(0); } + if (auto *A = Args.getLastArg(OPT_femit_dwarf_unwind_EQ)) { + Opts.EmitDwarfUnwind = + llvm::StringSwitch(A->getValue()) + .Case("always", EmitDwarfUnwindType::Always) + .Case("no-compact-unwind", EmitDwarfUnwindType::NoCompactUnwind) + .Case("default", EmitDwarfUnwindType::Default); + } + return Success; } @@ -361,6 +379,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, assert(MRI && "Unable to create target register info!"); MCTargetOptions MCOptions; + MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind; + std::unique_ptr MAI( TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions)); assert(MAI && "Unable to create target asm info!"); @@ -407,6 +427,8 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, // MCObjectFileInfo needs a MCContext reference in order to initialize itself. std::unique_ptr MOFI( TheTarget->createMCObjectFileInfo(Ctx, PIC)); + if (Opts.DarwinTargetVariantTriple) + MOFI->setDarwinTargetVariantTriple(*Opts.DarwinTargetVariantTriple); Ctx.setObjectFileInfo(MOFI.get()); if (Opts.SaveTemporaryLabels) @@ -455,7 +477,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, std::unique_ptr CE; if (Opts.ShowEncoding) - CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); + CE.reset(TheTarget->createMCCodeEmitter(*MCII, Ctx)); std::unique_ptr MAB( TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); @@ -475,7 +497,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, } std::unique_ptr CE( - TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); + TheTarget->createMCCodeEmitter(*MCII, Ctx)); std::unique_ptr MAB( TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); assert(MAB && "Unable to create asm backend!"); @@ -497,7 +519,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, if (Opts.EmbedBitcode && Ctx.getObjectFileType() == MCContext::IsMachO) { MCSection *AsmLabel = Ctx.getMachOSection( "__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly()); - Str.get()->SwitchSection(AsmLabel); + Str.get()->switchSection(AsmLabel); Str.get()->emitZeros(1); } diff --git a/src/zig_clang_driver.cpp b/src/zig_clang_driver.cpp index 1f24131f14..b83cddf202 100644 --- a/src/zig_clang_driver.cpp +++ b/src/zig_clang_driver.cpp @@ -410,18 +410,18 @@ int ZigClang_main(int Argc, const char **Argv) { if (ClangCLMode) { // Arguments in "CL" are prepended. llvm::Optional OptCL = llvm::sys::Process::GetEnv("CL"); - if (OptCL.hasValue()) { + if (OptCL) { SmallVector PrependedOpts; - getCLEnvVarOptions(OptCL.getValue(), Saver, PrependedOpts); + getCLEnvVarOptions(OptCL.value(), Saver, PrependedOpts); // Insert right after the program name to prepend to the argument list. Args.insert(Args.begin() + 1, PrependedOpts.begin(), PrependedOpts.end()); } // Arguments in "_CL_" are appended. llvm::Optional Opt_CL_ = llvm::sys::Process::GetEnv("_CL_"); - if (Opt_CL_.hasValue()) { + if (Opt_CL_) { SmallVector AppendedOpts; - getCLEnvVarOptions(Opt_CL_.getValue(), Saver, AppendedOpts); + getCLEnvVarOptions(Opt_CL_.value(), Saver, AppendedOpts); // Insert at the end of the argument list to append. Args.append(AppendedOpts.begin(), AppendedOpts.end()); @@ -488,32 +488,39 @@ int ZigClang_main(int Argc, const char **Argv) { } std::unique_ptr C(TheDriver.BuildCompilation(Args)); + + Driver::ReproLevel ReproLevel = Driver::ReproLevel::OnCrash; + if (Arg *A = C->getArgs().getLastArg(options::OPT_gen_reproducer_eq)) { + auto Level = llvm::StringSwitch>(A->getValue()) + .Case("off", Driver::ReproLevel::Off) + .Case("crash", Driver::ReproLevel::OnCrash) + .Case("error", Driver::ReproLevel::OnError) + .Case("always", Driver::ReproLevel::Always) + .Default(None); + if (!Level) { + llvm::errs() << "Unknown value for " << A->getSpelling() << ": '" + << A->getValue() << "'\n"; + return 1; + } + ReproLevel = *Level; + } + if (!!::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH")) + ReproLevel = Driver::ReproLevel::Always; + int Res = 1; bool IsCrash = false; + Driver::CommandStatus CommandStatus = Driver::CommandStatus::Ok; + // Pretend the first command failed if ReproStatus is Always. + const Command *FailingCommand = nullptr; + if (!C->getJobs().empty()) + FailingCommand = &*C->getJobs().begin(); if (C && !C->containsError()) { SmallVector, 4> FailingCommands; Res = TheDriver.ExecuteCompilation(*C, FailingCommands); - // Force a crash to test the diagnostics. - if (TheDriver.GenReproducer) { - Diags.Report(diag::err_drv_force_crash) - << !::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"); - - // Pretend that every command failed. - FailingCommands.clear(); - for (const auto &J : C->getJobs()) - if (const Command *C = dyn_cast(&J)) - FailingCommands.push_back(std::make_pair(-1, C)); - - // Print the bug report message that would be printed if we did actually - // crash, but only if we're crashing due to FORCE_CLANG_DIAGNOSTICS_CRASH. - if (::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH")) - llvm::dbgs() << llvm::getBugReportMsg(); - } - for (const auto &P : FailingCommands) { int CommandRes = P.first; - const Command *FailingCommand = P.second; + FailingCommand = P.second; if (!Res) Res = CommandRes; @@ -532,13 +539,22 @@ int ZigClang_main(int Argc, const char **Argv) { // https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html IsCrash |= CommandRes > 128; #endif - if (IsCrash) { - TheDriver.generateCompilationDiagnostics(*C, *FailingCommand); + CommandStatus = + IsCrash ? Driver::CommandStatus::Crash : Driver::CommandStatus::Error; + if (IsCrash) break; - } } } + // Print the bug report message that would be printed if we did actually + // crash, but only if we're crashing due to FORCE_CLANG_DIAGNOSTICS_CRASH. + if (::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH")) + llvm::dbgs() << llvm::getBugReportMsg(); + if (FailingCommand != nullptr && + TheDriver.maybeGenerateCompilationDiagnostics(CommandStatus, ReproLevel, + *C, *FailingCommand)) + Res = 1; + Diags.getClient()->finish(); if (!UseNewCC1Process && IsCrash) { 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) +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 + " \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"; +} - This program generates an index to speed access to archives - -USAGE: llvm-ranlib - -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] [-][modifiers] [relpos] [count] [files] - llvm-ar -M [ - 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) @ - read options from OPERATIONS: @@ -126,11 +131,20 @@ MODIFIERS: [V] - display the version and exit )"; + outs() << "OVERVIEW: LLVM Archiver\n\n" + << "USAGE: " + ToolName + + " [options] [-][modifiers] [relpos] " + "[count] [files]\n" + << " " + ToolName + " -M [ 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> ArchiveBuffers; static std::vector> 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(&Obj)) + return Triple(IRObj->getTargetTriple()).isArch64Bit(); + if (isa(Obj) || isa(Obj)) + return false; + if (XCOFFObjectFile *XCOFFObj = dyn_cast(&Obj)) + return XCOFFObj->is64Bit(); + if (isa(Obj)) + return false; + if (TapiFile *Tapi = dyn_cast(&Obj)) + return Tapi->is64Bit(); + if (MachOObjectFile *MachO = dyn_cast(&Obj)) + return MachO->is64Bit(); + if (ELFObjectFileBase *ElfO = dyn_cast(&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(&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> getAsBinary(const NewArchiveMember &NM, + LLVMContext *Context) { + auto BinaryOrErr = createBinary(NM.Buf->getMemBufferRef(), Context); + if (BinaryOrErr) + return std::move(*BinaryOrErr); + return BinaryOrErr.takeError(); +} + +Expected> getAsBinary(const Archive::Child &C, + LLVMContext *Context) { + return C.getAsBinary(Context); +} + +template static bool isValidInBitMode(const A &Member) { + if (object::Archive::getDefaultKindForHost() != object::Archive::K_AIXBIG) + return true; + LLVMContext Context; + Expected> 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 &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 NMOrErr = NewArchiveMember::getOldMember(M, Deterministic); failIfError(NMOrErr.takeError()); @@ -692,8 +805,7 @@ static void addChildMember(std::vector &Members, Members.push_back(std::move(*NMOrErr)); } -static void addMember(std::vector &Members, - StringRef FileName, bool FlattenArchive = false) { +static NewArchiveMember getArchiveMember(StringRef FileName) { Expected NMOrErr = NewArchiveMember::getFile(FileName, Deterministic); failIfError(NMOrErr.takeError(), FileName); @@ -713,9 +825,24 @@ static void addMember(std::vector &Members, PathOrErr ? *PathOrErr : sys::path::convert_to_slash(FileName)); } } + return std::move(*NMOrErr); +} + +static void addMember(std::vector &Members, + NewArchiveMember &NM) { + Members.push_back(std::move(NM)); +} + +static void addMember(std::vector &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 &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::iterator &Pos, StringMap &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 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::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> OptionalObject = - object::ObjectFile::createObjectFile(MemBufferRef); - - if (OptionalObject) - return isa(**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(**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 OldArchiveBuf, std::vector *NewMembersP) { + if (OldArchive) { + if (Thin && !OldArchive->isThin()) + fail("cannot convert a regular archive to a thin one"); + + if (OldArchive->isThin()) + Thin = true; + } + std::vector 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(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::iterator &ArgIt, ArrayRef 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::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; }