diff options
Diffstat (limited to 'llvm/tools')
161 files changed, 8468 insertions, 5190 deletions
diff --git a/llvm/tools/bugpoint/CrashDebugger.cpp b/llvm/tools/bugpoint/CrashDebugger.cpp index d06bca9a1258..2601ee318f7d 100644 --- a/llvm/tools/bugpoint/CrashDebugger.cpp +++ b/llvm/tools/bugpoint/CrashDebugger.cpp @@ -425,7 +425,7 @@ void simpleSimplifyCfg(Function &F, SmallVectorImpl<BasicBlock *> &BBs) { } /// ReduceCrashingBlocks reducer - This works by setting the terminators of /// all terminators except the specified basic blocks to a 'ret' instruction, -/// then running the simplify-cfg pass. This has the effect of chopping up +/// then running the simplifycfg pass. This has the effect of chopping up /// the CFG really fast which can reduce large functions quickly. /// class ReduceCrashingBlocks : public ListReducer<const BasicBlock *> { diff --git a/llvm/tools/bugpoint/FindBugs.cpp b/llvm/tools/bugpoint/FindBugs.cpp index 2b1146da9680..771329024c09 100644 --- a/llvm/tools/bugpoint/FindBugs.cpp +++ b/llvm/tools/bugpoint/FindBugs.cpp @@ -41,7 +41,7 @@ BugDriver::runManyPasses(const std::vector<std::string> &AllPasses) { // // Step 1: Randomize the order of the optimizer passes. // - std::shuffle(PassesToRun.begin(), PassesToRun.end(), randomness); + llvm::shuffle(PassesToRun.begin(), PassesToRun.end(), randomness); // // Step 2: Run optimizer passes on the program and check for success. diff --git a/llvm/tools/bugpoint/ListReducer.h b/llvm/tools/bugpoint/ListReducer.h index 04f2207a31ed..06f8ddb25534 100644 --- a/llvm/tools/bugpoint/ListReducer.h +++ b/llvm/tools/bugpoint/ListReducer.h @@ -92,7 +92,7 @@ template <typename ElTy> struct ListReducer { // distribution (improving the speed of convergence). if (ShufflingEnabled && NumOfIterationsWithoutProgress > MaxIterations) { std::vector<ElTy> ShuffledList(TheList); - std::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness); + llvm::shuffle(ShuffledList.begin(), ShuffledList.end(), randomness); errs() << "\n\n*** Testing shuffled set...\n\n"; // Check that random shuffle doesn't lose the bug Expected<TestResult> Result = doTest(ShuffledList, empty); diff --git a/llvm/tools/bugpoint/ToolRunner.cpp b/llvm/tools/bugpoint/ToolRunner.cpp index c4ea1dad122b..b81ab07980dd 100644 --- a/llvm/tools/bugpoint/ToolRunner.cpp +++ b/llvm/tools/bugpoint/ToolRunner.cpp @@ -607,12 +607,12 @@ AbstractInterpreter::createJIT(const char *Argv0, std::string &Message, static bool IsARMArchitecture(std::vector<StringRef> Args) { for (size_t I = 0; I < Args.size(); ++I) { - if (!Args[I].equals_lower("-arch")) + if (!Args[I].equals_insensitive("-arch")) continue; ++I; if (I == Args.size()) break; - if (Args[I].startswith_lower("arm")) + if (Args[I].startswith_insensitive("arm")) return true; } diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp index 48f0adf7c726..6a1e2bae2096 100644 --- a/llvm/tools/llc/llc.cpp +++ b/llvm/tools/llc/llc.cpp @@ -126,9 +126,10 @@ static cl::opt<bool> DisableSimplifyLibCalls("disable-simplify-libcalls", static cl::opt<bool> ShowMCEncoding("show-mc-encoding", cl::Hidden, cl::desc("Show encoding in .s output")); -static cl::opt<bool> EnableDwarfDirectory( - "enable-dwarf-directory", cl::Hidden, - cl::desc("Use .file directives with an explicit directory.")); +static cl::opt<bool> + DwarfDirectory("dwarf-directory", cl::Hidden, + cl::desc("Use .file directives with an explicit directory"), + cl::init(true)); static cl::opt<bool> AsmVerbose("asm-verbose", cl::desc("Add comments to directives."), @@ -276,7 +277,7 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName, std::error_code EC; sys::fs::OpenFlags OpenFlags = sys::fs::OF_None; if (!Binary) - OpenFlags |= sys::fs::OF_Text; + OpenFlags |= sys::fs::OF_TextWithCRLF; auto FDOut = std::make_unique<ToolOutputFile>(OutputFilename, EC, OpenFlags); if (EC) { reportError(EC.message()); @@ -290,6 +291,22 @@ struct LLCDiagnosticHandler : public DiagnosticHandler { bool *HasError; LLCDiagnosticHandler(bool *HasErrorPtr) : HasError(HasErrorPtr) {} bool handleDiagnostics(const DiagnosticInfo &DI) override { + if (DI.getKind() == llvm::DK_SrcMgr) { + const auto &DISM = cast<DiagnosticInfoSrcMgr>(DI); + const SMDiagnostic &SMD = DISM.getSMDiag(); + + if (SMD.getKind() == SourceMgr::DK_Error) + *HasError = true; + + SMD.print(nullptr, errs()); + + // For testing purposes, we print the LocCookie here. + if (DISM.isInlineAsmDiag() && DISM.getLocCookie()) + WithColor::note() << "!srcloc = " << DISM.getLocCookie() << "\n"; + + return true; + } + if (DI.getSeverity() == DS_Error) *HasError = true; @@ -305,19 +322,6 @@ struct LLCDiagnosticHandler : public DiagnosticHandler { } }; -static void InlineAsmDiagHandler(const SMDiagnostic &SMD, void *Context, - unsigned LocCookie) { - bool *HasError = static_cast<bool *>(Context); - if (SMD.getKind() == SourceMgr::DK_Error) - *HasError = true; - - SMD.print(nullptr, errs()); - - // For testing purposes, we print the LocCookie here. - if (LocCookie) - WithColor::note() << "!srcloc = " << LocCookie << "\n"; -} - // main - Entry point for the llc compiler. // int main(int argc, char **argv) { @@ -349,8 +353,10 @@ int main(int argc, char **argv) { initializeVectorization(*Registry); initializeScalarizeMaskedMemIntrinLegacyPassPass(*Registry); initializeExpandReductionsPass(*Registry); + initializeExpandVectorPredicationPass(*Registry); initializeHardwareLoopsPass(*Registry); initializeTransformUtils(*Registry); + initializeReplaceWithVeclibLegacyPass(*Registry); // Initialize debugging passes. initializeScavengerTestPass(*Registry); @@ -366,7 +372,6 @@ int main(int argc, char **argv) { bool HasError = false; Context.setDiagnosticHandler( std::make_unique<LLCDiagnosticHandler>(&HasError)); - Context.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, &HasError); Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr = setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses, @@ -469,7 +474,7 @@ static int compileModule(char **argv, LLVMContext &Context) { TargetMachine::parseBinutilsVersion(BinutilsVersion); Options.DisableIntegratedAS = NoIntegratedAssembler; Options.MCOptions.ShowMCEncoding = ShowMCEncoding; - Options.MCOptions.MCUseDwarfDirectory = EnableDwarfDirectory; + Options.MCOptions.MCUseDwarfDirectory = DwarfDirectory; Options.MCOptions.AsmVerbose = AsmVerbose; Options.MCOptions.PreserveAsmComments = PreserveComments; Options.MCOptions.IASSearchPaths = IncludeDirs; diff --git a/llvm/tools/lli/ExecutionUtils.cpp b/llvm/tools/lli/ExecutionUtils.cpp new file mode 100644 index 000000000000..55370ed40f2b --- /dev/null +++ b/llvm/tools/lli/ExecutionUtils.cpp @@ -0,0 +1,146 @@ +//===---- ExecutionUtils.cpp - Utilities for executing functions in lli ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ExecutionUtils.h" + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +#include <cstdint> +#include <vector> + +// Declarations follow the GDB JIT interface (version 1, 2009) and must match +// those of the DYLD used for testing. See: +// +// llvm/lib/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.cpp +// llvm/lib/ExecutionEngine/GDBRegistrationListener.cpp +// +typedef enum { + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN +} jit_actions_t; + +struct jit_code_entry { + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + const char *symfile_addr; + uint64_t symfile_size; +}; + +struct jit_descriptor { + uint32_t version; + // This should be jit_actions_t, but we want to be specific about the + // bit-width. + uint32_t action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; +}; + +namespace llvm { + +template <typename... Ts> static void outsv(const char *Fmt, Ts &&...Vals) { + outs() << formatv(Fmt, Vals...); +} + +static const char *actionFlagToStr(uint32_t ActionFlag) { + switch (ActionFlag) { + case JIT_NOACTION: + return "JIT_NOACTION"; + case JIT_REGISTER_FN: + return "JIT_REGISTER_FN"; + case JIT_UNREGISTER_FN: + return "JIT_UNREGISTER_FN"; + } + return "<invalid action_flag>"; +} + +// Sample output: +// +// Reading __jit_debug_descriptor at 0x0000000000404048 +// +// Version: 0 +// Action: JIT_REGISTER_FN +// +// Entry Symbol File Size Previous Entry +// [ 0] 0x0000000000451290 0x0000000000002000 200 0x0000000000000000 +// [ 1] 0x0000000000451260 0x0000000000001000 100 0x0000000000451290 +// ... +// +static void dumpDebugDescriptor(void *Addr) { + outsv("Reading __jit_debug_descriptor at {0}\n\n", Addr); + + jit_descriptor *Descriptor = reinterpret_cast<jit_descriptor *>(Addr); + outsv("Version: {0}\n", Descriptor->version); + outsv("Action: {0}\n\n", actionFlagToStr(Descriptor->action_flag)); + outsv("{0,11} {1,24} {2,15} {3,14}\n", "Entry", "Symbol File", "Size", + "Previous Entry"); + + unsigned Idx = 0; + for (auto *Entry = Descriptor->first_entry; Entry; Entry = Entry->next_entry) + outsv("[{0,2}] {1:X16} {2:X16} {3,8:D} {4}\n", Idx++, Entry, + reinterpret_cast<const void *>(Entry->symfile_addr), + Entry->symfile_size, Entry->prev_entry); +} + +static LLIBuiltinFunctionGenerator *Generator = nullptr; + +static void dumpDebugObjects(void *Addr) { + jit_descriptor *Descriptor = reinterpret_cast<jit_descriptor *>(Addr); + for (auto *Entry = Descriptor->first_entry; Entry; Entry = Entry->next_entry) + Generator->appendDebugObject(Entry->symfile_addr, Entry->symfile_size); +} + +LLIBuiltinFunctionGenerator::LLIBuiltinFunctionGenerator( + std::vector<BuiltinFunctionKind> Enabled, orc::MangleAndInterner &Mangle) + : TestOut(nullptr) { + Generator = this; + for (BuiltinFunctionKind F : Enabled) { + switch (F) { + case BuiltinFunctionKind::DumpDebugDescriptor: + expose(Mangle("__dump_jit_debug_descriptor"), &dumpDebugDescriptor); + break; + case BuiltinFunctionKind::DumpDebugObjects: + expose(Mangle("__dump_jit_debug_objects"), &dumpDebugObjects); + TestOut = createToolOutput(); + break; + } + } +} + +Error LLIBuiltinFunctionGenerator::tryToGenerate( + orc::LookupState &LS, orc::LookupKind K, orc::JITDylib &JD, + orc::JITDylibLookupFlags JDLookupFlags, + const orc::SymbolLookupSet &Symbols) { + orc::SymbolMap NewSymbols; + for (const auto &NameFlags : Symbols) { + auto It = BuiltinFunctions.find(NameFlags.first); + if (It != BuiltinFunctions.end()) + NewSymbols.insert(*It); + } + + if (NewSymbols.empty()) + return Error::success(); + + return JD.define(absoluteSymbols(std::move(NewSymbols))); +} + +// static +std::unique_ptr<ToolOutputFile> +LLIBuiltinFunctionGenerator::createToolOutput() { + std::error_code EC; + auto TestOut = std::make_unique<ToolOutputFile>("-", EC, sys::fs::OF_None); + if (EC) { + errs() << "Error creating tool output file: " << EC.message() << '\n'; + exit(1); + } + return TestOut; +} + +} // namespace llvm diff --git a/llvm/tools/lli/ExecutionUtils.h b/llvm/tools/lli/ExecutionUtils.h new file mode 100644 index 000000000000..fcd1db05cca3 --- /dev/null +++ b/llvm/tools/lli/ExecutionUtils.h @@ -0,0 +1,60 @@ +//===- ExecutionUtils.h - Utilities for executing code in lli ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Contains utilities for executing code in lli. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLI_EXECUTIONUTILS_H +#define LLVM_TOOLS_LLI_EXECUTIONUTILS_H + +#include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/Mangling.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ToolOutputFile.h" + +#include <memory> +#include <utility> + +namespace llvm { + +enum class BuiltinFunctionKind { + DumpDebugDescriptor, + DumpDebugObjects, +}; + +// Utility class to expose symbols for special-purpose functions to the JIT. +class LLIBuiltinFunctionGenerator : public orc::DefinitionGenerator { +public: + LLIBuiltinFunctionGenerator(std::vector<BuiltinFunctionKind> Enabled, + orc::MangleAndInterner &Mangle); + + Error tryToGenerate(orc::LookupState &LS, orc::LookupKind K, + orc::JITDylib &JD, orc::JITDylibLookupFlags JDLookupFlags, + const orc::SymbolLookupSet &Symbols) override; + + void appendDebugObject(const char *Addr, size_t Size) { + TestOut->os().write(Addr, Size); + } + +private: + orc::SymbolMap BuiltinFunctions; + std::unique_ptr<ToolOutputFile> TestOut; + + template <typename T> void expose(orc::SymbolStringPtr Name, T *Handler) { + BuiltinFunctions[Name] = JITEvaluatedSymbol( + pointerToJITTargetAddress(Handler), JITSymbolFlags::Exported); + } + + static std::unique_ptr<ToolOutputFile> createToolOutput(); +}; + +} // end namespace llvm + +#endif // LLVM_TOOLS_LLI_EXECUTIONUTILS_H diff --git a/llvm/tools/lli/lli.cpp b/llvm/tools/lli/lli.cpp index 70c838126946..af614c01b9a8 100644 --- a/llvm/tools/lli/lli.cpp +++ b/llvm/tools/lli/lli.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "ExecutionUtils.h" #include "RemoteJITUtils.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" @@ -22,15 +23,21 @@ #include "llvm/ExecutionEngine/GenericValue.h" #include "llvm/ExecutionEngine/Interpreter.h" #include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/ObjectCache.h" +#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h" #include "llvm/ExecutionEngine/Orc/DebugUtils.h" +#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h" +#include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h" #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" -#include "llvm/ExecutionEngine/Orc/MachOPlatform.h" #include "llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h" #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/IRBuilder.h" @@ -76,7 +83,8 @@ static codegen::RegisterCodeGenFlags CGF; namespace { - enum class JITKind { MCJIT, OrcLazy }; + enum class JITKind { MCJIT, Orc, OrcLazy }; + enum class JITLinkerKind { Default, RuntimeDyld, JITLink }; cl::opt<std::string> InputFile(cl::desc("<input bitcode>"), cl::Positional, cl::init("-")); @@ -90,11 +98,22 @@ namespace { cl::opt<JITKind> UseJITKind( "jit-kind", cl::desc("Choose underlying JIT kind."), - cl::init(JITKind::MCJIT), + cl::init(JITKind::Orc), cl::values(clEnumValN(JITKind::MCJIT, "mcjit", "MCJIT"), + clEnumValN(JITKind::Orc, "orc", "Orc JIT"), clEnumValN(JITKind::OrcLazy, "orc-lazy", "Orc-based lazy JIT."))); + cl::opt<JITLinkerKind> + JITLinker("jit-linker", cl::desc("Choose the dynamic linker/loader."), + cl::init(JITLinkerKind::Default), + cl::values(clEnumValN(JITLinkerKind::Default, "default", + "Default for platform and JIT-kind"), + clEnumValN(JITLinkerKind::RuntimeDyld, "rtdyld", + "RuntimeDyld"), + clEnumValN(JITLinkerKind::JITLink, "jitlink", + "Orc-specific linker"))); + cl::opt<unsigned> LazyJITCompileThreads("compile-threads", cl::desc("Choose the number of compile threads " @@ -208,7 +227,7 @@ namespace { cl::desc("Do not resolve lli process symbols in JIT'd code"), cl::init(false)); - enum class LLJITPlatform { DetectHost, GenericIR, MachO }; + enum class LLJITPlatform { Inactive, DetectHost, GenericIR }; cl::opt<LLJITPlatform> Platform("lljit-platform", cl::desc("Platform to use with LLJIT"), @@ -217,8 +236,8 @@ namespace { "Select based on JIT target triple"), clEnumValN(LLJITPlatform::GenericIR, "GenericIR", "Use LLJITGenericIRPlatform"), - clEnumValN(LLJITPlatform::MachO, "MachO", - "Use LLJITMachOPlatform")), + clEnumValN(LLJITPlatform::Inactive, "Inactive", + "Disable platform support explicitly")), cl::Hidden); enum class DumpKind { @@ -243,9 +262,28 @@ namespace { "will overwrite existing files).")), cl::Hidden); + cl::list<BuiltinFunctionKind> GenerateBuiltinFunctions( + "generate", + cl::desc("Provide built-in functions for access by JITed code " + "(jit-kind=orc-lazy only)"), + cl::values(clEnumValN(BuiltinFunctionKind::DumpDebugDescriptor, + "__dump_jit_debug_descriptor", + "Dump __jit_debug_descriptor contents to stdout"), + clEnumValN(BuiltinFunctionKind::DumpDebugObjects, + "__dump_jit_debug_objects", + "Dump __jit_debug_descriptor in-memory debug " + "objects as tool output")), + cl::Hidden); + ExitOnError ExitOnErr; } +LLVM_ATTRIBUTE_USED void linkComponents() { + errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper + << (void *)&llvm_orc_deregisterEHFrameSectionWrapper + << (void *)&llvm_orc_registerJITLoaderGDBWrapper; +} + //===----------------------------------------------------------------------===// // Object cache // @@ -288,7 +326,8 @@ public: return nullptr; // Load the object from the cache filename ErrorOr<std::unique_ptr<MemoryBuffer>> IRObjectBuffer = - MemoryBuffer::getFile(CacheName, -1, false); + MemoryBuffer::getFile(CacheName, /*IsText=*/false, + /*RequiresNullTerminator=*/false); // If the file isn't there, that's OK. if (!IRObjectBuffer) return nullptr; @@ -378,7 +417,7 @@ static void reportError(SMDiagnostic Err, const char *ProgName) { } Error loadDylibs(); -int runOrcLazyJIT(const char *ProgName); +int runOrcJIT(const char *ProgName); void disallowOrcOptions(); //===----------------------------------------------------------------------===// @@ -405,11 +444,12 @@ int main(int argc, char **argv, char * const *envp) { ExitOnErr(loadDylibs()); - if (UseJITKind == JITKind::OrcLazy) - return runOrcLazyJIT(argv[0]); - else + if (UseJITKind == JITKind::MCJIT) disallowOrcOptions(); + else + return runOrcJIT(argv[0]); + // Old lli implementation based on ExecutionEngine and MCJIT. LLVMContext Context; // Load the bitcode... @@ -677,7 +717,8 @@ int main(int argc, char **argv, char * const *envp) { } // Create a remote target client running over the channel. - llvm::orc::ExecutionSession ES; + llvm::orc::ExecutionSession ES( + std::make_unique<orc::UnsupportedExecutorProcessControl>()); ES.setErrorReporter([&](Error Err) { ExitOnErr(std::move(Err)); }); typedef orc::remote::OrcRemoteTargetClient MyRemote; auto R = ExitOnErr(MyRemote::Create(*C, ES)); @@ -748,7 +789,8 @@ static std::function<void(Module &)> createDebugDumper() { case DumpKind::DumpModsToDisk: return [](Module &M) { std::error_code EC; - raw_fd_ostream Out(M.getModuleIdentifier() + ".ll", EC, sys::fs::OF_Text); + raw_fd_ostream Out(M.getModuleIdentifier() + ".ll", EC, + sys::fs::OF_TextWithCRLF); if (EC) { errs() << "Couldn't open " << M.getModuleIdentifier() << " for dumping.\nError:" << EC.message() << "\n"; @@ -791,7 +833,7 @@ loadModule(StringRef Path, orc::ThreadSafeContext TSCtx) { return orc::ThreadSafeModule(std::move(M), std::move(TSCtx)); } -int runOrcLazyJIT(const char *ProgName) { +int runOrcJIT(const char *ProgName) { // Start setting up the JIT environment. // Parse the main module. @@ -829,6 +871,17 @@ int runOrcLazyJIT(const char *ProgName) { .setRelocationModel(codegen::getExplicitRelocModel()) .setCodeModel(codegen::getExplicitCodeModel()); + // FIXME: Setting a dummy call-through manager in non-lazy mode prevents the + // JIT builder to instantiate a default (which would fail with an error for + // unsupported architectures). + if (UseJITKind != JITKind::OrcLazy) { + auto ES = std::make_unique<orc::ExecutionSession>( + ExitOnErr(orc::SelfExecutorProcessControl::Create())); + Builder.setLazyCallthroughManager( + std::make_unique<orc::LazyCallThroughManager>(*ES, 0, nullptr)); + Builder.setExecutionSession(std::move(ES)); + } + Builder.setLazyCompileFailureAddr( pointerToJITTargetAddress(exitOnLazyCallThroughFailure)); Builder.setNumCompileThreads(LazyJITCompileThreads); @@ -859,32 +912,43 @@ int runOrcLazyJIT(const char *ProgName) { // Set up LLJIT platform. { LLJITPlatform P = Platform; - if (P == LLJITPlatform::DetectHost) { - if (TT->isOSBinFormatMachO()) - P = LLJITPlatform::MachO; - else - P = LLJITPlatform::GenericIR; - } + if (P == LLJITPlatform::DetectHost) + P = LLJITPlatform::GenericIR; switch (P) { case LLJITPlatform::GenericIR: // Nothing to do: LLJITBuilder will use this by default. break; - case LLJITPlatform::MachO: - Builder.setPlatformSetUp(orc::setUpMachOPlatform); - ExitOnErr(orc::enableObjCRegistration("libobjc.dylib")); + case LLJITPlatform::Inactive: + Builder.setPlatformSetUp(orc::setUpInactivePlatform); break; default: llvm_unreachable("Unrecognized platform value"); } } + std::unique_ptr<orc::ExecutorProcessControl> EPC = nullptr; + if (JITLinker == JITLinkerKind::JITLink) { + EPC = ExitOnErr(orc::SelfExecutorProcessControl::Create( + std::make_shared<orc::SymbolStringPool>())); + + Builder.setObjectLinkingLayerCreator([&EPC](orc::ExecutionSession &ES, + const Triple &) { + auto L = std::make_unique<orc::ObjectLinkingLayer>(ES, EPC->getMemMgr()); + L->addPlugin(std::make_unique<orc::EHFrameRegistrationPlugin>( + ES, ExitOnErr(orc::EPCEHFrameRegistrar::Create(ES)))); + L->addPlugin(std::make_unique<orc::DebugObjectManagerPlugin>( + ES, ExitOnErr(orc::createJITLoaderGDBRegistrar(ES)))); + return L; + }); + } + auto J = ExitOnErr(Builder.create()); - if (TT->isOSBinFormatELF()) - static_cast<llvm::orc::RTDyldObjectLinkingLayer &>(J->getObjLinkingLayer()) - .registerJITEventListener( - *JITEventListener::createGDBRegistrationListener()); + auto *ObjLayer = &J->getObjLinkingLayer(); + if (auto *RTDyldObjLayer = dyn_cast<orc::RTDyldObjectLinkingLayer>(ObjLayer)) + RTDyldObjLayer->registerJITEventListener( + *JITEventListener::createGDBRegistrationListener()); if (PerModuleLazy) J->setPartitionFunction(orc::CompileOnDemandLayer::compileWholeModule); @@ -916,8 +980,22 @@ int runOrcLazyJIT(const char *ProgName) { return Name != MainName; }))); + if (GenerateBuiltinFunctions.size() > 0) + J->getMainJITDylib().addGenerator( + std::make_unique<LLIBuiltinFunctionGenerator>(GenerateBuiltinFunctions, + Mangle)); + + // Regular modules are greedy: They materialize as a whole and trigger + // materialization for all required symbols recursively. Lazy modules go + // through partitioning and they replace outgoing calls with reexport stubs + // that resolve on call-through. + auto AddModule = [&](orc::JITDylib &JD, orc::ThreadSafeModule M) { + return UseJITKind == JITKind::OrcLazy ? J->addLazyIRModule(JD, std::move(M)) + : J->addIRModule(JD, std::move(M)); + }; + // Add the main module. - ExitOnErr(J->addLazyIRModule(std::move(MainModule))); + ExitOnErr(AddModule(J->getMainJITDylib(), std::move(MainModule))); // Create JITDylibs and add any extra modules. { @@ -945,7 +1023,7 @@ int runOrcLazyJIT(const char *ProgName) { assert(EMIdx != 0 && "ExtraModule should have index > 0"); auto JDItr = std::prev(IdxToDylib.lower_bound(EMIdx)); auto &JD = *JDItr->second; - ExitOnErr(J->addLazyIRModule(JD, std::move(M))); + ExitOnErr(AddModule(JD, std::move(M))); } for (auto EAItr = ExtraArchives.begin(), EAEnd = ExtraArchives.end(); @@ -978,13 +1056,19 @@ int runOrcLazyJIT(const char *ProgName) { AltEntryThreads.push_back(std::thread([EntryPoint]() { EntryPoint(); })); } - // Run main. - auto MainSym = ExitOnErr(J->lookup("main")); + // Resolve and run the main function. + JITEvaluatedSymbol MainSym = ExitOnErr(J->lookup(EntryFunc)); + int Result; - typedef int (*MainFnPtr)(int, char *[]); - auto Result = orc::runAsMain( - jitTargetAddressToFunction<MainFnPtr>(MainSym.getAddress()), InputArgv, - StringRef(InputFile)); + if (EPC) { + // ExecutorProcessControl-based execution with JITLink. + Result = ExitOnErr(EPC->runAsMain(MainSym.getAddress(), InputArgv)); + } else { + // Manual in-process execution with RuntimeDyld. + using MainFnTy = int(int, char *[]); + auto MainFn = jitTargetAddressToFunction<MainFnTy *>(MainSym.getAddress()); + Result = orc::runAsMain(MainFn, InputArgv, StringRef(InputFile)); + } // Wait for -entry-point threads. for (auto &AltEntryThread : AltEntryThreads) diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp index 4c26c8cad3fa..0e1dce6bc2e8 100644 --- a/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/llvm/tools/llvm-ar/llvm-ar.cpp @@ -126,9 +126,9 @@ MODIFIERS: )"; static void printHelpMessage() { - if (Stem.contains_lower("ranlib")) + if (Stem.contains_insensitive("ranlib")) outs() << RanlibHelp; - else if (Stem.contains_lower("ar")) + else if (Stem.contains_insensitive("ar")) outs() << ArHelp; } @@ -270,7 +270,8 @@ static void getArchive() { } static object::Archive &readLibrary(const Twine &Library) { - auto BufOrErr = MemoryBuffer::getFile(Library, -1, false); + auto BufOrErr = MemoryBuffer::getFile(Library, /*IsText=*/false, + /*RequiresNullTerminator=*/false); failIfError(BufOrErr.getError(), "could not open library " + Library); ArchiveBuffers.push_back(std::move(*BufOrErr)); auto LibOrErr = @@ -995,8 +996,8 @@ static void performOperation(ArchiveOperation Operation, static int performOperation(ArchiveOperation Operation, std::vector<NewArchiveMember> *NewMembers) { // Create or open the archive object. - ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = - MemoryBuffer::getFile(ArchiveName, -1, false); + ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile( + ArchiveName, /*IsText=*/false, /*RequiresNullTerminator=*/false); std::error_code EC = Buf.getError(); if (EC && EC != errc::no_such_file_or_directory) fail("unable to open '" + ArchiveName + "': " + EC.message()); @@ -1275,7 +1276,7 @@ int main(int argc, char **argv) { // Lib.exe -> lib (see D44808, MSBuild runs Lib.exe) // dlltool.exe -> dlltool // arm-pokymllib32-linux-gnueabi-llvm-ar-10 -> ar - auto I = Stem.rfind_lower(Tool); + auto I = Stem.rfind_insensitive(Tool); return I != StringRef::npos && (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); }; diff --git a/llvm/tools/llvm-as/llvm-as.cpp b/llvm/tools/llvm-as/llvm-as.cpp index f2b52890a7f5..307a7f9b7999 100644 --- a/llvm/tools/llvm-as/llvm-as.cpp +++ b/llvm/tools/llvm-as/llvm-as.cpp @@ -115,9 +115,9 @@ static void WriteOutputFile(const Module *M, const ModuleSummaryIndex *Index) { int main(int argc, char **argv) { InitLLVM X(argc, argv); - LLVMContext Context; cl::HideUnrelatedOptions(AsCat); cl::ParseCommandLineOptions(argc, argv, "llvm .ll -> .bc assembler\n"); + LLVMContext Context; // Parse the file now... SMDiagnostic Err; diff --git a/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index 639a6d1ec02c..f4851bfb2a9c 100644 --- a/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -32,37 +32,48 @@ #include "llvm/Support/Error.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <memory> using namespace llvm; -static cl::opt<std::string> - InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); +static cl::OptionCategory BCAnalyzerCategory("BC Analyzer Options"); + +static cl::opt<std::string> InputFilename(cl::Positional, + cl::desc("<input bitcode>"), + cl::init("-"), + cl::cat(BCAnalyzerCategory)); -static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace")); +static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace"), + cl::cat(BCAnalyzerCategory)); //===----------------------------------------------------------------------===// // Bitcode specific analysis. //===----------------------------------------------------------------------===// static cl::opt<bool> NoHistogram("disable-histogram", - cl::desc("Do not print per-code histogram")); + cl::desc("Do not print per-code histogram"), + cl::cat(BCAnalyzerCategory)); static cl::opt<bool> NonSymbolic("non-symbolic", cl::desc("Emit numeric info in dump even if" - " symbolic info is available")); + " symbolic info is available"), + cl::cat(BCAnalyzerCategory)); static cl::opt<std::string> BlockInfoFilename("block-info", - cl::desc("Use the BLOCK_INFO from the given file")); + cl::desc("Use the BLOCK_INFO from the given file"), + cl::cat(BCAnalyzerCategory)); static cl::opt<bool> ShowBinaryBlobs("show-binary-blobs", - cl::desc("Print binary blobs using hex escapes")); + cl::desc("Print binary blobs using hex escapes"), + cl::cat(BCAnalyzerCategory)); static cl::opt<std::string> CheckHash( "check-hash", - cl::desc("Check module hash using the argument as a string table")); + cl::desc("Check module hash using the argument as a string table"), + cl::cat(BCAnalyzerCategory)); static Error reportError(StringRef Message) { return createStringError(std::errc::illegal_byte_sequence, Message.data()); @@ -85,6 +96,8 @@ static Expected<std::unique_ptr<MemoryBuffer>> openBitcodeFile(StringRef Path) { int main(int argc, char **argv) { InitLLVM X(argc, argv); + + cl::HideUnrelatedOptions({&BCAnalyzerCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm-bcanalyzer file analyzer\n"); ExitOnError ExitOnErr("llvm-bcanalyzer: "); diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index baa968820b63..02c0106cbc29 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -80,6 +80,12 @@ private: /// directory, recursively collect all of the paths within the directory. void collectPaths(const std::string &Path); + /// Check if the two given files are the same file. + bool isEquivalentFile(StringRef FilePath1, StringRef FilePath2); + + /// Retrieve a file status with a cache. + Optional<sys::fs::file_status> getFileStatus(StringRef FilePath); + /// Return a memory buffer for the given source file. ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile); @@ -153,6 +159,9 @@ private: /// remapped to, when using -path-equivalence. Optional<std::pair<std::string, std::string>> PathRemapping; + /// File status cache used when finding the same file. + StringMap<Optional<sys::fs::file_status>> FileStatusCache; + /// The architecture the coverage mapping data targets. std::vector<StringRef> CoverageArches; @@ -239,6 +248,27 @@ void CodeCoverageTool::collectPaths(const std::string &Path) { } } +Optional<sys::fs::file_status> +CodeCoverageTool::getFileStatus(StringRef FilePath) { + auto It = FileStatusCache.try_emplace(FilePath); + auto &CachedStatus = It.first->getValue(); + if (!It.second) + return CachedStatus; + + sys::fs::file_status Status; + if (!sys::fs::status(FilePath, Status)) + CachedStatus = Status; + return CachedStatus; +} + +bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1, + StringRef FilePath2) { + auto Status1 = getFileStatus(FilePath1); + auto Status2 = getFileStatus(FilePath2); + return Status1.hasValue() && Status2.hasValue() && + sys::fs::equivalent(Status1.getValue(), Status2.getValue()); +} + ErrorOr<const MemoryBuffer &> CodeCoverageTool::getSourceFile(StringRef SourceFile) { // If we've remapped filenames, look up the real location for this file. @@ -249,7 +279,7 @@ CodeCoverageTool::getSourceFile(StringRef SourceFile) { SourceFile = Loc->second; } for (const auto &Files : LoadedSourceFiles) - if (sys::fs::equivalent(SourceFile, Files.first)) + if (isEquivalentFile(SourceFile, Files.first)) return *Files.second; auto Buffer = MemoryBuffer::getFile(SourceFile); if (auto EC = Buffer.getError()) { @@ -404,7 +434,8 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { warning("profile data may be out of date - object is newer", ObjectFilename); auto CoverageOrErr = - CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches); + CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches, + ViewOpts.CompilationDirectory); if (Error E = CoverageOrErr.takeError()) { error("Failed to load coverage: " + toString(std::move(E)), join(ObjectFilenames.begin(), ObjectFilenames.end(), ", ")); @@ -445,7 +476,7 @@ void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { SmallString<128> NativePath; sys::path::native(Path, NativePath); sys::path::remove_dots(NativePath, true); - if (!sys::path::is_separator(NativePath.back())) + if (!NativePath.empty() && !sys::path::is_separator(NativePath.back())) NativePath += sys::path::get_separator(); return NativePath.c_str(); }; @@ -709,6 +740,10 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), cl::aliasopt(NumThreads)); + cl::opt<std::string> CompilationDirectory( + "compilation-dir", cl::init(""), + cl::desc("Directory used as a base for relative coverage mapping paths")); + auto commandLineParser = [&, this](int argc, const char **argv) -> int { cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); ViewOpts.Debug = DebugDump; @@ -843,6 +878,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { ViewOpts.ShowInstantiationSummary = InstantiationSummary; ViewOpts.ExportSummaryOnly = SummaryOnly; ViewOpts.NumThreads = NumThreads; + ViewOpts.CompilationDirectory = CompilationDirectory; return 0; }; diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp index d1446f379f00..d341abe8dfc8 100644 --- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -123,8 +123,7 @@ collectNestedBranches(const coverage::CoverageMapping &Coverage, // Recursively collect branches from nested expansions. auto NestedExpansions = ExpansionCoverage.getExpansions(); auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions); - Branches.insert(Branches.end(), NestedExBranches.begin(), - NestedExBranches.end()); + append_range(Branches, NestedExBranches); // Add branches from this level of expansion. auto ExBranches = ExpansionCoverage.getBranches(); diff --git a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp index 99ca037e7b5e..6cf5d9285b90 100644 --- a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp @@ -91,8 +91,7 @@ collectNestedBranches(const coverage::CoverageMapping &Coverage, auto NestedExpansions = ExpansionCoverage.getExpansions(); auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions, ViewDepth + 1, SrcLine); - Branches.insert(Branches.end(), NestedExBranches.begin(), - NestedExBranches.end()); + append_range(Branches, NestedExBranches); // Add branches from this level of expansion. auto ExBranches = ExpansionCoverage.getBranches(); @@ -123,7 +122,7 @@ void renderBranchExecutionCounts(raw_ostream &OS, collectNestedBranches(Coverage, FileCoverage.getExpansions()); // Append Expansion Branches to Source Branches. - Branches.insert(Branches.end(), ExBranches.begin(), ExBranches.end()); + append_range(Branches, ExBranches); // Sort branches based on line number to ensure branches corresponding to the // same source line are counted together. diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp index 4a0a86168908..10e059adeb7d 100644 --- a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp +++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp @@ -100,11 +100,7 @@ FunctionCoverageSummary::get(const InstantiationGroup &Group, for (const auto &FCS : Summaries.drop_front()) { Summary.RegionCoverage.merge(FCS.RegionCoverage); Summary.LineCoverage.merge(FCS.LineCoverage); - - // Sum branch coverage across instantiation groups for the summary rather - // than "merge" the maximum count. This is a clearer view into whether all - // created branches are covered. - Summary.BranchCoverage += FCS.BranchCoverage; + Summary.BranchCoverage.merge(FCS.BranchCoverage); } return Summary; } diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.h b/llvm/tools/llvm-cov/CoverageSummaryInfo.h index 4bc1c24a079f..62e7cad1012b 100644 --- a/llvm/tools/llvm-cov/CoverageSummaryInfo.h +++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.h @@ -123,6 +123,11 @@ public: return *this; } + void merge(const BranchCoverageInfo &RHS) { + Covered = std::max(Covered, RHS.Covered); + NumBranches = std::max(NumBranches, RHS.NumBranches); + } + size_t getCovered() const { return Covered; } size_t getNumBranches() const { return NumBranches; } diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h index eee4ba74e165..045fb1787bce 100644 --- a/llvm/tools/llvm-cov/CoverageViewOptions.h +++ b/llvm/tools/llvm-cov/CoverageViewOptions.h @@ -49,6 +49,7 @@ struct CoverageViewOptions { std::string ProjectTitle; std::string CreatedTimeStr; unsigned NumThreads; + std::string CompilationDirectory; /// Change the output's stream color if the colors are enabled. ColoredRawOstream colored_ostream(raw_ostream &OS, diff --git a/llvm/tools/llvm-cov/TestingSupport.cpp b/llvm/tools/llvm-cov/TestingSupport.cpp index b99bd83157d0..9c6b25f2f585 100644 --- a/llvm/tools/llvm-cov/TestingSupport.cpp +++ b/llvm/tools/llvm-cov/TestingSupport.cpp @@ -10,6 +10,7 @@ #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Alignment.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" #include <functional> @@ -47,7 +48,7 @@ int convertForTestingMain(int argc, const char *argv[]) { // Look for the sections that we are interested in. int FoundSectionCount = 0; - SectionRef ProfileNames, CoverageMapping; + SectionRef ProfileNames, CoverageMapping, CoverageRecords; auto ObjFormat = OF->getTripleObjectFormat(); for (const auto &Section : OF->sections()) { StringRef Name; @@ -64,16 +65,20 @@ int convertForTestingMain(int argc, const char *argv[]) { } else if (Name == llvm::getInstrProfSectionName( IPSK_covmap, ObjFormat, /*AddSegmentInfo=*/false)) { CoverageMapping = Section; + } else if (Name == llvm::getInstrProfSectionName( + IPSK_covfun, ObjFormat, /*AddSegmentInfo=*/false)) { + CoverageRecords = Section; } else continue; ++FoundSectionCount; } - if (FoundSectionCount != 2) + if (FoundSectionCount != 3) return 1; // Get the contents of the given sections. uint64_t ProfileNamesAddress = ProfileNames.getAddress(); StringRef CoverageMappingData; + StringRef CoverageRecordsData; StringRef ProfileNamesData; if (Expected<StringRef> E = CoverageMapping.getContents()) CoverageMappingData = *E; @@ -81,6 +86,12 @@ int convertForTestingMain(int argc, const char *argv[]) { consumeError(E.takeError()); return 1; } + if (Expected<StringRef> E = CoverageRecords.getContents()) + CoverageRecordsData = *E; + else { + consumeError(E.takeError()); + return 1; + } if (Expected<StringRef> E = ProfileNames.getContents()) ProfileNamesData = *E; else { @@ -103,6 +114,10 @@ int convertForTestingMain(int argc, const char *argv[]) { for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad) OS.write(uint8_t(0)); OS << CoverageMappingData; + // Coverage records data is expected to have an alignment of 8. + for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad) + OS.write(uint8_t(0)); + OS << CoverageRecordsData; return 0; } diff --git a/llvm/tools/llvm-cov/gcov.cpp b/llvm/tools/llvm-cov/gcov.cpp index d42e7cd3b551..9a1ebebc87fc 100644 --- a/llvm/tools/llvm-cov/gcov.cpp +++ b/llvm/tools/llvm-cov/gcov.cpp @@ -46,7 +46,8 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, // Open .gcda and .gcda without requiring a NUL terminator. The concurrent // modification may nullify the NUL terminator condition. ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff = - MemoryBuffer::getFileOrSTDIN(GCNO, -1, /*RequiresNullTerminator=*/false); + MemoryBuffer::getFileOrSTDIN(GCNO, /*IsText=*/false, + /*RequiresNullTerminator=*/false); if (std::error_code EC = GCNO_Buff.getError()) { errs() << GCNO << ": " << EC.message() << "\n"; return; @@ -58,7 +59,8 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, } ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff = - MemoryBuffer::getFileOrSTDIN(GCDA, -1, /*RequiresNullTerminator=*/false); + MemoryBuffer::getFileOrSTDIN(GCDA, /*IsText=*/false, + /*RequiresNullTerminator=*/false); if (std::error_code EC = GCDA_Buff.getError()) { if (EC != errc::no_such_file_or_directory) { errs() << GCDA << ": " << EC.message() << "\n"; diff --git a/llvm/tools/llvm-cov/llvm-cov.cpp b/llvm/tools/llvm-cov/llvm-cov.cpp index 172ec9f3cedf..0e320c0965f9 100644 --- a/llvm/tools/llvm-cov/llvm-cov.cpp +++ b/llvm/tools/llvm-cov/llvm-cov.cpp @@ -60,7 +60,7 @@ int main(int argc, const char **argv) { InitLLVM X(argc, argv); // If argv[0] is or ends with 'gcov', always be gcov compatible - if (sys::path::stem(argv[0]).endswith_lower("gcov")) + if (sys::path::stem(argv[0]).endswith_insensitive("gcov")) return gcovMain(argc, argv); // Check if we are invoking a specific tool command. diff --git a/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp b/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp index 03e1bab9417e..f214288e951b 100644 --- a/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp +++ b/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp @@ -33,9 +33,10 @@ using namespace llvm::object; using namespace llvm::support; namespace opts { +cl::OptionCategory CXXDumpCategory("CXX Dump Options"); cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input object files>"), - cl::ZeroOrMore); + cl::ZeroOrMore, cl::cat(CXXDumpCategory)); } // namespace opts namespace llvm { @@ -549,6 +550,7 @@ int main(int argc, const char *argv[]) { // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + cl::HideUnrelatedOptions({&opts::CXXDumpCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "LLVM C++ ABI Data Dumper\n"); // Default to stdin if no filename is specified. diff --git a/llvm/tools/llvm-cxxfilt/Opts.td b/llvm/tools/llvm-cxxfilt/Opts.td new file mode 100644 index 000000000000..93f865245fe6 --- /dev/null +++ b/llvm/tools/llvm-cxxfilt/Opts.td @@ -0,0 +1,28 @@ +include "llvm/Option/OptParser.td" + +class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>; +class FF<string name, string help> : Flag<["--"], name>, HelpText<help>; + +multiclass BB<string name, string help1, string help2> { + def NAME: Flag<["--"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; +} + +multiclass Eq<string name, string help> { + def NAME #_EQ : Joined<["--"], name #"=">, + HelpText<help>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; +} + +def help : FF<"help", "Display this help">; +defm strip_underscore : BB<"strip-underscore", "Strip the leading underscore", "Don't strip the leading underscore">; +def types : FF<"types", "">; +def version : FF<"version", "Display the version">; + +defm : Eq<"format", "Specify mangling format. Currently ignored because only 'gnu' is supported">; +def : F<"s", "Alias for --format">; + +def : F<"_", "Alias for --strip-underscore">, Alias<strip_underscore>; +def : F<"h", "Alias for --help">, Alias<help>; +def : F<"n", "Alias for --no-strip-underscore">, Alias<no_strip_underscore>; +def : F<"t", "Alias for --types">, Alias<types>; diff --git a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp index 93d6322a167e..d8bf8dbccce0 100644 --- a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp +++ b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp @@ -9,69 +9,59 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <cstdlib> #include <iostream> using namespace llvm; -enum Style { - Auto, ///< auto-detect mangling - GNU, ///< GNU - Lucid, ///< Lucid compiler (lcc) - ARM, - HP, ///< HP compiler (xCC) - EDG, ///< EDG compiler - GNUv3, ///< GNU C++ v3 ABI - Java, ///< Java (gcj) - GNAT ///< ADA compiler (gnat) +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION }; -static cl::opt<Style> - Format("format", cl::desc("decoration style"), - cl::values(clEnumValN(Auto, "auto", "auto-detect style"), - clEnumValN(GNU, "gnu", "GNU (itanium) style")), - cl::init(Auto)); -static cl::alias FormatShort("s", cl::desc("alias for --format"), - cl::aliasopt(Format)); - -static cl::opt<bool> StripUnderscore("strip-underscore", - cl::desc("strip the leading underscore"), - cl::init(false)); -static cl::alias StripUnderscoreShort("_", - cl::desc("alias for --strip-underscore"), - cl::aliasopt(StripUnderscore)); -static cl::opt<bool> - NoStripUnderscore("no-strip-underscore", - cl::desc("do not strip the leading underscore"), - cl::init(false)); -static cl::alias - NoStripUnderscoreShort("n", cl::desc("alias for --no-strip-underscore"), - cl::aliasopt(NoStripUnderscore)); - -static cl::opt<bool> - Types("types", - cl::desc("attempt to demangle types as well as function names"), - cl::init(false)); -static cl::alias TypesShort("t", cl::desc("alias for --types"), - cl::aliasopt(Types)); - -static cl::list<std::string> -Decorated(cl::Positional, cl::desc("<mangled>"), cl::ZeroOrMore); - -static cl::extrahelp - HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); - -static bool shouldStripUnderscore() { - if (StripUnderscore) - return true; - if (NoStripUnderscore) - return false; - // If none of them are set, use the default value for platform. - // macho has symbols prefix with "_" so strip by default. - return Triple(sys::getProcessTriple()).isOSBinFormatMachO(); + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class CxxfiltOptTable : public opt::OptTable { +public: + CxxfiltOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } +}; +} // namespace + +static bool StripUnderscore; +static bool Types; + +static StringRef ToolName; + +static void error(const Twine &Message) { + WithColor::error(errs(), ToolName) << Message << '\n'; + exit(1); } static std::string demangle(const std::string &Mangled) { @@ -79,7 +69,7 @@ static std::string demangle(const std::string &Mangled) { std::string Prefix; const char *DecoratedStr = Mangled.c_str(); - if (shouldStripUnderscore()) + if (StripUnderscore) if (DecoratedStr[0] == '_') ++DecoratedStr; size_t DecoratedLength = strlen(DecoratedStr); @@ -97,6 +87,11 @@ static std::string demangle(const std::string &Mangled) { Undecorated = itaniumDemangle(DecoratedStr + 6, nullptr, nullptr, &Status); } + if (!Undecorated && + (DecoratedLength >= 2 && strncmp(DecoratedStr, "_R", 2) == 0)) { + Undecorated = rustDemangle(DecoratedStr, nullptr, nullptr, &Status); + } + std::string Result(Undecorated ? Prefix + Undecorated : Mangled); free(Undecorated); return Result; @@ -154,9 +149,37 @@ static void demangleLine(llvm::raw_ostream &OS, StringRef Mangled, bool Split) { int main(int argc, char **argv) { InitLLVM X(argc, argv); + BumpPtrAllocator A; + StringSaver Saver(A); + CxxfiltOptTable Tbl; + ToolName = argv[0]; + opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, + [&](StringRef Msg) { error(Msg); }); + if (Args.hasArg(OPT_help)) { + Tbl.printHelp(outs(), + (Twine(ToolName) + " [options] <mangled>").str().c_str(), + "LLVM symbol undecoration tool"); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + return 0; + } + if (Args.hasArg(OPT_version)) { + outs() << ToolName << '\n'; + cl::PrintVersionMessage(); + return 0; + } + + // The default value depends on the default triple. Mach-O has symbols + // prefixed with "_", so strip by default. + if (opt::Arg *A = + Args.getLastArg(OPT_strip_underscore, OPT_no_strip_underscore)) + StripUnderscore = A->getOption().matches(OPT_strip_underscore); + else + StripUnderscore = Triple(sys::getProcessTriple()).isOSBinFormatMachO(); - cl::ParseCommandLineOptions(argc, argv, "llvm symbol undecoration tool\n"); + Types = Args.hasArg(OPT_types); + std::vector<std::string> Decorated = Args.getAllArgValues(OPT_INPUT); if (Decorated.empty()) for (std::string Mangled; std::getline(std::cin, Mangled);) demangleLine(llvm::outs(), Mangled, true); diff --git a/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp b/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp index b53a6364c89e..1e18e379f23c 100644 --- a/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp +++ b/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp @@ -11,10 +11,11 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" @@ -24,23 +25,33 @@ using namespace llvm; +cl::OptionCategory CXXMapCategory("CXX Map Options"); + cl::opt<std::string> OldSymbolFile(cl::Positional, cl::Required, - cl::desc("<symbol-file>")); + cl::desc("<symbol-file>"), + cl::cat(CXXMapCategory)); cl::opt<std::string> NewSymbolFile(cl::Positional, cl::Required, - cl::desc("<symbol-file>")); + cl::desc("<symbol-file>"), + cl::cat(CXXMapCategory)); cl::opt<std::string> RemappingFile("remapping-file", cl::Required, - cl::desc("Remapping file")); -cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile)); + cl::desc("Remapping file"), + cl::cat(CXXMapCategory)); +cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile), + cl::cat(CXXMapCategory)); cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file")); -cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename)); + cl::init("-"), cl::desc("Output file"), + cl::cat(CXXMapCategory)); +cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename), + cl::cat(CXXMapCategory)); cl::opt<bool> WarnAmbiguous( "Wambiguous", - cl::desc("Warn on equivalent symbols in the output symbol list")); + cl::desc("Warn on equivalent symbols in the output symbol list"), + cl::cat(CXXMapCategory)); cl::opt<bool> WarnIncomplete( "Wincomplete", - cl::desc("Warn on input symbols missing from output symbol list")); + cl::desc("Warn on input symbols missing from output symbol list"), + cl::cat(CXXMapCategory)); static void warn(Twine Message, Twine Whence = "", std::string Hint = "") { @@ -130,6 +141,7 @@ static void remapSymbols(MemoryBuffer &OldSymbolFile, int main(int argc, const char *argv[]) { InitLLVM X(argc, argv); + cl::HideUnrelatedOptions({&CXXMapCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "LLVM C++ mangled name remapper\n"); auto OldSymbolBufOrError = MemoryBuffer::getFileOrSTDIN(OldSymbolFile); @@ -145,7 +157,7 @@ int main(int argc, const char *argv[]) { exitWithErrorCode(RemappingBufOrError.getError(), RemappingFile); std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text); + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); if (EC) exitWithErrorCode(EC, OutputFilename); diff --git a/llvm/tools/llvm-diff/DiffConsumer.cpp b/llvm/tools/llvm-diff/DiffConsumer.cpp index 6228ff2bae98..a703f42f14c3 100644 --- a/llvm/tools/llvm-diff/DiffConsumer.cpp +++ b/llvm/tools/llvm-diff/DiffConsumer.cpp @@ -17,34 +17,33 @@ using namespace llvm; -static void ComputeNumbering(Function *F, DenseMap<Value*,unsigned> &Numbering){ +static void ComputeNumbering(const Function *F, + DenseMap<const Value *, unsigned> &Numbering) { unsigned IN = 0; // Arguments get the first numbers. - for (Function::arg_iterator - AI = F->arg_begin(), AE = F->arg_end(); AI != AE; ++AI) - if (!AI->hasName()) - Numbering[&*AI] = IN++; + for (const auto &Arg : F->args()) + if (!Arg.hasName()) + Numbering[&Arg] = IN++; // Walk the basic blocks in order. - for (Function::iterator FI = F->begin(), FE = F->end(); FI != FE; ++FI) { - if (!FI->hasName()) - Numbering[&*FI] = IN++; + for (const auto &Func : *F) { + if (!Func.hasName()) + Numbering[&Func] = IN++; // Walk the instructions in order. - for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ++BI) + for (const auto &BB : Func) // void instructions don't get numbers. - if (!BI->hasName() && !BI->getType()->isVoidTy()) - Numbering[&*BI] = IN++; + if (!BB.hasName() && !BB.getType()->isVoidTy()) + Numbering[&BB] = IN++; } assert(!Numbering.empty() && "asked for numbering but numbering was no-op"); } - void Consumer::anchor() { } -void DiffConsumer::printValue(Value *V, bool isL) { +void DiffConsumer::printValue(const Value *V, bool isL) { if (V->hasName()) { out << (isa<GlobalValue>(V) ? '@' : '%') << V->getName(); return; @@ -99,16 +98,16 @@ void DiffConsumer::header() { // Extra newline between functions. if (Differences) out << "\n"; - Function *L = cast<Function>(I->L); - Function *R = cast<Function>(I->R); + const Function *L = cast<Function>(I->L); + const Function *R = cast<Function>(I->R); if (L->getName() != R->getName()) out << "in function " << L->getName() << " / " << R->getName() << ":\n"; else out << "in function " << L->getName() << ":\n"; } else if (isa<BasicBlock>(I->L)) { - BasicBlock *L = cast<BasicBlock>(I->L); - BasicBlock *R = cast<BasicBlock>(I->R); + const BasicBlock *L = cast<BasicBlock>(I->L); + const BasicBlock *R = cast<BasicBlock>(I->R); if (L->hasName() && R->hasName() && L->getName() == R->getName()) out << " in block %" << L->getName() << ":\n"; else { @@ -139,7 +138,7 @@ bool DiffConsumer::hadDifferences() const { return Differences; } -void DiffConsumer::enterContext(Value *L, Value *R) { +void DiffConsumer::enterContext(const Value *L, const Value *R) { contexts.push_back(DiffContext(L, R)); Indent += 2; } diff --git a/llvm/tools/llvm-diff/DiffConsumer.h b/llvm/tools/llvm-diff/DiffConsumer.h index 6cb8f2eb7eeb..f7b2f2450eec 100644 --- a/llvm/tools/llvm-diff/DiffConsumer.h +++ b/llvm/tools/llvm-diff/DiffConsumer.h @@ -34,7 +34,7 @@ class StringRef; /// Right are IR "containers" of some sort which are being /// considered for structural equivalence: global variables, /// functions, blocks, instructions, etc. - virtual void enterContext(Value *Left, Value *Right) = 0; + virtual void enterContext(const Value *Left, const Value *Right) = 0; /// Record that a local context has been exited. virtual void exitContext() = 0; @@ -55,14 +55,14 @@ class StringRef; class DiffConsumer : public Consumer { private: struct DiffContext { - DiffContext(Value *L, Value *R) - : L(L), R(R), Differences(false), IsFunction(isa<Function>(L)) {} - Value *L; - Value *R; + DiffContext(const Value *L, const Value *R) + : L(L), R(R), Differences(false), IsFunction(isa<Function>(L)) {} + const Value *L; + const Value *R; bool Differences; bool IsFunction; - DenseMap<Value*,unsigned> LNumbering; - DenseMap<Value*,unsigned> RNumbering; + DenseMap<const Value *, unsigned> LNumbering; + DenseMap<const Value *, unsigned> RNumbering; }; raw_ostream &out; @@ -70,7 +70,7 @@ class StringRef; bool Differences; unsigned Indent; - void printValue(Value *V, bool isL); + void printValue(const Value *V, bool isL); void header(); void indent(); @@ -79,7 +79,7 @@ class StringRef; : out(errs()), Differences(false), Indent(0) {} bool hadDifferences() const; - void enterContext(Value *L, Value *R) override; + void enterContext(const Value *L, const Value *R) override; void exitContext() override; void log(StringRef text) override; void logf(const LogBuilder &Log) override; diff --git a/llvm/tools/llvm-diff/DiffLog.cpp b/llvm/tools/llvm-diff/DiffLog.cpp index 6484197521f2..d31a345d255c 100644 --- a/llvm/tools/llvm-diff/DiffLog.cpp +++ b/llvm/tools/llvm-diff/DiffLog.cpp @@ -24,18 +24,18 @@ LogBuilder::~LogBuilder() { StringRef LogBuilder::getFormat() const { return Format; } unsigned LogBuilder::getNumArguments() const { return Arguments.size(); } -Value *LogBuilder::getArgument(unsigned I) const { return Arguments[I]; } +const Value *LogBuilder::getArgument(unsigned I) const { return Arguments[I]; } DiffLogBuilder::~DiffLogBuilder() { consumer.logd(*this); } -void DiffLogBuilder::addMatch(Instruction *L, Instruction *R) { +void DiffLogBuilder::addMatch(const Instruction *L, const Instruction *R) { Diff.push_back(DiffRecord(L, R)); } -void DiffLogBuilder::addLeft(Instruction *L) { +void DiffLogBuilder::addLeft(const Instruction *L) { // HACK: VS 2010 has a bug in the stdlib that requires this. Diff.push_back(DiffRecord(L, DiffRecord::second_type(nullptr))); } -void DiffLogBuilder::addRight(Instruction *R) { +void DiffLogBuilder::addRight(const Instruction *R) { // HACK: VS 2010 has a bug in the stdlib that requires this. Diff.push_back(DiffRecord(DiffRecord::first_type(nullptr), R)); } @@ -46,5 +46,9 @@ DiffChange DiffLogBuilder::getLineKind(unsigned I) const { return (Diff[I].first ? (Diff[I].second ? DC_match : DC_left) : DC_right); } -Instruction *DiffLogBuilder::getLeft(unsigned I) const { return Diff[I].first; } -Instruction *DiffLogBuilder::getRight(unsigned I) const { return Diff[I].second; } +const Instruction *DiffLogBuilder::getLeft(unsigned I) const { + return Diff[I].first; +} +const Instruction *DiffLogBuilder::getRight(unsigned I) const { + return Diff[I].second; +} diff --git a/llvm/tools/llvm-diff/DiffLog.h b/llvm/tools/llvm-diff/DiffLog.h index 0c8952496155..d8b07b971198 100644 --- a/llvm/tools/llvm-diff/DiffLog.h +++ b/llvm/tools/llvm-diff/DiffLog.h @@ -34,7 +34,7 @@ namespace llvm { /// might be initializing this format. StringRef Format; - SmallVector<Value*, 4> Arguments; + SmallVector<const Value *, 4> Arguments; public: LogBuilder(Consumer &c, StringRef Format) : consumer(&c), Format(Format) {} @@ -44,7 +44,7 @@ namespace llvm { L.consumer = nullptr; } - LogBuilder &operator<<(Value *V) { + LogBuilder &operator<<(const Value *V) { Arguments.push_back(V); return *this; } @@ -53,12 +53,12 @@ namespace llvm { StringRef getFormat() const; unsigned getNumArguments() const; - Value *getArgument(unsigned I) const; + const Value *getArgument(unsigned I) const; }; /// A temporary-object class for building up diff messages. class DiffLogBuilder { - typedef std::pair<Instruction*,Instruction*> DiffRecord; + typedef std::pair<const Instruction *, const Instruction *> DiffRecord; SmallVector<DiffRecord, 20> Diff; Consumer &consumer; @@ -67,15 +67,15 @@ namespace llvm { DiffLogBuilder(Consumer &c) : consumer(c) {} ~DiffLogBuilder(); - void addMatch(Instruction *L, Instruction *R); + void addMatch(const Instruction *L, const Instruction *R); // HACK: VS 2010 has a bug in the stdlib that requires this. - void addLeft(Instruction *L); - void addRight(Instruction *R); + void addLeft(const Instruction *L); + void addRight(const Instruction *R); unsigned getNumLines() const; DiffChange getLineKind(unsigned I) const; - Instruction *getLeft(unsigned I) const; - Instruction *getRight(unsigned I) const; + const Instruction *getLeft(unsigned I) const; + const Instruction *getRight(unsigned I) const; }; } diff --git a/llvm/tools/llvm-diff/DifferenceEngine.cpp b/llvm/tools/llvm-diff/DifferenceEngine.cpp index 64c0dc61e806..eb746cd2a865 100644 --- a/llvm/tools/llvm-diff/DifferenceEngine.cpp +++ b/llvm/tools/llvm-diff/DifferenceEngine.cpp @@ -113,22 +113,29 @@ public: class FunctionDifferenceEngine { DifferenceEngine &Engine; + // Some initializers may reference the variable we're currently checking. This + // can cause an infinite loop. The Saved[LR]HS ivars can be checked to prevent + // recursing. + const Value *SavedLHS; + const Value *SavedRHS; + /// The current mapping from old local values to new local values. - DenseMap<Value*, Value*> Values; + DenseMap<const Value *, const Value *> Values; /// The current mapping from old blocks to new blocks. - DenseMap<BasicBlock*, BasicBlock*> Blocks; + DenseMap<const BasicBlock *, const BasicBlock *> Blocks; - DenseSet<std::pair<Value*, Value*> > TentativeValues; + DenseSet<std::pair<const Value *, const Value *>> TentativeValues; - unsigned getUnprocPredCount(BasicBlock *Block) const { + unsigned getUnprocPredCount(const BasicBlock *Block) const { unsigned Count = 0; - for (pred_iterator I = pred_begin(Block), E = pred_end(Block); I != E; ++I) + for (const_pred_iterator I = pred_begin(Block), E = pred_end(Block); I != E; + ++I) if (!Blocks.count(*I)) Count++; return Count; } - typedef std::pair<BasicBlock*, BasicBlock*> BlockPair; + typedef std::pair<const BasicBlock *, const BasicBlock *> BlockPair; /// A type which sorts a priority queue by the number of unprocessed /// predecessor blocks it has remaining. @@ -138,7 +145,7 @@ class FunctionDifferenceEngine { const FunctionDifferenceEngine &fde; explicit QueueSorter(const FunctionDifferenceEngine &fde) : fde(fde) {} - bool operator()(const BlockPair &Old, const BlockPair &New) { + bool operator()(BlockPair &Old, BlockPair &New) { return fde.getUnprocPredCount(Old.first) < fde.getUnprocPredCount(New.first); } @@ -151,8 +158,8 @@ class FunctionDifferenceEngine { /// if they haven't already been processed. /// /// Returns true if there was a problem unifying them. - bool tryUnify(BasicBlock *L, BasicBlock *R) { - BasicBlock *&Ref = Blocks[L]; + bool tryUnify(const BasicBlock *L, const BasicBlock *R) { + const BasicBlock *&Ref = Blocks[L]; if (Ref) { if (Ref == R) return false; @@ -167,10 +174,10 @@ class FunctionDifferenceEngine { Queue.insert(BlockPair(L, R)); return false; } - + /// Unifies two instructions, given that they're known not to have /// structural differences. - void unify(Instruction *L, Instruction *R) { + void unify(const Instruction *L, const Instruction *R) { DifferenceEngine::Context C(Engine, L, R); bool Result = diff(L, R, true, true); @@ -187,15 +194,15 @@ class FunctionDifferenceEngine { } } - void diff(BasicBlock *L, BasicBlock *R) { + void diff(const BasicBlock *L, const BasicBlock *R) { DifferenceEngine::Context C(Engine, L, R); - BasicBlock::iterator LI = L->begin(), LE = L->end(); - BasicBlock::iterator RI = R->begin(); + BasicBlock::const_iterator LI = L->begin(), LE = L->end(); + BasicBlock::const_iterator RI = R->begin(); do { assert(LI != LE && RI != R->end()); - Instruction *LeftI = &*LI, *RightI = &*RI; + const Instruction *LeftI = &*LI, *RightI = &*RI; // If the instructions differ, start the more sophisticated diff // algorithm at the start of the block. @@ -219,10 +226,11 @@ class FunctionDifferenceEngine { unify(&*LI, &*RI); } - bool matchForBlockDiff(Instruction *L, Instruction *R); - void runBlockDiff(BasicBlock::iterator LI, BasicBlock::iterator RI); + bool matchForBlockDiff(const Instruction *L, const Instruction *R); + void runBlockDiff(BasicBlock::const_iterator LI, + BasicBlock::const_iterator RI); - bool diffCallSites(CallBase &L, CallBase &R, bool Complain) { + bool diffCallSites(const CallBase &L, const CallBase &R, bool Complain) { // FIXME: call attributes if (!equivalentAsOperands(L.getCalledOperand(), R.getCalledOperand())) { if (Complain) Engine.log("called functions differ"); @@ -242,7 +250,8 @@ class FunctionDifferenceEngine { return false; } - bool diff(Instruction *L, Instruction *R, bool Complain, bool TryUnify) { + bool diff(const Instruction *L, const Instruction *R, bool Complain, + bool TryUnify) { // FIXME: metadata (if Complain is set) // Different opcodes always imply different operations. @@ -273,8 +282,8 @@ class FunctionDifferenceEngine { // Terminators. } else if (isa<InvokeInst>(L)) { - InvokeInst &LI = cast<InvokeInst>(*L); - InvokeInst &RI = cast<InvokeInst>(*R); + const InvokeInst &LI = cast<InvokeInst>(*L); + const InvokeInst &RI = cast<InvokeInst>(*R); if (diffCallSites(LI, RI, Complain)) return true; @@ -284,9 +293,30 @@ class FunctionDifferenceEngine { } return false; + } else if (isa<CallBrInst>(L)) { + const CallBrInst &LI = cast<CallBrInst>(*L); + const CallBrInst &RI = cast<CallBrInst>(*R); + if (LI.getNumIndirectDests() != RI.getNumIndirectDests()) { + if (Complain) + Engine.log("callbr # of indirect destinations differ"); + return true; + } + + // Perform the "try unify" step so that we can equate the indirect + // destinations before checking the call site. + for (unsigned I = 0; I < LI.getNumIndirectDests(); I++) + tryUnify(LI.getIndirectDest(I), RI.getIndirectDest(I)); + + if (diffCallSites(LI, RI, Complain)) + return true; + + if (TryUnify) + tryUnify(LI.getDefaultDest(), RI.getDefaultDest()); + return false; + } else if (isa<BranchInst>(L)) { - BranchInst *LI = cast<BranchInst>(L); - BranchInst *RI = cast<BranchInst>(R); + const BranchInst *LI = cast<BranchInst>(L); + const BranchInst *RI = cast<BranchInst>(R); if (LI->isConditional() != RI->isConditional()) { if (Complain) Engine.log("branch conditionality differs"); return true; @@ -303,8 +333,8 @@ class FunctionDifferenceEngine { return false; } else if (isa<IndirectBrInst>(L)) { - IndirectBrInst *LI = cast<IndirectBrInst>(L); - IndirectBrInst *RI = cast<IndirectBrInst>(R); + const IndirectBrInst *LI = cast<IndirectBrInst>(L); + const IndirectBrInst *RI = cast<IndirectBrInst>(R); if (LI->getNumDestinations() != RI->getNumDestinations()) { if (Complain) Engine.log("indirectbr # of destinations differ"); return true; @@ -323,8 +353,8 @@ class FunctionDifferenceEngine { return false; } else if (isa<SwitchInst>(L)) { - SwitchInst *LI = cast<SwitchInst>(L); - SwitchInst *RI = cast<SwitchInst>(R); + const SwitchInst *LI = cast<SwitchInst>(L); + const SwitchInst *RI = cast<SwitchInst>(R); if (!equivalentAsOperands(LI->getCondition(), RI->getCondition())) { if (Complain) Engine.log("switch conditions differ"); return true; @@ -333,13 +363,13 @@ class FunctionDifferenceEngine { bool Difference = false; - DenseMap<ConstantInt*,BasicBlock*> LCases; + DenseMap<const ConstantInt *, const BasicBlock *> LCases; for (auto Case : LI->cases()) LCases[Case.getCaseValue()] = Case.getCaseSuccessor(); for (auto Case : RI->cases()) { - ConstantInt *CaseValue = Case.getCaseValue(); - BasicBlock *LCase = LCases[CaseValue]; + const ConstantInt *CaseValue = Case.getCaseValue(); + const BasicBlock *LCase = LCases[CaseValue]; if (LCase) { if (TryUnify) tryUnify(LCase, Case.getCaseSuccessor()); @@ -351,8 +381,10 @@ class FunctionDifferenceEngine { } } if (!Difference) - for (DenseMap<ConstantInt*,BasicBlock*>::iterator - I = LCases.begin(), E = LCases.end(); I != E; ++I) { + for (DenseMap<const ConstantInt *, const BasicBlock *>::iterator + I = LCases.begin(), + E = LCases.end(); + I != E; ++I) { if (Complain) Engine.logf("left switch has extra case %l") << I->first; Difference = true; @@ -378,14 +410,15 @@ class FunctionDifferenceEngine { return false; } - bool equivalentAsOperands(Constant *L, Constant *R) { +public: + bool equivalentAsOperands(const Constant *L, const Constant *R) { // Use equality as a preliminary filter. if (L == R) return true; if (L->getValueID() != R->getValueID()) return false; - + // Ask the engine about global values. if (isa<GlobalValue>(L)) return Engine.equivalentAsOperands(cast<GlobalValue>(L), @@ -409,8 +442,8 @@ class FunctionDifferenceEngine { // If L and R are ConstantVectors, compare each element if (isa<ConstantVector>(L)) { - ConstantVector *CVL = cast<ConstantVector>(L); - ConstantVector *CVR = cast<ConstantVector>(R); + const ConstantVector *CVL = cast<ConstantVector>(L); + const ConstantVector *CVR = cast<ConstantVector>(R); if (CVL->getType()->getNumElements() != CVR->getType()->getNumElements()) return false; for (unsigned i = 0; i < CVL->getType()->getNumElements(); i++) { @@ -420,12 +453,68 @@ class FunctionDifferenceEngine { return true; } + // If L and R are ConstantArrays, compare the element count and types. + if (isa<ConstantArray>(L)) { + const ConstantArray *CAL = cast<ConstantArray>(L); + const ConstantArray *CAR = cast<ConstantArray>(R); + // Sometimes a type may be equivalent, but not uniquified---e.g. it may + // contain a GEP instruction. Do a deeper comparison of the types. + if (CAL->getType()->getNumElements() != CAR->getType()->getNumElements()) + return false; + + for (unsigned I = 0; I < CAL->getType()->getNumElements(); ++I) { + if (!equivalentAsOperands(CAL->getAggregateElement(I), + CAR->getAggregateElement(I))) + return false; + } + + return true; + } + + // If L and R are ConstantStructs, compare each field and type. + if (isa<ConstantStruct>(L)) { + const ConstantStruct *CSL = cast<ConstantStruct>(L); + const ConstantStruct *CSR = cast<ConstantStruct>(R); + + const StructType *LTy = cast<StructType>(CSL->getType()); + const StructType *RTy = cast<StructType>(CSR->getType()); + + // The StructTypes should have the same attributes. Don't use + // isLayoutIdentical(), because that just checks the element pointers, + // which may not work here. + if (LTy->getNumElements() != RTy->getNumElements() || + LTy->isPacked() != RTy->isPacked()) + return false; + + for (unsigned I = 0; I < LTy->getNumElements(); I++) { + const Value *LAgg = CSL->getAggregateElement(I); + const Value *RAgg = CSR->getAggregateElement(I); + + if (LAgg == SavedLHS || RAgg == SavedRHS) { + if (LAgg != SavedLHS || RAgg != SavedRHS) + // If the left and right operands aren't both re-analyzing the + // variable, then the initialiers don't match, so report "false". + // Otherwise, we skip these operands.. + return false; + + continue; + } + + if (!equivalentAsOperands(LAgg, RAgg)) { + return false; + } + } + + return true; + } + return false; } - bool equivalentAsOperands(ConstantExpr *L, ConstantExpr *R) { + bool equivalentAsOperands(const ConstantExpr *L, const ConstantExpr *R) { if (L == R) return true; + if (L->getOpcode() != R->getOpcode()) return false; @@ -447,14 +536,28 @@ class FunctionDifferenceEngine { if (L->getNumOperands() != R->getNumOperands()) return false; - for (unsigned I = 0, E = L->getNumOperands(); I != E; ++I) - if (!equivalentAsOperands(L->getOperand(I), R->getOperand(I))) + for (unsigned I = 0, E = L->getNumOperands(); I != E; ++I) { + const auto *LOp = L->getOperand(I); + const auto *ROp = R->getOperand(I); + + if (LOp == SavedLHS || ROp == SavedRHS) { + if (LOp != SavedLHS || ROp != SavedRHS) + // If the left and right operands aren't both re-analyzing the + // variable, then the initialiers don't match, so report "false". + // Otherwise, we skip these operands.. + return false; + + continue; + } + + if (!equivalentAsOperands(LOp, ROp)) return false; + } return true; } - bool equivalentAsOperands(Value *L, Value *R) { + bool equivalentAsOperands(const Value *L, const Value *R) { // Fall out if the values have different kind. // This possibly shouldn't take priority over oracles. if (L->getValueID() != R->getValueID()) @@ -483,17 +586,19 @@ class FunctionDifferenceEngine { FunctionDifferenceEngine *this_() { return this; } public: - FunctionDifferenceEngine(DifferenceEngine &Engine) : - Engine(Engine), Queue(QueueSorter(*this_())) {} + FunctionDifferenceEngine(DifferenceEngine &Engine, + const Value *SavedLHS = nullptr, + const Value *SavedRHS = nullptr) + : Engine(Engine), SavedLHS(SavedLHS), SavedRHS(SavedRHS), + Queue(QueueSorter(*this_())) {} - void diff(Function *L, Function *R) { + void diff(const Function *L, const Function *R) { if (L->arg_size() != R->arg_size()) Engine.log("different argument counts"); // Map the arguments. - for (Function::arg_iterator - LI = L->arg_begin(), LE = L->arg_end(), - RI = R->arg_begin(), RE = R->arg_end(); + for (Function::const_arg_iterator LI = L->arg_begin(), LE = L->arg_end(), + RI = R->arg_begin(), RE = R->arg_end(); LI != LE && RI != RE; ++LI, ++RI) Values[&*LI] = &*RI; @@ -509,15 +614,15 @@ struct DiffEntry { llvm::SmallVector<char, 8> Path; // actually of DifferenceEngine::DiffChange }; -bool FunctionDifferenceEngine::matchForBlockDiff(Instruction *L, - Instruction *R) { +bool FunctionDifferenceEngine::matchForBlockDiff(const Instruction *L, + const Instruction *R) { return !diff(L, R, false, false); } -void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, - BasicBlock::iterator RStart) { - BasicBlock::iterator LE = LStart->getParent()->end(); - BasicBlock::iterator RE = RStart->getParent()->end(); +void FunctionDifferenceEngine::runBlockDiff(BasicBlock::const_iterator LStart, + BasicBlock::const_iterator RStart) { + BasicBlock::const_iterator LE = LStart->getParent()->end(); + BasicBlock::const_iterator RE = RStart->getParent()->end(); unsigned NL = std::distance(LStart, LE); @@ -540,14 +645,14 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, Cur[I].Path.push_back(DC_left); } - for (BasicBlock::iterator RI = RStart; RI != RE; ++RI) { + for (BasicBlock::const_iterator RI = RStart; RI != RE; ++RI) { // Initialize the first row. Next[0] = Cur[0]; Next[0].Cost += RightCost; Next[0].Path.push_back(DC_right); unsigned Index = 1; - for (BasicBlock::iterator LI = LStart; LI != LE; ++LI, ++Index) { + for (BasicBlock::const_iterator LI = LStart; LI != LE; ++LI, ++Index) { if (matchForBlockDiff(&*LI, &*RI)) { Next[Index] = Cur[Index-1]; Next[Index].Cost += MatchCost; @@ -572,7 +677,7 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, TentativeValues.clear(); SmallVectorImpl<char> &Path = Cur[NL].Path; - BasicBlock::iterator LI = LStart, RI = RStart; + BasicBlock::const_iterator LI = LStart, RI = RStart; DiffLogBuilder Diff(Engine.getConsumer()); @@ -595,7 +700,7 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, case DC_match: assert(LI != LE && RI != RE); { - Instruction *L = &*LI, *R = &*RI; + const Instruction *L = &*LI, *R = &*RI; unify(L, R); Diff.addMatch(L, R); } @@ -628,16 +733,16 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, // If the terminators have different kinds, but one is an invoke and the // other is an unconditional branch immediately following a call, unify // the results and the destinations. - Instruction *LTerm = LStart->getParent()->getTerminator(); - Instruction *RTerm = RStart->getParent()->getTerminator(); + const Instruction *LTerm = LStart->getParent()->getTerminator(); + const Instruction *RTerm = RStart->getParent()->getTerminator(); if (isa<BranchInst>(LTerm) && isa<InvokeInst>(RTerm)) { if (cast<BranchInst>(LTerm)->isConditional()) return; - BasicBlock::iterator I = LTerm->getIterator(); + BasicBlock::const_iterator I = LTerm->getIterator(); if (I == LStart->getParent()->begin()) return; --I; if (!isa<CallInst>(*I)) return; - CallInst *LCall = cast<CallInst>(&*I); - InvokeInst *RInvoke = cast<InvokeInst>(RTerm); + const CallInst *LCall = cast<CallInst>(&*I); + const InvokeInst *RInvoke = cast<InvokeInst>(RTerm); if (!equivalentAsOperands(LCall->getCalledOperand(), RInvoke->getCalledOperand())) return; @@ -646,12 +751,12 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, tryUnify(LTerm->getSuccessor(0), RInvoke->getNormalDest()); } else if (isa<InvokeInst>(LTerm) && isa<BranchInst>(RTerm)) { if (cast<BranchInst>(RTerm)->isConditional()) return; - BasicBlock::iterator I = RTerm->getIterator(); + BasicBlock::const_iterator I = RTerm->getIterator(); if (I == RStart->getParent()->begin()) return; --I; if (!isa<CallInst>(*I)) return; - CallInst *RCall = cast<CallInst>(I); - InvokeInst *LInvoke = cast<InvokeInst>(LTerm); + const CallInst *RCall = cast<CallInst>(I); + const InvokeInst *LInvoke = cast<InvokeInst>(LTerm); if (!equivalentAsOperands(LInvoke->getCalledOperand(), RCall->getCalledOperand())) return; @@ -660,12 +765,11 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, tryUnify(LInvoke->getNormalDest(), RTerm->getSuccessor(0)); } } - } void DifferenceEngine::Oracle::anchor() { } -void DifferenceEngine::diff(Function *L, Function *R) { +void DifferenceEngine::diff(const Function *L, const Function *R) { Context C(*this, L, R); // FIXME: types @@ -683,15 +787,15 @@ void DifferenceEngine::diff(Function *L, Function *R) { FunctionDifferenceEngine(*this).diff(L, R); } -void DifferenceEngine::diff(Module *L, Module *R) { +void DifferenceEngine::diff(const Module *L, const Module *R) { StringSet<> LNames; - SmallVector<std::pair<Function*,Function*>, 20> Queue; + SmallVector<std::pair<const Function *, const Function *>, 20> Queue; unsigned LeftAnonCount = 0; unsigned RightAnonCount = 0; - for (Module::iterator I = L->begin(), E = L->end(); I != E; ++I) { - Function *LFn = &*I; + for (Module::const_iterator I = L->begin(), E = L->end(); I != E; ++I) { + const Function *LFn = &*I; StringRef Name = LFn->getName(); if (Name.empty()) { ++LeftAnonCount; @@ -706,8 +810,8 @@ void DifferenceEngine::diff(Module *L, Module *R) { logf("function %l exists only in left module") << LFn; } - for (Module::iterator I = R->begin(), E = R->end(); I != E; ++I) { - Function *RFn = &*I; + for (Module::const_iterator I = R->begin(), E = R->end(); I != E; ++I) { + const Function *RFn = &*I; StringRef Name = RFn->getName(); if (Name.empty()) { ++RightAnonCount; @@ -718,7 +822,6 @@ void DifferenceEngine::diff(Module *L, Module *R) { logf("function %r exists only in right module") << RFn; } - if (LeftAnonCount != 0 || RightAnonCount != 0) { SmallString<32> Tmp; logf(("not comparing " + Twine(LeftAnonCount) + @@ -727,20 +830,24 @@ void DifferenceEngine::diff(Module *L, Module *R) { .toStringRef(Tmp)); } - for (SmallVectorImpl<std::pair<Function*,Function*> >::iterator - I = Queue.begin(), E = Queue.end(); I != E; ++I) + for (SmallVectorImpl<std::pair<const Function *, const Function *>>::iterator + I = Queue.begin(), + E = Queue.end(); + I != E; ++I) diff(I->first, I->second); } -bool DifferenceEngine::equivalentAsOperands(GlobalValue *L, GlobalValue *R) { +bool DifferenceEngine::equivalentAsOperands(const GlobalValue *L, + const GlobalValue *R) { if (globalValueOracle) return (*globalValueOracle)(L, R); if (isa<GlobalVariable>(L) && isa<GlobalVariable>(R)) { - GlobalVariable *GVL = cast<GlobalVariable>(L); - GlobalVariable *GVR = cast<GlobalVariable>(R); + const GlobalVariable *GVL = cast<GlobalVariable>(L); + const GlobalVariable *GVR = cast<GlobalVariable>(R); if (GVL->hasLocalLinkage() && GVL->hasUniqueInitializer() && GVR->hasLocalLinkage() && GVR->hasUniqueInitializer()) - return GVL->getInitializer() == GVR->getInitializer(); + return FunctionDifferenceEngine(*this, GVL, GVR) + .equivalentAsOperands(GVL->getInitializer(), GVR->getInitializer()); } return L->getName() == R->getName(); diff --git a/llvm/tools/llvm-diff/DifferenceEngine.h b/llvm/tools/llvm-diff/DifferenceEngine.h index da1b6526a6e2..436a35566360 100644 --- a/llvm/tools/llvm-diff/DifferenceEngine.h +++ b/llvm/tools/llvm-diff/DifferenceEngine.h @@ -33,7 +33,8 @@ namespace llvm { public: /// A RAII object for recording the current context. struct Context { - Context(DifferenceEngine &Engine, Value *L, Value *R) : Engine(Engine) { + Context(DifferenceEngine &Engine, const Value *L, const Value *R) + : Engine(Engine) { Engine.consumer.enterContext(L, R); } @@ -50,7 +51,7 @@ namespace llvm { class Oracle { virtual void anchor(); public: - virtual bool operator()(Value *L, Value *R) = 0; + virtual bool operator()(const Value *L, const Value *R) = 0; protected: virtual ~Oracle() {} @@ -59,8 +60,8 @@ namespace llvm { DifferenceEngine(Consumer &consumer) : consumer(consumer), globalValueOracle(nullptr) {} - void diff(Module *L, Module *R); - void diff(Function *L, Function *R); + void diff(const Module *L, const Module *R); + void diff(const Function *L, const Function *R); void log(StringRef text) { consumer.log(text); } @@ -78,7 +79,7 @@ namespace llvm { } /// Determines whether two global values are equivalent. - bool equivalentAsOperands(GlobalValue *L, GlobalValue *R); + bool equivalentAsOperands(const GlobalValue *L, const GlobalValue *R); private: Consumer &consumer; diff --git a/llvm/tools/llvm-diff/llvm-diff.cpp b/llvm/tools/llvm-diff/llvm-diff.cpp index aaf7989e2e3d..8a11179e741e 100644 --- a/llvm/tools/llvm-diff/llvm-diff.cpp +++ b/llvm/tools/llvm-diff/llvm-diff.cpp @@ -55,16 +55,20 @@ static void diffGlobal(DifferenceEngine &Engine, Module &L, Module &R, errs() << "No function named @" << Name << " in right module\n"; } +cl::OptionCategory DiffCategory("Diff Options"); + static cl::opt<std::string> LeftFilename(cl::Positional, - cl::desc("<first file>"), - cl::Required); + cl::desc("<first file>"), cl::Required, + cl::cat(DiffCategory)); static cl::opt<std::string> RightFilename(cl::Positional, cl::desc("<second file>"), - cl::Required); + cl::Required, cl::cat(DiffCategory)); static cl::list<std::string> GlobalsToCompare(cl::Positional, - cl::desc("<globals to compare>")); + cl::desc("<globals to compare>"), + cl::cat(DiffCategory)); int main(int argc, char **argv) { + cl::HideUnrelatedOptions({&DiffCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv); LLVMContext Context; diff --git a/llvm/tools/llvm-dis/llvm-dis.cpp b/llvm/tools/llvm-dis/llvm-dis.cpp index 5d609468a380..62d165fb8413 100644 --- a/llvm/tools/llvm-dis/llvm-dis.cpp +++ b/llvm/tools/llvm-dis/llvm-dis.cpp @@ -35,37 +35,44 @@ #include <system_error> using namespace llvm; -static cl::opt<std::string> -InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); +static cl::OptionCategory DisCategory("Disassembler Options"); -static cl::opt<std::string> -OutputFilename("o", cl::desc("Override output filename"), - cl::value_desc("filename")); +static cl::list<std::string> InputFilenames(cl::Positional, cl::ZeroOrMore, + cl::desc("[input bitcode]..."), + cl::cat(DisCategory)); -static cl::opt<bool> -Force("f", cl::desc("Enable binary output on terminals")); +static cl::opt<std::string> OutputFilename("o", + cl::desc("Override output filename"), + cl::value_desc("filename"), + cl::cat(DisCategory)); -static cl::opt<bool> -DontPrint("disable-output", cl::desc("Don't output the .ll file"), cl::Hidden); +static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"), + cl::cat(DisCategory)); + +static cl::opt<bool> DontPrint("disable-output", + cl::desc("Don't output the .ll file"), + cl::Hidden, cl::cat(DisCategory)); static cl::opt<bool> SetImporting("set-importing", cl::desc("Set lazy loading to pretend to import a module"), - cl::Hidden); + cl::Hidden, cl::cat(DisCategory)); static cl::opt<bool> ShowAnnotations("show-annotations", - cl::desc("Add informational comments to the .ll file")); + cl::desc("Add informational comments to the .ll file"), + cl::cat(DisCategory)); static cl::opt<bool> PreserveAssemblyUseListOrder( "preserve-ll-uselistorder", cl::desc("Preserve use-list order when writing LLVM assembly."), - cl::init(false), cl::Hidden); + cl::init(false), cl::Hidden, cl::cat(DisCategory)); static cl::opt<bool> MaterializeMetadata("materialize-metadata", cl::desc("Load module without materializing metadata, " - "then materialize only the metadata")); + "then materialize only the metadata"), + cl::cat(DisCategory)); namespace { @@ -151,77 +158,89 @@ int main(int argc, char **argv) { ExitOnErr.setBanner(std::string(argv[0]) + ": error: "); + cl::HideUnrelatedOptions({&DisCategory, &getColorCategory()}); + cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n"); + LLVMContext Context; Context.setDiagnosticHandler( std::make_unique<LLVMDisDiagnosticHandler>(argv[0])); - cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n"); - std::unique_ptr<MemoryBuffer> MB = - ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename))); + if (InputFilenames.size() < 1) { + InputFilenames.push_back("-"); + } else if (InputFilenames.size() > 1 && !OutputFilename.empty()) { + errs() + << "error: output file name cannot be set for multiple input files\n"; + return 1; + } + + for (std::string InputFilename : InputFilenames) { + std::unique_ptr<MemoryBuffer> MB = ExitOnErr( + errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename))); - BitcodeFileContents IF = ExitOnErr(llvm::getBitcodeFileContents(*MB)); + BitcodeFileContents IF = ExitOnErr(llvm::getBitcodeFileContents(*MB)); - const size_t N = IF.Mods.size(); + const size_t N = IF.Mods.size(); - if (OutputFilename == "-" && N > 1) + if (OutputFilename == "-" && N > 1) errs() << "only single module bitcode files can be written to stdout\n"; - for (size_t i = 0; i < N; ++i) { - BitcodeModule MB = IF.Mods[i]; - std::unique_ptr<Module> M = ExitOnErr(MB.getLazyModule(Context, MaterializeMetadata, - SetImporting)); - if (MaterializeMetadata) - ExitOnErr(M->materializeMetadata()); - else - ExitOnErr(M->materializeAll()); - - BitcodeLTOInfo LTOInfo = ExitOnErr(MB.getLTOInfo()); - std::unique_ptr<ModuleSummaryIndex> Index; - if (LTOInfo.HasSummary) - Index = ExitOnErr(MB.getSummary()); + for (size_t I = 0; I < N; ++I) { + BitcodeModule MB = IF.Mods[I]; + std::unique_ptr<Module> M = ExitOnErr( + MB.getLazyModule(Context, MaterializeMetadata, SetImporting)); + if (MaterializeMetadata) + ExitOnErr(M->materializeMetadata()); + else + ExitOnErr(M->materializeAll()); - std::string FinalFilename(OutputFilename); + BitcodeLTOInfo LTOInfo = ExitOnErr(MB.getLTOInfo()); + std::unique_ptr<ModuleSummaryIndex> Index; + if (LTOInfo.HasSummary) + Index = ExitOnErr(MB.getSummary()); - // Just use stdout. We won't actually print anything on it. - if (DontPrint) - FinalFilename = "-"; + std::string FinalFilename(OutputFilename); - if (FinalFilename.empty()) { // Unspecified output, infer it. - if (InputFilename == "-") { + // Just use stdout. We won't actually print anything on it. + if (DontPrint) FinalFilename = "-"; + + if (FinalFilename.empty()) { // Unspecified output, infer it. + if (InputFilename == "-") { + FinalFilename = "-"; + } else { + StringRef IFN = InputFilename; + FinalFilename = (IFN.endswith(".bc") ? IFN.drop_back(3) : IFN).str(); + if (N > 1) + FinalFilename += std::string(".") + std::to_string(I); + FinalFilename += ".ll"; + } } else { - StringRef IFN = InputFilename; - FinalFilename = (IFN.endswith(".bc") ? IFN.drop_back(3) : IFN).str(); if (N > 1) - FinalFilename += std::string(".") + std::to_string(i); - FinalFilename += ".ll"; + FinalFilename += std::string(".") + std::to_string(I); } - } else { - if (N > 1) - FinalFilename += std::string(".") + std::to_string(i); - } - std::error_code EC; - std::unique_ptr<ToolOutputFile> Out( - new ToolOutputFile(FinalFilename, EC, sys::fs::OF_Text)); - if (EC) { - errs() << EC.message() << '\n'; - return 1; - } + std::error_code EC; + std::unique_ptr<ToolOutputFile> Out( + new ToolOutputFile(FinalFilename, EC, sys::fs::OF_TextWithCRLF)); + if (EC) { + errs() << EC.message() << '\n'; + return 1; + } - std::unique_ptr<AssemblyAnnotationWriter> Annotator; - if (ShowAnnotations) - Annotator.reset(new CommentWriter()); + std::unique_ptr<AssemblyAnnotationWriter> Annotator; + if (ShowAnnotations) + Annotator.reset(new CommentWriter()); - // All that llvm-dis does is write the assembly to a file. - if (!DontPrint) { - M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder); - if (Index) - Index->print(Out->os()); - } + // All that llvm-dis does is write the assembly to a file. + if (!DontPrint) { + M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder); + if (Index) + Index->print(Out->os()); + } - // Declare success. - Out->keep(); + // Declare success. + Out->keep(); + } } return 0; diff --git a/llvm/tools/llvm-dwarfdump/SectionSizes.cpp b/llvm/tools/llvm-dwarfdump/SectionSizes.cpp index 8c456d50baa7..731bf05fd758 100644 --- a/llvm/tools/llvm-dwarfdump/SectionSizes.cpp +++ b/llvm/tools/llvm-dwarfdump/SectionSizes.cpp @@ -18,10 +18,8 @@ static size_t getNameColumnWidth(const SectionSizes &Sizes, const StringRef SectionNameTitle) { // The minimum column width should be the size of "SECTION". size_t Width = SectionNameTitle.size(); - for (const auto &DebugSec : Sizes.DebugSectionSizes) { - StringRef SectionName = DebugSec.getKey(); - Width = std::max(Width, SectionName.size()); - } + for (const auto &It : Sizes.DebugSectionSizes) + Width = std::max(Width, It.first.size()); return Width; } @@ -29,8 +27,8 @@ static size_t getSizeColumnWidth(const SectionSizes &Sizes, const StringRef SectionSizeTitle) { // The minimum column width should be the size of the column title. size_t Width = SectionSizeTitle.size(); - for (const auto &DebugSec : Sizes.DebugSectionSizes) { - size_t NumWidth = std::to_string(DebugSec.getValue()).size(); + for (const auto &It : Sizes.DebugSectionSizes) { + size_t NumWidth = std::to_string(It.second).size(); Width = std::max(Width, NumWidth); } return Width; @@ -59,13 +57,13 @@ static void prettyPrintSectionSizes(const ObjectFile &Obj, OS << "-"; OS << '\n'; - for (const auto &DebugSec : Sizes.DebugSectionSizes) { - OS << left_justify(DebugSec.getKey(), NameColWidth) << " "; + for (const auto &It : Sizes.DebugSectionSizes) { + OS << left_justify(It.first, NameColWidth) << " "; - auto NumBytes = std::to_string(DebugSec.getValue()); + std::string NumBytes = std::to_string(It.second); OS << right_justify(NumBytes, SizeColWidth) << " (" - << format("%0.2f", DebugSec.getValue() / - static_cast<double>(Sizes.TotalObjectSize) * 100) + << format("%0.2f", + It.second / static_cast<double>(Sizes.TotalObjectSize) * 100) << "%)\n"; } @@ -95,11 +93,11 @@ void dwarfdump::calculateSectionSizes(const ObjectFile &Obj, LLVM_DEBUG(dbgs() << SectionName.str() << ": " << Section.getSize() << '\n'); - if (!Section.isDebugSection(SectionName)) + if (!Section.isDebugSection()) continue; Sizes.TotalDebugSectionsSize += Section.getSize(); - Sizes.DebugSectionSizes[SectionName] += Section.getSize(); + Sizes.DebugSectionSizes[std::string(SectionName)] += Section.getSize(); } } diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp index 82da06eab1d6..19a971afa311 100644 --- a/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -8,9 +8,7 @@ #include "llvm-dwarfdump.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" -#include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/Object/ObjectFile.h" @@ -21,13 +19,23 @@ using namespace llvm; using namespace llvm::dwarfdump; using namespace llvm::object; +namespace { /// This represents the number of categories of debug location coverage being /// calculated. The first category is the number of variables with 0% location /// coverage, but the last category is the number of variables with 100% /// location coverage. constexpr int NumOfCoverageCategories = 12; -namespace { +/// This is used for zero location coverage bucket. +constexpr unsigned ZeroCoverageBucket = 0; + +/// This represents variables DIE offsets. +using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>; +/// This maps function DIE offset to its variables. +using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>; +/// This represents function DIE offsets containing an abstract_origin. +using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>; + /// Holds statistics for one function (or other entity that has a PC range and /// contains variables, such as a compile unit). struct PerFunctionStats { @@ -164,12 +172,14 @@ static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope, }; unsigned CoverageBucket = getCoverageBucket(); + VarParamLocStats[CoverageBucket]++; if (IsParam) ParamLocStats[CoverageBucket]++; else if (IsLocalVar) LocalVarLocStats[CoverageBucket]++; } + /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName /// and DeclLine. The identifier aims to be unique for any unique entities, /// but keeping the same among different instances of the same entity. @@ -210,12 +220,18 @@ static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) { } /// Collect debug info quality metrics for one DIE. -static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, - std::string VarPrefix, uint64_t BytesInScope, - uint32_t InlineDepth, +static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix, + const std::string &VarPrefix, + uint64_t BytesInScope, uint32_t InlineDepth, StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats, - LocationStats &LocStats) { + LocationStats &LocStats, + AbstractOriginVarsTy *AbstractOriginVariables) { + const dwarf::Tag Tag = Die.getTag(); + // Skip CU node. + if (Tag == dwarf::DW_TAG_compile_unit) + return; + bool HasLoc = false; bool HasSrcLoc = false; bool HasType = false; @@ -223,19 +239,22 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, uint64_t ScopeBytesCovered = 0; uint64_t BytesEntryValuesCovered = 0; auto &FnStats = FnStatMap[FnPrefix]; - bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter; - bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable; - bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member && + bool IsParam = Tag == dwarf::DW_TAG_formal_parameter; + bool IsLocalVar = Tag == dwarf::DW_TAG_variable; + bool IsConstantMember = Tag == dwarf::DW_TAG_member && Die.find(dwarf::DW_AT_const_value); - if (Die.getTag() == dwarf::DW_TAG_call_site || - Die.getTag() == dwarf::DW_TAG_GNU_call_site) { + // For zero covered inlined variables the locstats will be + // calculated later. + bool DeferLocStats = false; + + if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) { GlobalStats.CallSiteDIEs++; return; } - if (Die.getTag() == dwarf::DW_TAG_call_site_parameter || - Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) { + if (Tag == dwarf::DW_TAG_call_site_parameter || + Tag == dwarf::DW_TAG_GNU_call_site_parameter) { GlobalStats.CallSiteParamDIEs++; return; } @@ -256,6 +275,21 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, if (Die.findRecursively(dwarf::DW_AT_type)) HasType = true; + if (Die.find(dwarf::DW_AT_abstract_origin)) { + if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) { + if (AbstractOriginVariables) { + auto Offset = Die.find(dwarf::DW_AT_abstract_origin); + // Do not track this variable any more, since it has location + // coverage. + llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue()); + } + } else { + // The locstats will be handled at the end of + // the collectStatsRecursive(). + DeferLocStats = true; + } + } + auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool { DWARFUnit *U = Die.getDwarfUnit(); DataExtractor Data(toStringRef(D), @@ -315,7 +349,7 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, } // Calculate the debug location statistics. - if (BytesInScope) { + if (BytesInScope && !DeferLocStats) { LocStats.NumVarParam++; if (IsParam) LocStats.NumParam++; @@ -389,27 +423,73 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, } } +/// Recursively collect variables from subprogram with DW_AT_inline attribute. +static void collectAbstractOriginFnInfo( + DWARFDie Die, uint64_t SPOffset, + AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo) { + DWARFDie Child = Die.getFirstChild(); + while (Child) { + const dwarf::Tag ChildTag = Child.getTag(); + if (ChildTag == dwarf::DW_TAG_formal_parameter || + ChildTag == dwarf::DW_TAG_variable) + GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset()); + else if (ChildTag == dwarf::DW_TAG_lexical_block) + collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo); + Child = Child.getSibling(); + } +} + /// Recursively collect debug info quality metrics. -static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, - std::string VarPrefix, uint64_t BytesInScope, - uint32_t InlineDepth, - StringMap<PerFunctionStats> &FnStatMap, - GlobalStats &GlobalStats, - LocationStats &LocStats) { +static void collectStatsRecursive( + DWARFDie Die, std::string FnPrefix, std::string VarPrefix, + uint64_t BytesInScope, uint32_t InlineDepth, + StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats, + LocationStats &LocStats, + AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, + FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed, + AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) { + // Skip NULL nodes. + if (Die.isNULL()) + return; + const dwarf::Tag Tag = Die.getTag(); // Skip function types. if (Tag == dwarf::DW_TAG_subroutine_type) return; // Handle any kind of lexical scope. + const bool HasAbstractOrigin = Die.find(dwarf::DW_AT_abstract_origin) != None; const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; - if (IsFunction || IsInlinedFunction || IsBlock) { + // We want to know how many variables (with abstract_origin) don't have + // location info. + const bool IsCandidateForZeroLocCovTracking = + (IsInlinedFunction || (IsFunction && HasAbstractOrigin)); + + AbstractOriginVarsTy AbstractOriginVars; + + // Get the vars of the inlined fn, so the locstats + // reports the missing vars (with coverage 0%). + if (IsCandidateForZeroLocCovTracking) { + auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin); + if (OffsetFn) { + uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue(); + if (GlobalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) { + AbstractOriginVars = GlobalAbstractOriginFnInfo[OffsetOfInlineFnCopy]; + AbstractOriginVarsPtr = &AbstractOriginVars; + } else { + // This means that the DW_AT_inline fn copy is out of order, + // so this abstract origin instance will be processed later. + FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset()); + AbstractOriginVarsPtr = nullptr; + } + } + } + if (IsFunction || IsInlinedFunction || IsBlock) { // Reset VarPrefix when entering a new function. - if (Die.getTag() == dwarf::DW_TAG_subprogram || - Die.getTag() == dwarf::DW_TAG_inlined_subroutine) + if (IsFunction || IsInlinedFunction) VarPrefix = "v"; // Ignore forward declarations. @@ -434,9 +514,15 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, // Count the function. if (!IsBlock) { - // Skip over abstract origins. - if (Die.find(dwarf::DW_AT_inline)) + // Skip over abstract origins, but collect variables + // from it so it can be used for location statistics + // for inlined instancies. + if (Die.find(dwarf::DW_AT_inline)) { + uint64_t SPOffset = Die.getOffset(); + collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo); return; + } + std::string FnID = constructDieID(Die); // We've seen an instance of this function. auto &FnStats = FnStatMap[FnID]; @@ -465,7 +551,7 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, } else { // Not a scope, visit the Die itself. It could be a variable. collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth, - FnStatMap, GlobalStats, LocStats); + FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr); } // Set InlineDepth correctly for child recursion @@ -485,10 +571,33 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, if (Child.getTag() == dwarf::DW_TAG_formal_parameter) ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.'; - collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope, - InlineDepth, FnStatMap, GlobalStats, LocStats); + collectStatsRecursive( + Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap, + GlobalStats, LocStats, GlobalAbstractOriginFnInfo, + FnsWithAbstractOriginToBeProcessed, AbstractOriginVarsPtr); Child = Child.getSibling(); } + + if (!IsCandidateForZeroLocCovTracking) + return; + + // After we have processed all vars of the inlined function (or function with + // an abstract_origin), we want to know how many variables have no location. + for (auto Offset : AbstractOriginVars) { + LocStats.NumVarParam++; + LocStats.VarParamLocStats[ZeroCoverageBucket]++; + auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset); + if (!FnDie) + continue; + auto Tag = FnDie.getTag(); + if (Tag == dwarf::DW_TAG_formal_parameter) { + LocStats.NumParam++; + LocStats.ParamLocStats[ZeroCoverageBucket]++; + } else if (Tag == dwarf::DW_TAG_variable) { + LocStats.NumVar++; + LocStats.LocalVarLocStats[ZeroCoverageBucket]++; + } + } } /// Print human-readable output. @@ -536,9 +645,63 @@ static void printLocationStats(json::OStream &J, const char *Key, } static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) { - for (const auto &DebugSec : Sizes.DebugSectionSizes) - J.attribute((Twine("#bytes in ") + DebugSec.getKey()).str(), - int64_t(DebugSec.getValue())); + for (const auto &It : Sizes.DebugSectionSizes) + J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second)); +} + +/// Stop tracking variables that contain abstract_origin with a location. +/// This is used for out-of-order DW_AT_inline subprograms only. +static void updateVarsWithAbstractOriginLocCovInfo( + DWARFDie FnDieWithAbstractOrigin, + AbstractOriginVarsTy &AbstractOriginVars) { + DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild(); + while (Child) { + const dwarf::Tag ChildTag = Child.getTag(); + if ((ChildTag == dwarf::DW_TAG_formal_parameter || + ChildTag == dwarf::DW_TAG_variable) && + (Child.find(dwarf::DW_AT_location) || + Child.find(dwarf::DW_AT_const_value))) { + auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin); + if (OffsetVar) + llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue()); + } else if (ChildTag == dwarf::DW_TAG_lexical_block) + updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars); + Child = Child.getSibling(); + } +} + +/// Collect zero location coverage for inlined variables which refer to +/// a DW_AT_inline copy of subprogram that is out of order in the DWARF. +/// Also cover the variables of a concrete function (represented with +/// the DW_TAG_subprogram) with an abstract_origin attribute. +static void collectZeroLocCovForVarsWithAbstractOrigin( + DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats, + AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo, + FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) { + for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) { + DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset); + auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin); + AbstractOriginVarsTy AbstractOriginVars; + if (!FnCopy) + continue; + + AbstractOriginVars = GlobalAbstractOriginFnInfo[(*FnCopy).getRawUValue()]; + updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin, + AbstractOriginVars); + + for (auto Offset : AbstractOriginVars) { + LocStats.NumVarParam++; + LocStats.VarParamLocStats[ZeroCoverageBucket]++; + auto Tag = DwUnit->getDIEForOffset(Offset).getTag(); + if (Tag == dwarf::DW_TAG_formal_parameter) { + LocStats.NumParam++; + LocStats.ParamLocStats[ZeroCoverageBucket]++; + } else if (Tag == dwarf::DW_TAG_variable) { + LocStats.NumVar++; + LocStats.LocalVarLocStats[ZeroCoverageBucket]++; + } + } + } } /// \} @@ -558,10 +721,27 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, GlobalStats GlobalStats; LocationStats LocStats; StringMap<PerFunctionStats> Statistics; - for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) - if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) + for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) { + if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) { + // These variables are being reset for each CU, since there could be + // a situation where we have two subprogram DIEs with the same offsets + // in two diferent CUs, and we can end up using wrong variables info + // when trying to resolve abstract_origin attribute. + // TODO: Handle LTO cases where the abstract origin of + // the function is in a different CU than the one it's + // referenced from or inlined into. + AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo; + FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed; + collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, - LocStats); + LocStats, GlobalAbstractOriginFnInfo, + FnsWithAbstractOriginToBeProcessed); + + collectZeroLocCovForVarsWithAbstractOrigin( + CUDie.getDwarfUnit(), GlobalStats, LocStats, + GlobalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed); + } + } /// Collect the sizes of debug sections. SectionSizes Sizes; @@ -570,7 +750,7 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, /// The version number should be increased every time the algorithm is changed /// (including bug fixes). New metrics may be added without increasing the /// version. - unsigned Version = 6; + unsigned Version = 8; unsigned VarParamTotal = 0; unsigned VarParamUnique = 0; unsigned VarParamWithLoc = 0; diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index 4322f125a84d..a324ff710af5 100644 --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -130,7 +130,8 @@ cl::OptionCategory SectionCategory("Section-specific Dump Options", static opt<bool> DumpAll("all", desc("Dump all debug info sections"), cat(SectionCategory)); -static alias DumpAllAlias("a", desc("Alias for -all"), aliasopt(DumpAll)); +static alias DumpAllAlias("a", desc("Alias for --all"), aliasopt(DumpAll), + cl::NotHidden); // Options for dumping specific sections. static unsigned DumpType = DIDT_Null; @@ -143,7 +144,8 @@ static std::array<llvm::Optional<uint64_t>, (unsigned)DIDT_ID_Count> #include "llvm/BinaryFormat/Dwarf.def" #undef HANDLE_DWARF_SECTION -static alias DumpDebugFrameAlias("eh-frame", desc("Alias for -debug-frame"), +// The aliased DumpDebugFrame is created by the Dwarf.def x-macro just above. +static alias DumpDebugFrameAlias("eh-frame", desc("Alias for --debug-frame"), NotHidden, cat(SectionCategory), aliasopt(DumpDebugFrame)); static list<std::string> @@ -164,19 +166,21 @@ static list<std::string> "accelerator tables are available, the slower but more complete " "-name option can be used instead."), value_desc("name"), cat(DwarfDumpCategory)); -static alias FindAlias("f", desc("Alias for -find."), aliasopt(Find)); +static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find), + cl::NotHidden); static opt<bool> IgnoreCase("ignore-case", desc("Ignore case distinctions when searching."), value_desc("i"), cat(DwarfDumpCategory)); -static alias IgnoreCaseAlias("i", desc("Alias for -ignore-case."), - aliasopt(IgnoreCase)); +static alias IgnoreCaseAlias("i", desc("Alias for --ignore-case."), + aliasopt(IgnoreCase), cl::NotHidden); static list<std::string> Name( "name", desc("Find and print all debug info entries whose name (DW_AT_name " "attribute) matches the exact text in <pattern>. When used with the " "the -regex option <pattern> is interpreted as a regular expression."), value_desc("pattern"), cat(DwarfDumpCategory)); -static alias NameAlias("n", desc("Alias for -name"), aliasopt(Name)); +static alias NameAlias("n", desc("Alias for --name"), aliasopt(Name), + cl::NotHidden); static opt<uint64_t> Lookup("lookup", desc("Lookup <address> in the debug information and print out any " @@ -193,34 +197,36 @@ static opt<bool> desc("Treat any <pattern> strings as regular expressions when " "searching instead of just as an exact string match."), cat(DwarfDumpCategory)); -static alias RegexAlias("x", desc("Alias for -regex"), aliasopt(UseRegex)); +static alias RegexAlias("x", desc("Alias for --regex"), aliasopt(UseRegex), + cl::NotHidden); static opt<bool> ShowChildren("show-children", desc("Show a debug info entry's children when selectively " "printing entries."), cat(DwarfDumpCategory)); -static alias ShowChildrenAlias("c", desc("Alias for -show-children."), - aliasopt(ShowChildren)); +static alias ShowChildrenAlias("c", desc("Alias for --show-children."), + aliasopt(ShowChildren), cl::NotHidden); static opt<bool> ShowParents("show-parents", desc("Show a debug info entry's parents when selectively " "printing entries."), cat(DwarfDumpCategory)); -static alias ShowParentsAlias("p", desc("Alias for -show-parents."), - aliasopt(ShowParents)); +static alias ShowParentsAlias("p", desc("Alias for --show-parents."), + aliasopt(ShowParents), cl::NotHidden); static opt<bool> ShowForm("show-form", desc("Show DWARF form types after the DWARF attribute types."), cat(DwarfDumpCategory)); -static alias ShowFormAlias("F", desc("Alias for -show-form."), - aliasopt(ShowForm), cat(DwarfDumpCategory)); +static alias ShowFormAlias("F", desc("Alias for --show-form."), + aliasopt(ShowForm), cat(DwarfDumpCategory), + cl::NotHidden); static opt<unsigned> ChildRecurseDepth("recurse-depth", desc("Only recurse to a depth of N when displaying " "children of debug info entries."), cat(DwarfDumpCategory), init(-1U), value_desc("N")); -static alias ChildRecurseDepthAlias("r", desc("Alias for -recurse-depth."), - aliasopt(ChildRecurseDepth)); +static alias ChildRecurseDepthAlias("r", desc("Alias for --recurse-depth."), + aliasopt(ChildRecurseDepth), cl::NotHidden); static opt<unsigned> ParentRecurseDepth("parent-recurse-depth", desc("Only recurse to a depth of N when displaying " @@ -245,25 +251,30 @@ static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."), cat(DwarfDumpCategory)); static opt<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."), cat(DwarfDumpCategory)); -static alias DumpUUIDAlias("u", desc("Alias for -uuid."), aliasopt(DumpUUID)); +static alias DumpUUIDAlias("u", desc("Alias for --uuid."), aliasopt(DumpUUID), + cl::NotHidden); static opt<bool> Verbose("verbose", desc("Print more low-level encoding details."), cat(DwarfDumpCategory)); -static alias VerboseAlias("v", desc("Alias for -verbose."), aliasopt(Verbose), - cat(DwarfDumpCategory)); +static alias VerboseAlias("v", desc("Alias for --verbose."), aliasopt(Verbose), + cat(DwarfDumpCategory), cl::NotHidden); static cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); } // namespace /// @} //===----------------------------------------------------------------------===// -static void error(StringRef Prefix, std::error_code EC) { - if (!EC) +static void error(StringRef Prefix, Error Err) { + if (!Err) return; - WithColor::error() << Prefix << ": " << EC.message() << "\n"; + WithColor::error() << Prefix << ": " << toString(std::move(Err)) << "\n"; exit(1); } +static void error(StringRef Prefix, std::error_code EC) { + error(Prefix, errorCodeToError(EC)); +} + static DIDumpOptions getDumpOpts(DWARFContext &C) { DIDumpOptions DumpOpts; DumpOpts.DumpType = DumpType; @@ -484,7 +495,7 @@ static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx, // fails. raw_ostream &stream = Quiet ? nulls() : OS; stream << "Verifying " << Filename.str() << ":\tfile format " - << Obj.getFileFormatName() << "\n"; + << Obj.getFileFormatName() << "\n"; bool Result = DICtx.verify(stream, getDumpOpts(DICtx)); if (Result) stream << "No errors.\n"; @@ -502,13 +513,13 @@ static bool handleArchive(StringRef Filename, Archive &Arch, Error Err = Error::success(); for (auto Child : Arch.children(Err)) { auto BuffOrErr = Child.getMemoryBufferRef(); - error(Filename, errorToErrorCode(BuffOrErr.takeError())); + error(Filename, BuffOrErr.takeError()); auto NameOrErr = Child.getName(); - error(Filename, errorToErrorCode(NameOrErr.takeError())); + error(Filename, NameOrErr.takeError()); std::string Name = (Filename + "(" + NameOrErr.get() + ")").str(); Result &= handleBuffer(Name, BuffOrErr.get(), HandleObj, OS); } - error(Filename, errorToErrorCode(std::move(Err))); + error(Filename, std::move(Err)); return Result; } @@ -516,7 +527,7 @@ static bool handleArchive(StringRef Filename, Archive &Arch, static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, HandlerFn HandleObj, raw_ostream &OS) { Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Buffer); - error(Filename, errorToErrorCode(BinOrErr.takeError())); + error(Filename, BinOrErr.takeError()); bool Result = true; auto RecoverableErrorHandler = [&](Error E) { @@ -530,8 +541,7 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, if (!HandleObj(*Obj, *DICtx, Filename, OS)) Result = false; } - } - else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) + } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) for (auto &ObjForArch : Fat->objects()) { std::string ObjName = (Filename + "(" + ObjForArch.getArchFlagName() + ")").str(); @@ -547,7 +557,7 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, } else consumeError(MachOOrErr.takeError()); if (auto ArchiveOrErr = ObjForArch.getAsArchive()) { - error(ObjName, errorToErrorCode(ArchiveOrErr.takeError())); + error(ObjName, ArchiveOrErr.takeError()); if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS)) Result = false; continue; @@ -562,7 +572,7 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, static bool handleFile(StringRef Filename, HandlerFn HandleObj, raw_ostream &OS) { ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = - MemoryBuffer::getFileOrSTDIN(Filename); + MemoryBuffer::getFileOrSTDIN(Filename); error(Filename, BuffOrErr.getError()); std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get()); return handleBuffer(Filename, *Buffer, HandleObj, OS); @@ -613,7 +623,8 @@ int main(int argc, char **argv) { llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); - HideUnrelatedOptions({&DwarfDumpCategory, &SectionCategory, &ColorCategory}); + HideUnrelatedOptions( + {&DwarfDumpCategory, &SectionCategory, &getColorCategory()}); cl::ParseCommandLineOptions( argc, argv, "pretty-print DWARF debug information in object files" @@ -628,8 +639,8 @@ int main(int argc, char **argv) { } std::error_code EC; - ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_Text); - error("Unable to open output file" + OutputFilename, EC); + ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_TextWithCRLF); + error("unable to open output file " + OutputFilename, EC); // Don't remove output file if we exit with an error. OutputFile.keep(); @@ -659,7 +670,8 @@ int main(int argc, char **argv) { } // Unless dumping a specific DIE, default to --show-children. - if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && Find.empty()) + if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && + Find.empty()) ShowChildren = true; // Defaults to a.out if no filenames specified. diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h index dc41298265d2..cf7da56c91f8 100644 --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h @@ -9,6 +9,8 @@ #ifndef LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H #define LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/Twine.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Object/ObjectFile.h" @@ -21,7 +23,7 @@ namespace dwarfdump { struct SectionSizes { /// Map of .debug section names and their sizes across all such-named /// sections. - StringMap<uint64_t> DebugSectionSizes; + MapVector<std::string, uint64_t, StringMap<uint64_t>> DebugSectionSizes; /// Total number of bytes of all sections. uint64_t TotalObjectSize = 0; /// Total number of bytes of all debug sections. diff --git a/llvm/tools/llvm-dwp/DWPError.cpp b/llvm/tools/llvm-dwp/DWPError.cpp deleted file mode 100644 index 21d53ed6d198..000000000000 --- a/llvm/tools/llvm-dwp/DWPError.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "DWPError.h" -using namespace llvm; -char DWPError::ID; diff --git a/llvm/tools/llvm-dwp/DWPError.h b/llvm/tools/llvm-dwp/DWPError.h deleted file mode 100644 index 62025ed4caa5..000000000000 --- a/llvm/tools/llvm-dwp/DWPError.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TOOLS_LLVM_DWP_DWPERROR -#define TOOLS_LLVM_DWP_DWPERROR - -#include "llvm/Support/Error.h" -#include "llvm/Support/ErrorHandling.h" -#include <string> - -namespace llvm { -class DWPError : public ErrorInfo<DWPError> { -public: - DWPError(std::string Info) : Info(std::move(Info)) {} - void log(raw_ostream &OS) const override { OS << Info; } - std::error_code convertToErrorCode() const override { - llvm_unreachable("Not implemented"); - } - static char ID; - -private: - std::string Info; -}; -} - -#endif diff --git a/llvm/tools/llvm-dwp/DWPStringPool.h b/llvm/tools/llvm-dwp/DWPStringPool.h deleted file mode 100644 index e423076f4333..000000000000 --- a/llvm/tools/llvm-dwp/DWPStringPool.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef TOOLS_LLVM_DWP_DWPSTRINGPOOL -#define TOOLS_LLVM_DWP_DWPSTRINGPOOL - -#include "llvm/ADT/DenseMap.h" -#include "llvm/MC/MCSection.h" -#include "llvm/MC/MCStreamer.h" -#include <cassert> - -namespace llvm { -class DWPStringPool { - - struct CStrDenseMapInfo { - static inline const char *getEmptyKey() { - return reinterpret_cast<const char *>(~static_cast<uintptr_t>(0)); - } - static inline const char *getTombstoneKey() { - return reinterpret_cast<const char *>(~static_cast<uintptr_t>(1)); - } - static unsigned getHashValue(const char *Val) { - assert(Val != getEmptyKey() && "Cannot hash the empty key!"); - assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!"); - return (unsigned)hash_value(StringRef(Val)); - } - static bool isEqual(const char *LHS, const char *RHS) { - if (RHS == getEmptyKey()) - return LHS == getEmptyKey(); - if (RHS == getTombstoneKey()) - return LHS == getTombstoneKey(); - return strcmp(LHS, RHS) == 0; - } - }; - - MCStreamer &Out; - MCSection *Sec; - DenseMap<const char *, uint32_t, CStrDenseMapInfo> Pool; - uint32_t Offset = 0; - -public: - DWPStringPool(MCStreamer &Out, MCSection *Sec) : Out(Out), Sec(Sec) {} - - uint32_t getOffset(const char *Str, unsigned Length) { - assert(strlen(Str) + 1 == Length && "Ensure length hint is correct"); - - auto Pair = Pool.insert(std::make_pair(Str, Offset)); - if (Pair.second) { - Out.SwitchSection(Sec); - Out.emitBytes(StringRef(Str, Length)); - Offset += Length; - } - - return Pair.first->second; - } -}; -} - -#endif diff --git a/llvm/tools/llvm-dwp/llvm-dwp.cpp b/llvm/tools/llvm-dwp/llvm-dwp.cpp index 9aed3526b0aa..1f583728c141 100644 --- a/llvm/tools/llvm-dwp/llvm-dwp.cpp +++ b/llvm/tools/llvm-dwp/llvm-dwp.cpp @@ -10,39 +10,22 @@ // package files). // //===----------------------------------------------------------------------===// -#include "DWPError.h" -#include "DWPStringPool.h" -#include "llvm/ADT/MapVector.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" -#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" -#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" +#include "llvm/DWP/DWP.h" +#include "llvm/DWP/DWPError.h" +#include "llvm/DWP/DWPStringPool.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstrInfo.h" -#include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" -#include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCTargetOptionsCommandFlags.h" -#include "llvm/Object/Decompressor.h" -#include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Support/DataExtractor.h" -#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" -#include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::object; @@ -64,449 +47,6 @@ static cl::opt<std::string> OutputFilename(cl::Required, "o", cl::value_desc("filename"), cl::cat(DwpCategory)); -static void writeStringsAndOffsets(MCStreamer &Out, DWPStringPool &Strings, - MCSection *StrOffsetSection, - StringRef CurStrSection, - StringRef CurStrOffsetSection) { - // Could possibly produce an error or warning if one of these was non-null but - // the other was null. - if (CurStrSection.empty() || CurStrOffsetSection.empty()) - return; - - DenseMap<uint64_t, uint32_t> OffsetRemapping; - - DataExtractor Data(CurStrSection, true, 0); - uint64_t LocalOffset = 0; - uint64_t PrevOffset = 0; - while (const char *s = Data.getCStr(&LocalOffset)) { - OffsetRemapping[PrevOffset] = - Strings.getOffset(s, LocalOffset - PrevOffset); - PrevOffset = LocalOffset; - } - - Data = DataExtractor(CurStrOffsetSection, true, 0); - - Out.SwitchSection(StrOffsetSection); - - uint64_t Offset = 0; - uint64_t Size = CurStrOffsetSection.size(); - while (Offset < Size) { - auto OldOffset = Data.getU32(&Offset); - auto NewOffset = OffsetRemapping[OldOffset]; - Out.emitIntValue(NewOffset, 4); - } -} - -static uint64_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) { - uint64_t CurCode; - uint64_t Offset = 0; - DataExtractor AbbrevData(Abbrev, true, 0); - while ((CurCode = AbbrevData.getULEB128(&Offset)) != AbbrCode) { - // Tag - AbbrevData.getULEB128(&Offset); - // DW_CHILDREN - AbbrevData.getU8(&Offset); - // Attributes - while (AbbrevData.getULEB128(&Offset) | AbbrevData.getULEB128(&Offset)) - ; - } - return Offset; -} - -struct CompileUnitIdentifiers { - uint64_t Signature = 0; - const char *Name = ""; - const char *DWOName = ""; -}; - -static Expected<const char *> -getIndexedString(dwarf::Form Form, DataExtractor InfoData, - uint64_t &InfoOffset, StringRef StrOffsets, StringRef Str) { - if (Form == dwarf::DW_FORM_string) - return InfoData.getCStr(&InfoOffset); - if (Form != dwarf::DW_FORM_GNU_str_index) - return make_error<DWPError>( - "string field encoded without DW_FORM_string or DW_FORM_GNU_str_index"); - auto StrIndex = InfoData.getULEB128(&InfoOffset); - DataExtractor StrOffsetsData(StrOffsets, true, 0); - uint64_t StrOffsetsOffset = 4 * StrIndex; - uint64_t StrOffset = StrOffsetsData.getU32(&StrOffsetsOffset); - DataExtractor StrData(Str, true, 0); - return StrData.getCStr(&StrOffset); -} - -static Expected<CompileUnitIdentifiers> getCUIdentifiers(StringRef Abbrev, - StringRef Info, - StringRef StrOffsets, - StringRef Str) { - uint64_t Offset = 0; - DataExtractor InfoData(Info, true, 0); - dwarf::DwarfFormat Format = dwarf::DwarfFormat::DWARF32; - uint64_t Length = InfoData.getU32(&Offset); - CompileUnitIdentifiers ID; - Optional<uint64_t> Signature = None; - // If the length is 0xffffffff, then this indictes that this is a DWARF 64 - // stream and the length is actually encoded into a 64 bit value that follows. - if (Length == 0xffffffffU) { - Format = dwarf::DwarfFormat::DWARF64; - Length = InfoData.getU64(&Offset); - } - uint16_t Version = InfoData.getU16(&Offset); - if (Version >= 5) { - auto UnitType = InfoData.getU8(&Offset); - if (UnitType != dwarf::DW_UT_split_compile) - return make_error<DWPError>( - std::string("unit type DW_UT_split_compile type not found in " - "debug_info header. Unexpected unit type 0x" + - utostr(UnitType) + " found")); - } - InfoData.getU32(&Offset); // Abbrev offset (should be zero) - uint8_t AddrSize = InfoData.getU8(&Offset); - if (Version >= 5) - Signature = InfoData.getU64(&Offset); - uint32_t AbbrCode = InfoData.getULEB128(&Offset); - - DataExtractor AbbrevData(Abbrev, true, 0); - uint64_t AbbrevOffset = getCUAbbrev(Abbrev, AbbrCode); - auto Tag = static_cast<dwarf::Tag>(AbbrevData.getULEB128(&AbbrevOffset)); - if (Tag != dwarf::DW_TAG_compile_unit) - return make_error<DWPError>("top level DIE is not a compile unit"); - // DW_CHILDREN - AbbrevData.getU8(&AbbrevOffset); - uint32_t Name; - dwarf::Form Form; - while ((Name = AbbrevData.getULEB128(&AbbrevOffset)) | - (Form = static_cast<dwarf::Form>(AbbrevData.getULEB128(&AbbrevOffset))) && - (Name != 0 || Form != 0)) { - switch (Name) { - case dwarf::DW_AT_name: { - Expected<const char *> EName = - getIndexedString(Form, InfoData, Offset, StrOffsets, Str); - if (!EName) - return EName.takeError(); - ID.Name = *EName; - break; - } - case dwarf::DW_AT_GNU_dwo_name: - case dwarf::DW_AT_dwo_name: { - Expected<const char *> EName = - getIndexedString(Form, InfoData, Offset, StrOffsets, Str); - if (!EName) - return EName.takeError(); - ID.DWOName = *EName; - break; - } - case dwarf::DW_AT_GNU_dwo_id: - Signature = InfoData.getU64(&Offset); - break; - default: - DWARFFormValue::skipValue(Form, InfoData, &Offset, - dwarf::FormParams({Version, AddrSize, Format})); - } - } - if (!Signature) - return make_error<DWPError>("compile unit missing dwo_id"); - ID.Signature = *Signature; - return ID; -} - -struct UnitIndexEntry { - DWARFUnitIndex::Entry::SectionContribution Contributions[8]; - std::string Name; - std::string DWOName; - StringRef DWPName; -}; - -static bool isSupportedSectionKind(DWARFSectionKind Kind) { - return Kind != DW_SECT_EXT_unknown; -} - -// Convert an internal section identifier into the index to use with -// UnitIndexEntry::Contributions. -static unsigned getContributionIndex(DWARFSectionKind Kind) { - // Assuming the pre-standard DWP format. - assert(serializeSectionKind(Kind, 2) >= DW_SECT_INFO); - return serializeSectionKind(Kind, 2) - DW_SECT_INFO; -} - -// Convert a UnitIndexEntry::Contributions index to the corresponding on-disk -// value of the section identifier. -static unsigned getOnDiskSectionId(unsigned Index) { - return Index + DW_SECT_INFO; -} - -static StringRef getSubsection(StringRef Section, - const DWARFUnitIndex::Entry &Entry, - DWARFSectionKind Kind) { - const auto *Off = Entry.getContribution(Kind); - if (!Off) - return StringRef(); - return Section.substr(Off->Offset, Off->Length); -} - -static void addAllTypesFromDWP( - MCStreamer &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries, - const DWARFUnitIndex &TUIndex, MCSection *OutputTypes, StringRef Types, - const UnitIndexEntry &TUEntry, uint32_t &TypesOffset) { - Out.SwitchSection(OutputTypes); - for (const DWARFUnitIndex::Entry &E : TUIndex.getRows()) { - auto *I = E.getContributions(); - if (!I) - continue; - auto P = TypeIndexEntries.insert(std::make_pair(E.getSignature(), TUEntry)); - if (!P.second) - continue; - auto &Entry = P.first->second; - // Zero out the debug_info contribution - Entry.Contributions[0] = {}; - for (auto Kind : TUIndex.getColumnKinds()) { - if (!isSupportedSectionKind(Kind)) - continue; - auto &C = Entry.Contributions[getContributionIndex(Kind)]; - C.Offset += I->Offset; - C.Length = I->Length; - ++I; - } - unsigned TypesIndex = getContributionIndex(DW_SECT_EXT_TYPES); - auto &C = Entry.Contributions[TypesIndex]; - Out.emitBytes(Types.substr( - C.Offset - TUEntry.Contributions[TypesIndex].Offset, C.Length)); - C.Offset = TypesOffset; - TypesOffset += C.Length; - } -} - -static void addAllTypes(MCStreamer &Out, - MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries, - MCSection *OutputTypes, - const std::vector<StringRef> &TypesSections, - const UnitIndexEntry &CUEntry, uint32_t &TypesOffset) { - for (StringRef Types : TypesSections) { - Out.SwitchSection(OutputTypes); - uint64_t Offset = 0; - DataExtractor Data(Types, true, 0); - while (Data.isValidOffset(Offset)) { - UnitIndexEntry Entry = CUEntry; - // Zero out the debug_info contribution - Entry.Contributions[0] = {}; - auto &C = Entry.Contributions[getContributionIndex(DW_SECT_EXT_TYPES)]; - C.Offset = TypesOffset; - auto PrevOffset = Offset; - // Length of the unit, including the 4 byte length field. - C.Length = Data.getU32(&Offset) + 4; - - Data.getU16(&Offset); // Version - Data.getU32(&Offset); // Abbrev offset - Data.getU8(&Offset); // Address size - auto Signature = Data.getU64(&Offset); - Offset = PrevOffset + C.Length; - - auto P = TypeIndexEntries.insert(std::make_pair(Signature, Entry)); - if (!P.second) - continue; - - Out.emitBytes(Types.substr(PrevOffset, C.Length)); - TypesOffset += C.Length; - } - } -} - -static void -writeIndexTable(MCStreamer &Out, ArrayRef<unsigned> ContributionOffsets, - const MapVector<uint64_t, UnitIndexEntry> &IndexEntries, - uint32_t DWARFUnitIndex::Entry::SectionContribution::*Field) { - for (const auto &E : IndexEntries) - for (size_t i = 0; i != array_lengthof(E.second.Contributions); ++i) - if (ContributionOffsets[i]) - Out.emitIntValue(E.second.Contributions[i].*Field, 4); -} - -static void -writeIndex(MCStreamer &Out, MCSection *Section, - ArrayRef<unsigned> ContributionOffsets, - const MapVector<uint64_t, UnitIndexEntry> &IndexEntries) { - if (IndexEntries.empty()) - return; - - unsigned Columns = 0; - for (auto &C : ContributionOffsets) - if (C) - ++Columns; - - std::vector<unsigned> Buckets(NextPowerOf2(3 * IndexEntries.size() / 2)); - uint64_t Mask = Buckets.size() - 1; - size_t i = 0; - for (const auto &P : IndexEntries) { - auto S = P.first; - auto H = S & Mask; - auto HP = ((S >> 32) & Mask) | 1; - while (Buckets[H]) { - assert(S != IndexEntries.begin()[Buckets[H] - 1].first && - "Duplicate unit"); - H = (H + HP) & Mask; - } - Buckets[H] = i + 1; - ++i; - } - - Out.SwitchSection(Section); - Out.emitIntValue(2, 4); // Version - Out.emitIntValue(Columns, 4); // Columns - Out.emitIntValue(IndexEntries.size(), 4); // Num Units - Out.emitIntValue(Buckets.size(), 4); // Num Buckets - - // Write the signatures. - for (const auto &I : Buckets) - Out.emitIntValue(I ? IndexEntries.begin()[I - 1].first : 0, 8); - - // Write the indexes. - for (const auto &I : Buckets) - Out.emitIntValue(I, 4); - - // Write the column headers (which sections will appear in the table) - for (size_t i = 0; i != ContributionOffsets.size(); ++i) - if (ContributionOffsets[i]) - Out.emitIntValue(getOnDiskSectionId(i), 4); - - // Write the offsets. - writeIndexTable(Out, ContributionOffsets, IndexEntries, - &DWARFUnitIndex::Entry::SectionContribution::Offset); - - // Write the lengths. - writeIndexTable(Out, ContributionOffsets, IndexEntries, - &DWARFUnitIndex::Entry::SectionContribution::Length); -} - -static std::string buildDWODescription(StringRef Name, StringRef DWPName, - StringRef DWOName) { - std::string Text = "\'"; - Text += Name; - Text += '\''; - if (!DWPName.empty()) { - Text += " (from "; - if (!DWOName.empty()) { - Text += '\''; - Text += DWOName; - Text += "' in "; - } - Text += '\''; - Text += DWPName; - Text += "')"; - } - return Text; -} - -static Error createError(StringRef Name, Error E) { - return make_error<DWPError>( - ("failure while decompressing compressed section: '" + Name + "', " + - llvm::toString(std::move(E))) - .str()); -} - -static Error -handleCompressedSection(std::deque<SmallString<32>> &UncompressedSections, - StringRef &Name, StringRef &Contents) { - if (!Decompressor::isGnuStyle(Name)) - return Error::success(); - - Expected<Decompressor> Dec = - Decompressor::create(Name, Contents, false /*IsLE*/, false /*Is64Bit*/); - if (!Dec) - return createError(Name, Dec.takeError()); - - UncompressedSections.emplace_back(); - if (Error E = Dec->resizeAndDecompress(UncompressedSections.back())) - return createError(Name, std::move(E)); - - Name = Name.substr(2); // Drop ".z" - Contents = UncompressedSections.back(); - return Error::success(); -} - -static Error handleSection( - const StringMap<std::pair<MCSection *, DWARFSectionKind>> &KnownSections, - const MCSection *StrSection, const MCSection *StrOffsetSection, - const MCSection *TypesSection, const MCSection *CUIndexSection, - const MCSection *TUIndexSection, const SectionRef &Section, MCStreamer &Out, - std::deque<SmallString<32>> &UncompressedSections, - uint32_t (&ContributionOffsets)[8], UnitIndexEntry &CurEntry, - StringRef &CurStrSection, StringRef &CurStrOffsetSection, - std::vector<StringRef> &CurTypesSection, StringRef &InfoSection, - StringRef &AbbrevSection, StringRef &CurCUIndexSection, - StringRef &CurTUIndexSection) { - if (Section.isBSS()) - return Error::success(); - - if (Section.isVirtual()) - return Error::success(); - - Expected<StringRef> NameOrErr = Section.getName(); - if (!NameOrErr) - return NameOrErr.takeError(); - StringRef Name = *NameOrErr; - - Expected<StringRef> ContentsOrErr = Section.getContents(); - if (!ContentsOrErr) - return ContentsOrErr.takeError(); - StringRef Contents = *ContentsOrErr; - - if (auto Err = handleCompressedSection(UncompressedSections, Name, Contents)) - return Err; - - Name = Name.substr(Name.find_first_not_of("._")); - - auto SectionPair = KnownSections.find(Name); - if (SectionPair == KnownSections.end()) - return Error::success(); - - if (DWARFSectionKind Kind = SectionPair->second.second) { - auto Index = getContributionIndex(Kind); - if (Kind != DW_SECT_EXT_TYPES) { - CurEntry.Contributions[Index].Offset = ContributionOffsets[Index]; - ContributionOffsets[Index] += - (CurEntry.Contributions[Index].Length = Contents.size()); - } - - switch (Kind) { - case DW_SECT_INFO: - InfoSection = Contents; - break; - case DW_SECT_ABBREV: - AbbrevSection = Contents; - break; - default: - break; - } - } - - MCSection *OutSection = SectionPair->second.first; - if (OutSection == StrOffsetSection) - CurStrOffsetSection = Contents; - else if (OutSection == StrSection) - CurStrSection = Contents; - else if (OutSection == TypesSection) - CurTypesSection.push_back(Contents); - else if (OutSection == CUIndexSection) - CurCUIndexSection = Contents; - else if (OutSection == TUIndexSection) - CurTUIndexSection = Contents; - else { - Out.SwitchSection(OutSection); - Out.emitBytes(Contents); - } - return Error::success(); -} - -static Error -buildDuplicateError(const std::pair<uint64_t, UnitIndexEntry> &PrevE, - const CompileUnitIdentifiers &ID, StringRef DWPName) { - return make_error<DWPError>( - std::string("duplicate DWO ID (") + utohexstr(PrevE.first) + ") in " + - buildDWODescription(PrevE.second.Name, PrevE.second.DWPName, - PrevE.second.DWOName) + - " and " + buildDWODescription(ID.Name, DWPName, ID.DWOName)); -} - static Expected<SmallVector<std::string, 16>> getDWOFilenames(StringRef ExecFilename) { auto ErrOrObj = object::ObjectFile::createObjectFile(ExecFilename); @@ -526,8 +66,8 @@ getDWOFilenames(StringRef ExecFilename) { std::string DWOCompDir = dwarf::toString(Die.find(dwarf::DW_AT_comp_dir), ""); if (!DWOCompDir.empty()) { - SmallString<16> DWOPath; - sys::path::append(DWOPath, DWOCompDir, DWOName); + SmallString<16> DWOPath(std::move(DWOName)); + sys::fs::make_absolute(DWOCompDir, DWOPath); DWOPaths.emplace_back(DWOPath.data(), DWOPath.size()); } else { DWOPaths.push_back(std::move(DWOName)); @@ -536,160 +76,6 @@ getDWOFilenames(StringRef ExecFilename) { return std::move(DWOPaths); } -static Error write(MCStreamer &Out, ArrayRef<std::string> Inputs) { - const auto &MCOFI = *Out.getContext().getObjectFileInfo(); - MCSection *const StrSection = MCOFI.getDwarfStrDWOSection(); - MCSection *const StrOffsetSection = MCOFI.getDwarfStrOffDWOSection(); - MCSection *const TypesSection = MCOFI.getDwarfTypesDWOSection(); - MCSection *const CUIndexSection = MCOFI.getDwarfCUIndexSection(); - MCSection *const TUIndexSection = MCOFI.getDwarfTUIndexSection(); - const StringMap<std::pair<MCSection *, DWARFSectionKind>> KnownSections = { - {"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}}, - {"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_EXT_TYPES}}, - {"debug_str_offsets.dwo", {StrOffsetSection, DW_SECT_STR_OFFSETS}}, - {"debug_str.dwo", {StrSection, static_cast<DWARFSectionKind>(0)}}, - {"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_EXT_LOC}}, - {"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}}, - {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}}, - {"debug_cu_index", {CUIndexSection, static_cast<DWARFSectionKind>(0)}}, - {"debug_tu_index", {TUIndexSection, static_cast<DWARFSectionKind>(0)}}}; - - MapVector<uint64_t, UnitIndexEntry> IndexEntries; - MapVector<uint64_t, UnitIndexEntry> TypeIndexEntries; - - uint32_t ContributionOffsets[8] = {}; - - DWPStringPool Strings(Out, StrSection); - - SmallVector<OwningBinary<object::ObjectFile>, 128> Objects; - Objects.reserve(Inputs.size()); - - std::deque<SmallString<32>> UncompressedSections; - - for (const auto &Input : Inputs) { - auto ErrOrObj = object::ObjectFile::createObjectFile(Input); - if (!ErrOrObj) - return ErrOrObj.takeError(); - - auto &Obj = *ErrOrObj->getBinary(); - Objects.push_back(std::move(*ErrOrObj)); - - UnitIndexEntry CurEntry = {}; - - StringRef CurStrSection; - StringRef CurStrOffsetSection; - std::vector<StringRef> CurTypesSection; - StringRef InfoSection; - StringRef AbbrevSection; - StringRef CurCUIndexSection; - StringRef CurTUIndexSection; - - for (const auto &Section : Obj.sections()) - if (auto Err = handleSection( - KnownSections, StrSection, StrOffsetSection, TypesSection, - CUIndexSection, TUIndexSection, Section, Out, - UncompressedSections, ContributionOffsets, CurEntry, - CurStrSection, CurStrOffsetSection, CurTypesSection, InfoSection, - AbbrevSection, CurCUIndexSection, CurTUIndexSection)) - return Err; - - if (InfoSection.empty()) - continue; - - writeStringsAndOffsets(Out, Strings, StrOffsetSection, CurStrSection, - CurStrOffsetSection); - - if (CurCUIndexSection.empty()) { - Expected<CompileUnitIdentifiers> EID = getCUIdentifiers( - AbbrevSection, InfoSection, CurStrOffsetSection, CurStrSection); - if (!EID) - return createFileError(Input, EID.takeError()); - const auto &ID = *EID; - auto P = IndexEntries.insert(std::make_pair(ID.Signature, CurEntry)); - if (!P.second) - return buildDuplicateError(*P.first, ID, ""); - P.first->second.Name = ID.Name; - P.first->second.DWOName = ID.DWOName; - addAllTypes(Out, TypeIndexEntries, TypesSection, CurTypesSection, - CurEntry, - ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)]); - continue; - } - - DWARFUnitIndex CUIndex(DW_SECT_INFO); - DataExtractor CUIndexData(CurCUIndexSection, Obj.isLittleEndian(), 0); - if (!CUIndex.parse(CUIndexData)) - return make_error<DWPError>("failed to parse cu_index"); - if (CUIndex.getVersion() != 2) - return make_error<DWPError>( - "unsupported cu_index version: " + utostr(CUIndex.getVersion()) + - " (only version 2 is supported)"); - - for (const DWARFUnitIndex::Entry &E : CUIndex.getRows()) { - auto *I = E.getContributions(); - if (!I) - continue; - auto P = IndexEntries.insert(std::make_pair(E.getSignature(), CurEntry)); - Expected<CompileUnitIdentifiers> EID = getCUIdentifiers( - getSubsection(AbbrevSection, E, DW_SECT_ABBREV), - getSubsection(InfoSection, E, DW_SECT_INFO), - getSubsection(CurStrOffsetSection, E, DW_SECT_STR_OFFSETS), - CurStrSection); - if (!EID) - return createFileError(Input, EID.takeError()); - const auto &ID = *EID; - if (!P.second) - return buildDuplicateError(*P.first, ID, Input); - auto &NewEntry = P.first->second; - NewEntry.Name = ID.Name; - NewEntry.DWOName = ID.DWOName; - NewEntry.DWPName = Input; - for (auto Kind : CUIndex.getColumnKinds()) { - if (!isSupportedSectionKind(Kind)) - continue; - auto &C = NewEntry.Contributions[getContributionIndex(Kind)]; - C.Offset += I->Offset; - C.Length = I->Length; - ++I; - } - } - - if (!CurTypesSection.empty()) { - if (CurTypesSection.size() != 1) - return make_error<DWPError>("multiple type unit sections in .dwp file"); - DWARFUnitIndex TUIndex(DW_SECT_EXT_TYPES); - DataExtractor TUIndexData(CurTUIndexSection, Obj.isLittleEndian(), 0); - if (!TUIndex.parse(TUIndexData)) - return make_error<DWPError>("failed to parse tu_index"); - if (TUIndex.getVersion() != 2) - return make_error<DWPError>( - "unsupported tu_index version: " + utostr(TUIndex.getVersion()) + - " (only version 2 is supported)"); - - addAllTypesFromDWP( - Out, TypeIndexEntries, TUIndex, TypesSection, CurTypesSection.front(), - CurEntry, - ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)]); - } - } - - // Lie about there being no info contributions so the TU index only includes - // the type unit contribution - ContributionOffsets[0] = 0; - writeIndex(Out, MCOFI.getDwarfTUIndexSection(), ContributionOffsets, - TypeIndexEntries); - - // Lie about the type contribution - ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)] = 0; - // Unlie about the info contribution - ContributionOffsets[0] = 1; - - writeIndex(Out, MCOFI.getDwarfCUIndexSection(), ContributionOffsets, - IndexEntries); - - return Error::success(); -} - static int error(const Twine &Error, const Twine &Context) { errs() << Twine("while processing ") + Context + ":\n"; errs() << Twine("error: ") + Error + "\n"; @@ -707,6 +93,7 @@ static Expected<Triple> readTargetTriple(StringRef FileName) { int main(int argc, char **argv) { InitLLVM X(argc, argv); + cl::HideUnrelatedOptions({&DwpCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files\n"); llvm::InitializeAllTargetInfos(); @@ -756,15 +143,16 @@ int main(int argc, char **argv) { if (!MAI) return error("no asm info for target " + TripleName, Context); - MCObjectFileInfo MOFI; - MCContext MC(MAI.get(), MRI.get(), &MOFI); - MOFI.InitMCObjectFileInfo(*ErrOrTriple, /*PIC*/ false, MC); - std::unique_ptr<MCSubtargetInfo> MSTI( TheTarget->createMCSubtargetInfo(TripleName, "", "")); if (!MSTI) return error("no subtarget info for target " + TripleName, Context); + MCContext MC(*ErrOrTriple, MAI.get(), MRI.get(), MSTI.get()); + std::unique_ptr<MCObjectFileInfo> MOFI( + TheTarget->createMCObjectFileInfo(MC, /*PIC=*/false)); + MC.setObjectFileInfo(MOFI.get()); + MCTargetOptions Options; auto MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options); if (!MAB) diff --git a/llvm/tools/llvm-link/llvm-link.cpp b/llvm/tools/llvm-link/llvm-link.cpp index eed49c438335..9abe8efaa4e8 100644 --- a/llvm/tools/llvm-link/llvm-link.cpp +++ b/llvm/tools/llvm-link/llvm-link.cpp @@ -41,21 +41,25 @@ #include <utility> using namespace llvm; -static cl::list<std::string> -InputFilenames(cl::Positional, cl::OneOrMore, - cl::desc("<input bitcode files>")); +static cl::OptionCategory LinkCategory("Link Options"); + +static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, + cl::desc("<input bitcode files>"), + cl::cat(LinkCategory)); static cl::list<std::string> OverridingInputs( "override", cl::ZeroOrMore, cl::value_desc("filename"), cl::desc( - "input bitcode file which can override previously defined symbol(s)")); + "input bitcode file which can override previously defined symbol(s)"), + cl::cat(LinkCategory)); // Option to simulate function importing for testing. This enables using // llvm-link to simulate ThinLTO backend processes. static cl::list<std::string> Imports( "import", cl::ZeroOrMore, cl::value_desc("function:filename"), cl::desc("Pair of function name and filename, where function should be " - "imported from bitcode in filename")); + "imported from bitcode in filename"), + cl::cat(LinkCategory)); // Option to support testing of function importing. The module summary // must be specified in the case were we request imports via the -import @@ -64,51 +68,61 @@ static cl::list<std::string> Imports( // consistent promotion and renaming of locals. static cl::opt<std::string> SummaryIndex("summary-index", cl::desc("Module summary index filename"), - cl::init(""), cl::value_desc("filename")); + cl::init(""), cl::value_desc("filename"), + cl::cat(LinkCategory)); static cl::opt<std::string> -OutputFilename("o", cl::desc("Override output filename"), cl::init("-"), - cl::value_desc("filename")); + OutputFilename("o", cl::desc("Override output filename"), cl::init("-"), + cl::value_desc("filename"), cl::cat(LinkCategory)); -static cl::opt<bool> -Internalize("internalize", cl::desc("Internalize linked symbols")); +static cl::opt<bool> Internalize("internalize", + cl::desc("Internalize linked symbols"), + cl::cat(LinkCategory)); static cl::opt<bool> DisableDITypeMap("disable-debug-info-type-map", - cl::desc("Don't use a uniquing type map for debug info")); + cl::desc("Don't use a uniquing type map for debug info"), + cl::cat(LinkCategory)); -static cl::opt<bool> -OnlyNeeded("only-needed", cl::desc("Link only needed symbols")); +static cl::opt<bool> OnlyNeeded("only-needed", + cl::desc("Link only needed symbols"), + cl::cat(LinkCategory)); -static cl::opt<bool> -Force("f", cl::desc("Enable binary output on terminals")); +static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"), + cl::cat(LinkCategory)); -static cl::opt<bool> - DisableLazyLoad("disable-lazy-loading", - cl::desc("Disable lazy module loading")); +static cl::opt<bool> DisableLazyLoad("disable-lazy-loading", + cl::desc("Disable lazy module loading"), + cl::cat(LinkCategory)); -static cl::opt<bool> - OutputAssembly("S", cl::desc("Write output as LLVM assembly"), cl::Hidden); +static cl::opt<bool> OutputAssembly("S", + cl::desc("Write output as LLVM assembly"), + cl::Hidden, cl::cat(LinkCategory)); -static cl::opt<bool> -Verbose("v", cl::desc("Print information about actions taken")); +static cl::opt<bool> Verbose("v", + cl::desc("Print information about actions taken"), + cl::cat(LinkCategory)); -static cl::opt<bool> -DumpAsm("d", cl::desc("Print assembly as linked"), cl::Hidden); +static cl::opt<bool> DumpAsm("d", cl::desc("Print assembly as linked"), + cl::Hidden, cl::cat(LinkCategory)); -static cl::opt<bool> -SuppressWarnings("suppress-warnings", cl::desc("Suppress all linking warnings"), - cl::init(false)); +static cl::opt<bool> SuppressWarnings("suppress-warnings", + cl::desc("Suppress all linking warnings"), + cl::init(false), cl::cat(LinkCategory)); static cl::opt<bool> PreserveBitcodeUseListOrder( "preserve-bc-uselistorder", cl::desc("Preserve use-list order when writing LLVM bitcode."), - cl::init(true), cl::Hidden); + cl::init(true), cl::Hidden, cl::cat(LinkCategory)); static cl::opt<bool> PreserveAssemblyUseListOrder( "preserve-ll-uselistorder", cl::desc("Preserve use-list order when writing LLVM assembly."), - cl::init(false), cl::Hidden); + cl::init(false), cl::Hidden, cl::cat(LinkCategory)); + +static cl::opt<bool> NoVerify("disable-verify", + cl::desc("Do not run the verifier"), cl::Hidden, + cl::cat(LinkCategory)); static ExitOnError ExitOnErr; @@ -158,9 +172,8 @@ static std::unique_ptr<Module> loadArFile(const char *Argv0, Expected<StringRef> Ename = C.getName(); if (Error E = Ename.takeError()) { errs() << Argv0 << ": "; - WithColor::error() - << " failed to read name of archive member" - << ArchiveName << "'\n"; + WithColor::error() << " failed to read name of archive member" + << ArchiveName << "'\n"; return nullptr; } std::string ChildName = Ename.get().str(); @@ -177,10 +190,10 @@ static std::unique_ptr<Module> loadArFile(const char *Argv0, return nullptr; }; - if (!isBitcode(reinterpret_cast<const unsigned char *> - (MemBuf.get().getBufferStart()), - reinterpret_cast<const unsigned char *> - (MemBuf.get().getBufferEnd()))) { + if (!isBitcode(reinterpret_cast<const unsigned char *>( + MemBuf.get().getBufferStart()), + reinterpret_cast<const unsigned char *>( + MemBuf.get().getBufferEnd()))) { errs() << Argv0 << ": "; WithColor::error() << " member of archive is not a bitcode file: '" << ChildName << "'\n"; @@ -246,8 +259,10 @@ public: Module &ModuleLazyLoaderCache::operator()(const char *argv0, const std::string &Identifier) { auto &Module = ModuleMap[Identifier]; - if (!Module) + if (!Module) { Module = createLazyModule(argv0, Identifier); + assert(Module && "Failed to create lazy module!"); + } return *Module; } } // anonymous namespace @@ -276,7 +291,7 @@ struct LLVMLinkDiagnosticHandler : public DiagnosticHandler { return true; } }; -} +} // namespace /// Import any functions requested via the -import option. static bool importFunctions(const char *argv0, Module &DestModule) { @@ -309,7 +324,7 @@ static bool importFunctions(const char *argv0, Module &DestModule) { // Load the specified source module. auto &SrcModule = ModuleLoaderCache(argv0, FileName); - if (verifyModule(SrcModule, &errs())) { + if (!NoVerify && verifyModule(SrcModule, &errs())) { errs() << argv0 << ": " << FileName; WithColor::error() << "input module is broken!\n"; return false; @@ -347,8 +362,7 @@ static bool importFunctions(const char *argv0, Module &DestModule) { } static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, - const cl::list<std::string> &Files, - unsigned Flags) { + const cl::list<std::string> &Files, unsigned Flags) { // Filter out flags that don't apply to the first file we load. unsigned ApplicableFlags = Flags & Linker::Flags::OverrideFromSrc; // Similar to some flags, internalization doesn't apply to the first file. @@ -370,7 +384,7 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, // Note that when ODR merging types cannot verify input files in here When // doing that debug metadata in the src module might already be pointing to // the destination. - if (DisableDITypeMap && verifyModule(*M, &errs())) { + if (DisableDITypeMap && !NoVerify && verifyModule(*M, &errs())) { errs() << argv0 << ": " << File << ": "; WithColor::error() << "input module is broken!\n"; return false; @@ -431,8 +445,9 @@ int main(int argc, char **argv) { ExitOnErr.setBanner(std::string(argv[0]) + ": "); LLVMContext Context; - Context.setDiagnosticHandler( - std::make_unique<LLVMLinkDiagnosticHandler>(), true); + Context.setDiagnosticHandler(std::make_unique<LLVMLinkDiagnosticHandler>(), + true); + cl::HideUnrelatedOptions({&LinkCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm linker\n"); if (!DisableDITypeMap) @@ -463,13 +478,14 @@ int main(int argc, char **argv) { std::error_code EC; ToolOutputFile Out(OutputFilename, EC, - OutputAssembly ? sys::fs::OF_Text : sys::fs::OF_None); + OutputAssembly ? sys::fs::OF_TextWithCRLF + : sys::fs::OF_None); if (EC) { WithColor::error() << EC.message() << '\n'; return 1; } - if (verifyModule(*Composite, &errs())) { + if (!NoVerify && verifyModule(*Composite, &errs())) { errs() << argv[0] << ": "; WithColor::error() << "linked module is broken!\n"; return 1; diff --git a/llvm/tools/llvm-lto/llvm-lto.cpp b/llvm/tools/llvm-lto/llvm-lto.cpp index 1745b7449714..45bfa84fb826 100644 --- a/llvm/tools/llvm-lto/llvm-lto.cpp +++ b/llvm/tools/llvm-lto/llvm-lto.cpp @@ -64,31 +64,38 @@ using namespace llvm; static codegen::RegisterCodeGenFlags CGF; +static cl::OptionCategory LTOCategory("LTO Options"); + static cl::opt<char> - OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " - "(default = '-O2')"), - cl::Prefix, cl::ZeroOrMore, cl::init('2')); + OptLevel("O", + cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " + "(default = '-O2')"), + cl::Prefix, cl::ZeroOrMore, cl::init('2'), cl::cat(LTOCategory)); static cl::opt<bool> IndexStats("thinlto-index-stats", cl::desc("Print statistic for the index in every input files"), - cl::init(false)); + cl::init(false), cl::cat(LTOCategory)); static cl::opt<bool> DisableVerify( "disable-verify", cl::init(false), - cl::desc("Do not run the verifier during the optimization pipeline")); + cl::desc("Do not run the verifier during the optimization pipeline"), + cl::cat(LTOCategory)); static cl::opt<bool> EnableFreestanding( "lto-freestanding", cl::init(false), - cl::desc("Enable Freestanding (disable builtins / TLI) during LTO")); + cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"), + cl::cat(LTOCategory)); static cl::opt<bool> UseDiagnosticHandler( "use-diagnostic-handler", cl::init(false), - cl::desc("Use a diagnostic handler to test the handler interface")); + cl::desc("Use a diagnostic handler to test the handler interface"), + cl::cat(LTOCategory)); static cl::opt<bool> ThinLTO("thinlto", cl::init(false), - cl::desc("Only write combined global index for ThinLTO backends")); + cl::desc("Only write combined global index for ThinLTO backends"), + cl::cat(LTOCategory)); enum ThinLTOModes { THINLINK, @@ -114,113 +121,144 @@ cl::opt<ThinLTOModes> ThinLTOMode( "Emit imports files for distributed backends."), clEnumValN(THINPROMOTE, "promote", "Perform pre-import promotion (requires -thinlto-index)."), - clEnumValN(THINIMPORT, "import", "Perform both promotion and " - "cross-module importing (requires " - "-thinlto-index)."), + clEnumValN(THINIMPORT, "import", + "Perform both promotion and " + "cross-module importing (requires " + "-thinlto-index)."), clEnumValN(THININTERNALIZE, "internalize", "Perform internalization driven by -exported-symbol " "(requires -thinlto-index)."), clEnumValN(THINOPT, "optimize", "Perform ThinLTO optimizations."), clEnumValN(THINCODEGEN, "codegen", "CodeGen (expected to match llc)"), - clEnumValN(THINALL, "run", "Perform ThinLTO end-to-end"))); + clEnumValN(THINALL, "run", "Perform ThinLTO end-to-end")), + cl::cat(LTOCategory)); static cl::opt<std::string> ThinLTOIndex("thinlto-index", cl::desc("Provide the index produced by a ThinLink, required " - "to perform the promotion and/or importing.")); + "to perform the promotion and/or importing."), + cl::cat(LTOCategory)); static cl::opt<std::string> ThinLTOPrefixReplace( "thinlto-prefix-replace", cl::desc("Control where files for distributed backends are " "created. Expects 'oldprefix;newprefix' and if path " "prefix of output file is oldprefix it will be " - "replaced with newprefix.")); + "replaced with newprefix."), + cl::cat(LTOCategory)); static cl::opt<std::string> ThinLTOModuleId( "thinlto-module-id", cl::desc("For the module ID for the file to process, useful to " - "match what is in the index.")); + "match what is in the index."), + cl::cat(LTOCategory)); -static cl::opt<std::string> - ThinLTOCacheDir("thinlto-cache-dir", cl::desc("Enable ThinLTO caching.")); +static cl::opt<std::string> ThinLTOCacheDir("thinlto-cache-dir", + cl::desc("Enable ThinLTO caching."), + cl::cat(LTOCategory)); -static cl::opt<int> - ThinLTOCachePruningInterval("thinlto-cache-pruning-interval", - cl::init(1200), cl::desc("Set ThinLTO cache pruning interval.")); +static cl::opt<int> ThinLTOCachePruningInterval( + "thinlto-cache-pruning-interval", cl::init(1200), + cl::desc("Set ThinLTO cache pruning interval."), cl::cat(LTOCategory)); static cl::opt<uint64_t> ThinLTOCacheMaxSizeBytes( "thinlto-cache-max-size-bytes", - cl::desc("Set ThinLTO cache pruning directory maximum size in bytes.")); + cl::desc("Set ThinLTO cache pruning directory maximum size in bytes."), + cl::cat(LTOCategory)); -static cl::opt<int> - ThinLTOCacheMaxSizeFiles("thinlto-cache-max-size-files", cl::init(1000000), - cl::desc("Set ThinLTO cache pruning directory maximum number of files.")); +static cl::opt<int> ThinLTOCacheMaxSizeFiles( + "thinlto-cache-max-size-files", cl::init(1000000), + cl::desc("Set ThinLTO cache pruning directory maximum number of files."), + cl::cat(LTOCategory)); -static cl::opt<unsigned> - ThinLTOCacheEntryExpiration("thinlto-cache-entry-expiration", cl::init(604800) /* 1w */, - cl::desc("Set ThinLTO cache entry expiration time.")); +static cl::opt<unsigned> ThinLTOCacheEntryExpiration( + "thinlto-cache-entry-expiration", cl::init(604800) /* 1w */, + cl::desc("Set ThinLTO cache entry expiration time."), cl::cat(LTOCategory)); static cl::opt<std::string> ThinLTOSaveTempsPrefix( "thinlto-save-temps", cl::desc("Save ThinLTO temp files using filenames created by adding " - "suffixes to the given file path prefix.")); + "suffixes to the given file path prefix."), + cl::cat(LTOCategory)); static cl::opt<std::string> ThinLTOGeneratedObjectsDir( "thinlto-save-objects", cl::desc("Save ThinLTO generated object files using filenames created in " - "the given directory.")); + "the given directory."), + cl::cat(LTOCategory)); static cl::opt<bool> SaveLinkedModuleFile( "save-linked-module", cl::init(false), - cl::desc("Write linked LTO module to file before optimize")); + cl::desc("Write linked LTO module to file before optimize"), + cl::cat(LTOCategory)); static cl::opt<bool> SaveModuleFile("save-merged-module", cl::init(false), - cl::desc("Write merged LTO module to file before CodeGen")); + cl::desc("Write merged LTO module to file before CodeGen"), + cl::cat(LTOCategory)); static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, - cl::desc("<input bitcode files>")); + cl::desc("<input bitcode files>"), + cl::cat(LTOCategory)); static cl::opt<std::string> OutputFilename("o", cl::init(""), cl::desc("Override output filename"), - cl::value_desc("filename")); + cl::value_desc("filename"), + cl::cat(LTOCategory)); static cl::list<std::string> ExportedSymbols( "exported-symbol", cl::desc("List of symbols to export from the resulting object file"), - cl::ZeroOrMore); + cl::ZeroOrMore, cl::cat(LTOCategory)); static cl::list<std::string> DSOSymbols("dso-symbol", cl::desc("Symbol to put in the symtab in the resulting dso"), - cl::ZeroOrMore); + cl::ZeroOrMore, cl::cat(LTOCategory)); static cl::opt<bool> ListSymbolsOnly( "list-symbols-only", cl::init(false), - cl::desc("Instead of running LTO, list the symbols in each IR file")); + cl::desc("Instead of running LTO, list the symbols in each IR file"), + cl::cat(LTOCategory)); static cl::opt<bool> ListDependentLibrariesOnly( "list-dependent-libraries-only", cl::init(false), - cl::desc("Instead of running LTO, list the dependent libraries in each IR file")); + cl::desc( + "Instead of running LTO, list the dependent libraries in each IR file"), + cl::cat(LTOCategory)); -static cl::opt<bool> SetMergedModule( - "set-merged-module", cl::init(false), - cl::desc("Use the first input module as the merged module")); +static cl::opt<bool> + SetMergedModule("set-merged-module", cl::init(false), + cl::desc("Use the first input module as the merged module"), + cl::cat(LTOCategory)); static cl::opt<unsigned> Parallelism("j", cl::Prefix, cl::init(1), - cl::desc("Number of backend threads")); + cl::desc("Number of backend threads"), + cl::cat(LTOCategory)); static cl::opt<bool> RestoreGlobalsLinkage( "restore-linkage", cl::init(false), - cl::desc("Restore original linkage of globals prior to CodeGen")); + cl::desc("Restore original linkage of globals prior to CodeGen"), + cl::cat(LTOCategory)); static cl::opt<bool> CheckHasObjC( "check-for-objc", cl::init(false), - cl::desc("Only check if the module has objective-C defined in it")); + cl::desc("Only check if the module has objective-C defined in it"), + cl::cat(LTOCategory)); static cl::opt<bool> PrintMachOCPUOnly( "print-macho-cpu-only", cl::init(false), - cl::desc("Instead of running LTO, print the mach-o cpu in each IR file")); + cl::desc("Instead of running LTO, print the mach-o cpu in each IR file"), + cl::cat(LTOCategory)); + +static cl::opt<bool> UseNewPM( + "use-new-pm", cl::desc("Run LTO passes using the new pass manager"), + cl::init(LLVM_ENABLE_NEW_PASS_MANAGER), cl::Hidden, cl::cat(LTOCategory)); + +static cl::opt<bool> + DebugPassManager("debug-pass-manager", cl::init(false), cl::Hidden, + cl::desc("Print pass management debugging information"), + cl::cat(LTOCategory)); namespace { @@ -552,6 +590,8 @@ public: ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles); ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes); ThinGenerator.setFreestanding(EnableFreestanding); + ThinGenerator.setUseNewPM(UseNewPM); + ThinGenerator.setDebugPassManager(DebugPassManager); // Add all the exported symbols to the table of symbols to preserve. for (unsigned i = 0; i < ExportedSymbols.size(); ++i) @@ -884,6 +924,7 @@ private: int main(int argc, char **argv) { InitLLVM X(argc, argv); + cl::HideUnrelatedOptions({<OCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm LTO linker\n"); if (OptLevel < '0' || OptLevel > '3') @@ -1014,6 +1055,8 @@ int main(int argc, char **argv) { CodeGen.setOptLevel(OptLevel - '0'); CodeGen.setAttrs(codegen::getMAttrs()); + CodeGen.setUseNewPM(UseNewPM); + if (auto FT = codegen::getExplicitFileType()) CodeGen.setFileType(FT.getValue()); @@ -1041,25 +1084,24 @@ int main(int argc, char **argv) { error("writing merged module failed."); } - std::list<ToolOutputFile> OSs; - std::vector<raw_pwrite_stream *> OSPtrs; - for (unsigned I = 0; I != Parallelism; ++I) { + auto AddStream = + [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> { std::string PartFilename = OutputFilename; if (Parallelism != 1) - PartFilename += "." + utostr(I); + PartFilename += "." + utostr(Task); + std::error_code EC; - OSs.emplace_back(PartFilename, EC, sys::fs::OF_None); + auto S = + std::make_unique<raw_fd_ostream>(PartFilename, EC, sys::fs::OF_None); if (EC) error("error opening the file '" + PartFilename + "': " + EC.message()); - OSPtrs.push_back(&OSs.back().os()); - } + return std::make_unique<lto::NativeObjectStream>(std::move(S)); + }; - if (!CodeGen.compileOptimized(OSPtrs)) + if (!CodeGen.compileOptimized(AddStream, Parallelism)) // Diagnostic messages should have been printed by the handler. error("error compiling the code"); - for (ToolOutputFile &OS : OSs) - OS.keep(); } else { if (Parallelism != 1) error("-j must be specified together with -o"); diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp index ca4278fafb89..c0bff1eabee2 100644 --- a/llvm/tools/llvm-lto2/llvm-lto2.cpp +++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp @@ -325,12 +325,12 @@ static int run(int argc, char **argv) { std::vector<SymbolResolution> Res; for (const InputFile::Symbol &Sym : Input->symbols()) { auto I = CommandLineResolutions.find({F, std::string(Sym.getName())}); - // If it isn't found, look for "$", which would have been added + // If it isn't found, look for ".", which would have been added // (followed by a hash) when the symbol was promoted during module // splitting if it was defined in one part and used in the other. - // Try looking up the symbol name before the "$". + // Try looking up the symbol name before the suffix. if (I == CommandLineResolutions.end()) { - auto SplitName = Sym.getName().rsplit("$"); + auto SplitName = Sym.getName().rsplit("."); I = CommandLineResolutions.find({F, std::string(SplitName.first)}); } if (I == CommandLineResolutions.end()) { @@ -418,7 +418,8 @@ static int dumpSymtab(int argc, char **argv) { outs() << '\n'; } - std::vector<StringRef> ComdatTable = Input->getComdatTable(); + ArrayRef<std::pair<StringRef, Comdat::SelectionKind>> ComdatTable = + Input->getComdatTable(); for (const InputFile::Symbol &Sym : Input->symbols()) { switch (Sym.getVisibility()) { case GlobalValue::HiddenVisibility: @@ -447,8 +448,27 @@ static int dumpSymtab(int argc, char **argv) { << Sym.getCommonAlignment() << '\n'; int Comdat = Sym.getComdatIndex(); - if (Comdat != -1) - outs() << " comdat " << ComdatTable[Comdat] << '\n'; + if (Comdat != -1) { + outs() << " comdat "; + switch (ComdatTable[Comdat].second) { + case Comdat::Any: + outs() << "any"; + break; + case Comdat::ExactMatch: + outs() << "exactmatch"; + break; + case Comdat::Largest: + outs() << "largest"; + break; + case Comdat::NoDeduplicate: + outs() << "nodeduplicate"; + break; + case Comdat::SameSize: + outs() << "samesize"; + break; + } + outs() << ' ' << ComdatTable[Comdat].first << '\n'; + } if (TT.isOSBinFormatCOFF() && Sym.isWeak() && Sym.isIndirect()) outs() << " fallback " << Sym.getCOFFWeakExternalFallback() << '\n'; diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp index f3a66187dd0a..24c601b7033f 100644 --- a/llvm/tools/llvm-mc/llvm-mc.cpp +++ b/llvm/tools/llvm-mc/llvm-mc.cpp @@ -43,23 +43,33 @@ using namespace llvm; static mc::RegisterMCTargetOptionsFlags MOF; -static cl::opt<std::string> -InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-")); +static cl::OptionCategory MCCategory("MC Options"); + +static cl::opt<std::string> InputFilename(cl::Positional, + cl::desc("<input file>"), + cl::init("-"), cl::cat(MCCategory)); + +static cl::list<std::string> + DisassemblerOptions("M", cl::desc("Disassembler options"), + cl::cat(MCCategory)); static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), - cl::init("-")); + cl::init("-"), cl::cat(MCCategory)); static cl::opt<std::string> SplitDwarfFile("split-dwarf-file", cl::desc("DWO output filename"), - cl::value_desc("filename")); + cl::value_desc("filename"), + cl::cat(MCCategory)); -static cl::opt<bool> -ShowEncoding("show-encoding", cl::desc("Show instruction encodings")); +static cl::opt<bool> ShowEncoding("show-encoding", + cl::desc("Show instruction encodings"), + cl::cat(MCCategory)); static cl::opt<bool> RelaxELFRel( "relax-relocations", cl::init(true), - cl::desc("Emit R_X86_64_GOTPCRELX instead of R_X86_64_GOTPCREL")); + cl::desc("Emit R_X86_64_GOTPCRELX instead of R_X86_64_GOTPCREL"), + cl::cat(MCCategory)); static cl::opt<DebugCompressionType> CompressDebugSections( "compress-debug-sections", cl::ValueOptional, @@ -69,29 +79,37 @@ static cl::opt<DebugCompressionType> CompressDebugSections( clEnumValN(DebugCompressionType::Z, "zlib", "Use zlib compression"), clEnumValN(DebugCompressionType::GNU, "zlib-gnu", - "Use zlib-gnu compression (deprecated)"))); + "Use zlib-gnu compression (deprecated)")), + cl::cat(MCCategory)); static cl::opt<bool> -ShowInst("show-inst", cl::desc("Show internal instruction representation")); + ShowInst("show-inst", cl::desc("Show internal instruction representation"), + cl::cat(MCCategory)); static cl::opt<bool> -ShowInstOperands("show-inst-operands", - cl::desc("Show instructions operands as parsed")); + ShowInstOperands("show-inst-operands", + cl::desc("Show instructions operands as parsed"), + cl::cat(MCCategory)); static cl::opt<unsigned> -OutputAsmVariant("output-asm-variant", - cl::desc("Syntax variant to use for output printing")); + OutputAsmVariant("output-asm-variant", + cl::desc("Syntax variant to use for output printing"), + cl::cat(MCCategory)); static cl::opt<bool> -PrintImmHex("print-imm-hex", cl::init(false), - cl::desc("Prefer hex format for immediate values")); + PrintImmHex("print-imm-hex", cl::init(false), + cl::desc("Prefer hex format for immediate values"), + cl::cat(MCCategory)); static cl::list<std::string> -DefineSymbol("defsym", cl::desc("Defines a symbol to be an integer constant")); + DefineSymbol("defsym", + cl::desc("Defines a symbol to be an integer constant"), + cl::cat(MCCategory)); static cl::opt<bool> PreserveComments("preserve-comments", - cl::desc("Preserve Comments in outputted assembly")); + cl::desc("Preserve Comments in outputted assembly"), + cl::cat(MCCategory)); enum OutputFileType { OFT_Null, @@ -99,82 +117,101 @@ enum OutputFileType { OFT_ObjectFile }; static cl::opt<OutputFileType> -FileType("filetype", cl::init(OFT_AssemblyFile), - cl::desc("Choose an output file type:"), - cl::values( - clEnumValN(OFT_AssemblyFile, "asm", - "Emit an assembly ('.s') file"), - clEnumValN(OFT_Null, "null", - "Don't emit anything (for timing purposes)"), - clEnumValN(OFT_ObjectFile, "obj", - "Emit a native object ('.o') file"))); - -static cl::list<std::string> -IncludeDirs("I", cl::desc("Directory of include files"), - cl::value_desc("directory"), cl::Prefix); + FileType("filetype", cl::init(OFT_AssemblyFile), + cl::desc("Choose an output file type:"), + cl::values(clEnumValN(OFT_AssemblyFile, "asm", + "Emit an assembly ('.s') file"), + clEnumValN(OFT_Null, "null", + "Don't emit anything (for timing purposes)"), + clEnumValN(OFT_ObjectFile, "obj", + "Emit a native object ('.o') file")), + cl::cat(MCCategory)); + +static cl::list<std::string> IncludeDirs("I", + cl::desc("Directory of include files"), + cl::value_desc("directory"), + cl::Prefix, cl::cat(MCCategory)); static cl::opt<std::string> -ArchName("arch", cl::desc("Target arch to assemble for, " - "see -version for available targets")); + ArchName("arch", + cl::desc("Target arch to assemble for, " + "see -version for available targets"), + cl::cat(MCCategory)); static cl::opt<std::string> -TripleName("triple", cl::desc("Target triple to assemble for, " - "see -version for available targets")); + TripleName("triple", + cl::desc("Target triple to assemble for, " + "see -version for available targets"), + cl::cat(MCCategory)); static cl::opt<std::string> -MCPU("mcpu", - cl::desc("Target a specific cpu type (-mcpu=help for details)"), - cl::value_desc("cpu-name"), - cl::init("")); + MCPU("mcpu", + cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), cl::init(""), cl::cat(MCCategory)); static cl::list<std::string> -MAttrs("mattr", - cl::CommaSeparated, - cl::desc("Target specific attributes (-mattr=help for details)"), - cl::value_desc("a1,+a2,-a3,...")); + MAttrs("mattr", cl::CommaSeparated, + cl::desc("Target specific attributes (-mattr=help for details)"), + cl::value_desc("a1,+a2,-a3,..."), cl::cat(MCCategory)); static cl::opt<bool> PIC("position-independent", - cl::desc("Position independent"), cl::init(false)); + cl::desc("Position independent"), cl::init(false), + cl::cat(MCCategory)); static cl::opt<bool> LargeCodeModel("large-code-model", cl::desc("Create cfi directives that assume the code might " - "be more than 2gb away")); + "be more than 2gb away"), + cl::cat(MCCategory)); static cl::opt<bool> -NoInitialTextSection("n", cl::desc("Don't assume assembly file starts " - "in the text section")); + NoInitialTextSection("n", + cl::desc("Don't assume assembly file starts " + "in the text section"), + cl::cat(MCCategory)); static cl::opt<bool> -GenDwarfForAssembly("g", cl::desc("Generate dwarf debugging info for assembly " - "source files")); + GenDwarfForAssembly("g", + cl::desc("Generate dwarf debugging info for assembly " + "source files"), + cl::cat(MCCategory)); static cl::opt<std::string> -DebugCompilationDir("fdebug-compilation-dir", - cl::desc("Specifies the debug info's compilation dir")); + DebugCompilationDir("fdebug-compilation-dir", + cl::desc("Specifies the debug info's compilation dir"), + cl::cat(MCCategory)); -static cl::list<std::string> -DebugPrefixMap("fdebug-prefix-map", - cl::desc("Map file source paths in debug info"), - cl::value_desc("= separated key-value pairs")); +static cl::list<std::string> DebugPrefixMap( + "fdebug-prefix-map", cl::desc("Map file source paths in debug info"), + cl::value_desc("= separated key-value pairs"), cl::cat(MCCategory)); -static cl::opt<std::string> -MainFileName("main-file-name", - cl::desc("Specifies the name we should consider the input file")); +static cl::opt<std::string> MainFileName( + "main-file-name", + cl::desc("Specifies the name we should consider the input file"), + cl::cat(MCCategory)); static cl::opt<bool> SaveTempLabels("save-temp-labels", - cl::desc("Don't discard temporary labels")); + cl::desc("Don't discard temporary labels"), + cl::cat(MCCategory)); static cl::opt<bool> LexMasmIntegers( "masm-integers", - cl::desc("Enable binary and hex masm integers (0b110 and 0ABCh)")); + cl::desc("Enable binary and hex masm integers (0b110 and 0ABCh)"), + cl::cat(MCCategory)); static cl::opt<bool> LexMasmHexFloats( "masm-hexfloats", - cl::desc("Enable MASM-style hex float initializers (3F800000r)")); + cl::desc("Enable MASM-style hex float initializers (3F800000r)"), + cl::cat(MCCategory)); + +static cl::opt<bool> LexMotorolaIntegers( + "motorola-integers", + cl::desc("Enable binary and hex Motorola integers (%110 and $ABC)"), + cl::cat(MCCategory)); static cl::opt<bool> NoExecStack("no-exec-stack", - cl::desc("File doesn't need an exec stack")); + cl::desc("File doesn't need an exec stack"), + cl::cat(MCCategory)); enum ActionType { AC_AsLex, @@ -183,17 +220,16 @@ enum ActionType { AC_MDisassemble, }; -static cl::opt<ActionType> -Action(cl::desc("Action to perform:"), - cl::init(AC_Assemble), - cl::values(clEnumValN(AC_AsLex, "as-lex", - "Lex tokens from a .s file"), - clEnumValN(AC_Assemble, "assemble", - "Assemble a .s file (default)"), - clEnumValN(AC_Disassemble, "disassemble", - "Disassemble strings of hex bytes"), - clEnumValN(AC_MDisassemble, "mdis", - "Marked up disassembly of strings of hex bytes"))); +static cl::opt<ActionType> Action( + cl::desc("Action to perform:"), cl::init(AC_Assemble), + cl::values(clEnumValN(AC_AsLex, "as-lex", "Lex tokens from a .s file"), + clEnumValN(AC_Assemble, "assemble", + "Assemble a .s file (default)"), + clEnumValN(AC_Disassemble, "disassemble", + "Disassemble strings of hex bytes"), + clEnumValN(AC_MDisassemble, "mdis", + "Marked up disassembly of strings of hex bytes")), + cl::cat(MCCategory)); static const Target *GetTarget(const char *ProgName) { // Figure out the target triple. @@ -305,6 +341,7 @@ static int AssembleInput(const char *ProgName, const Target *TheTarget, Parser->setTargetParser(*TAP); Parser->getLexer().setLexMasmIntegers(LexMasmIntegers); Parser->getLexer().setLexMasmHexFloats(LexMasmHexFloats); + Parser->getLexer().setLexMotorolaIntegers(LexMotorolaIntegers); int Res = Parser->Run(NoInitialTextSection); @@ -323,6 +360,7 @@ int main(int argc, char **argv) { // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + cl::HideUnrelatedOptions({&MCCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n"); const MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); setDwarfDebugFlags(argc, argv); @@ -338,7 +376,7 @@ int main(int argc, char **argv) { Triple TheTriple(TripleName); ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr = - MemoryBuffer::getFileOrSTDIN(InputFilename); + MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true); if (std::error_code EC = BufferPtr.getError()) { WithColor::error(errs(), ProgName) << InputFilename << ": " << EC.message() << '\n'; @@ -374,11 +412,26 @@ int main(int argc, char **argv) { } MAI->setPreserveAsmComments(PreserveComments); + // Package up features to be passed to target/subtarget + std::string FeaturesStr; + if (MAttrs.size()) { + SubtargetFeatures Features; + for (unsigned i = 0; i != MAttrs.size(); ++i) + Features.AddFeature(MAttrs[i]); + FeaturesStr = Features.getString(); + } + + std::unique_ptr<MCSubtargetInfo> STI( + TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); + assert(STI && "Unable to create subtarget info!"); + // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and // MCObjectFileInfo needs a MCContext reference in order to initialize itself. - MCObjectFileInfo MOFI; - MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr, &MCOptions); - MOFI.InitMCObjectFileInfo(TheTriple, PIC, Ctx, LargeCodeModel); + MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr, + &MCOptions); + std::unique_ptr<MCObjectFileInfo> MOFI( + TheTarget->createMCObjectFileInfo(Ctx, PIC, LargeCodeModel)); + Ctx.setObjectFileInfo(MOFI.get()); if (SaveTempLabels) Ctx.setAllowTemporaryLabels(false); @@ -438,17 +491,9 @@ int main(int argc, char **argv) { if (GenDwarfForAssembly) Ctx.setGenDwarfRootFile(InputFilename, Buffer->getBuffer()); - // Package up features to be passed to target/subtarget - std::string FeaturesStr; - if (MAttrs.size()) { - SubtargetFeatures Features; - for (unsigned i = 0; i != MAttrs.size(); ++i) - Features.AddFeature(MAttrs[i]); - FeaturesStr = Features.getString(); - } - - sys::fs::OpenFlags Flags = (FileType == OFT_AssemblyFile) ? sys::fs::OF_Text - : sys::fs::OF_None; + sys::fs::OpenFlags Flags = (FileType == OFT_AssemblyFile) + ? sys::fs::OF_TextWithCRLF + : sys::fs::OF_None; std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename, Flags); if (!Out) return 1; @@ -471,10 +516,6 @@ int main(int argc, char **argv) { std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); assert(MCII && "Unable to create instruction info!"); - std::unique_ptr<MCSubtargetInfo> STI( - TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); - assert(STI && "Unable to create subtarget info!"); - MCInstPrinter *IP = nullptr; if (FileType == OFT_AssemblyFile) { IP = TheTarget->createMCInstPrinter(Triple(TripleName), OutputAsmVariant, @@ -488,6 +529,12 @@ int main(int argc, char **argv) { return 1; } + for (StringRef Opt : DisassemblerOptions) + if (!IP->applyTargetSpecificCLOption(Opt)) { + WithColor::error() << "invalid disassembler option '" << Opt << "'\n"; + return 1; + } + // Set the display preference for hex vs. decimal immediates. IP->setPrintImmHex(PrintImmHex); diff --git a/llvm/tools/llvm-mca/CodeRegion.h b/llvm/tools/llvm-mca/CodeRegion.h index d2b05fa80c54..0b2590767dfa 100644 --- a/llvm/tools/llvm-mca/CodeRegion.h +++ b/llvm/tools/llvm-mca/CodeRegion.h @@ -53,7 +53,7 @@ class CodeRegion { // An optional descriptor for this region. llvm::StringRef Description; // Instructions that form this region. - llvm::SmallVector<llvm::MCInst, 8> Instructions; + llvm::SmallVector<llvm::MCInst, 16> Instructions; // Source location range. llvm::SMLoc RangeStart; llvm::SMLoc RangeEnd; diff --git a/llvm/tools/llvm-mca/CodeRegionGenerator.cpp b/llvm/tools/llvm-mca/CodeRegionGenerator.cpp index 831b76ab80cf..6ad2a65592b9 100644 --- a/llvm/tools/llvm-mca/CodeRegionGenerator.cpp +++ b/llvm/tools/llvm-mca/CodeRegionGenerator.cpp @@ -106,11 +106,21 @@ void MCACommentConsumer::HandleComment(SMLoc Loc, StringRef CommentText) { Regions.beginRegion(Comment, Loc); } -Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions() { +Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions( + const std::unique_ptr<MCInstPrinter> &IP) { MCTargetOptions Opts; Opts.PreserveAsmComments = false; MCStreamerWrapper Str(Ctx, Regions); + // Need to initialize an MCTargetStreamer otherwise + // certain asm directives will cause a segfault. + // Using nulls() so that anything emitted by the MCTagetStreamer + // doesn't show up in the llvm-mca output. + raw_ostream &OSRef = nulls(); + formatted_raw_ostream FOSRef(OSRef); + TheTarget.createAsmTargetStreamer(Str, FOSRef, IP.get(), + /*IsVerboseAsm=*/true); + // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM // comments. std::unique_ptr<MCAsmParser> Parser( diff --git a/llvm/tools/llvm-mca/CodeRegionGenerator.h b/llvm/tools/llvm-mca/CodeRegionGenerator.h index 9a10aa2c148b..1c11784ca3fb 100644 --- a/llvm/tools/llvm-mca/CodeRegionGenerator.h +++ b/llvm/tools/llvm-mca/CodeRegionGenerator.h @@ -39,7 +39,8 @@ protected: public: CodeRegionGenerator(SourceMgr &SM) : Regions(SM) {} virtual ~CodeRegionGenerator(); - virtual Expected<const CodeRegions &> parseCodeRegions() = 0; + virtual Expected<const CodeRegions &> + parseCodeRegions(const std::unique_ptr<MCInstPrinter> &IP) = 0; }; /// This class is responsible for parsing input ASM and generating @@ -60,7 +61,8 @@ public: AssemblerDialect(0) {} unsigned getAssemblerDialect() const { return AssemblerDialect; } - Expected<const CodeRegions &> parseCodeRegions() override; + Expected<const CodeRegions &> + parseCodeRegions(const std::unique_ptr<MCInstPrinter> &IP) override; }; } // namespace mca diff --git a/llvm/tools/llvm-mca/PipelinePrinter.cpp b/llvm/tools/llvm-mca/PipelinePrinter.cpp index e7dfbfdce26d..955b825891fa 100644 --- a/llvm/tools/llvm-mca/PipelinePrinter.cpp +++ b/llvm/tools/llvm-mca/PipelinePrinter.cpp @@ -12,14 +12,119 @@ //===----------------------------------------------------------------------===// #include "PipelinePrinter.h" +#include "CodeRegion.h" +#include "Views/InstructionView.h" #include "Views/View.h" namespace llvm { namespace mca { +void PipelinePrinter::printRegionHeader(llvm::raw_ostream &OS) const { + StringRef RegionName; + if (!Region.getDescription().empty()) + RegionName = Region.getDescription(); + + OS << "\n[" << RegionIdx << "] Code Region"; + if (!RegionName.empty()) + OS << " - " << RegionName; + OS << "\n\n"; +} + +json::Object PipelinePrinter::getJSONReportRegion() const { + json::Object JO; + + StringRef RegionName = ""; + if (!Region.getDescription().empty()) + RegionName = Region.getDescription(); + + JO.try_emplace("Name", RegionName); + for (const auto &V : Views) + if (V->isSerializable()) + JO.try_emplace(V->getNameAsString().str(), V->toJSON()); + + return JO; +} + +json::Object PipelinePrinter::getJSONSimulationParameters() const { + json::Object SimParameters({{"-mcpu", STI.getCPU()}, + {"-mtriple", STI.getTargetTriple().getTriple()}, + {"-march", STI.getTargetTriple().getArchName()}}); + + const MCSchedModel &SM = STI.getSchedModel(); + if (!SM.isOutOfOrder()) + return SimParameters; + + if (PO.RegisterFileSize) + SimParameters.try_emplace("-register-file-size", PO.RegisterFileSize); + + if (!PO.AssumeNoAlias) + SimParameters.try_emplace("-noalias", PO.AssumeNoAlias); + + if (PO.DecodersThroughput) + SimParameters.try_emplace("-decoder-throughput", PO.DecodersThroughput); + + if (PO.MicroOpQueueSize) + SimParameters.try_emplace("-micro-op-queue-size", PO.MicroOpQueueSize); + + if (PO.DispatchWidth) + SimParameters.try_emplace("-dispatch", PO.DispatchWidth); + + if (PO.LoadQueueSize) + SimParameters.try_emplace("-lqueue", PO.LoadQueueSize); + + if (PO.StoreQueueSize) + SimParameters.try_emplace("-squeue", PO.StoreQueueSize); + + return SimParameters; +} + +json::Object PipelinePrinter::getJSONTargetInfo() const { + json::Array Resources; + const MCSchedModel &SM = STI.getSchedModel(); + StringRef MCPU = STI.getCPU(); + + for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) { + const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); + unsigned NumUnits = ProcResource.NumUnits; + if (ProcResource.SubUnitsIdxBegin || !NumUnits) + continue; + + for (unsigned J = 0; J < NumUnits; ++J) { + std::string ResourceName = ProcResource.Name; + if (NumUnits > 1) { + ResourceName += "."; + ResourceName += J; + } + + Resources.push_back(ResourceName); + } + } + + return json::Object({{"CPUName", MCPU}, {"Resources", std::move(Resources)}}); +} + +void PipelinePrinter::printReport(json::Object &JO) const { + if (!RegionIdx) { + JO.try_emplace("TargetInfo", getJSONTargetInfo()); + JO.try_emplace("SimulationParameters", getJSONSimulationParameters()); + // Construct an array of regions. + JO.try_emplace("CodeRegions", json::Array()); + } + + json::Array *Regions = JO.getArray("CodeRegions"); + assert(Regions && "This array must exist!"); + Regions->push_back(getJSONReportRegion()); +} + void PipelinePrinter::printReport(llvm::raw_ostream &OS) const { + // Don't print the header of this region if it is the default region, and if + // it doesn't have an end location. + if (Region.startLoc().isValid() || Region.endLoc().isValid()) + printRegionHeader(OS); + for (const auto &V : Views) - V->printView(OutputKind, OS); + V->printView(OS); } -} // namespace mca. + +} // namespace mca } // namespace llvm diff --git a/llvm/tools/llvm-mca/PipelinePrinter.h b/llvm/tools/llvm-mca/PipelinePrinter.h index ae18140d32b7..1365f75be0f5 100644 --- a/llvm/tools/llvm-mca/PipelinePrinter.h +++ b/llvm/tools/llvm-mca/PipelinePrinter.h @@ -18,6 +18,8 @@ #include "Views/View.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MCA/Context.h" #include "llvm/MCA/Pipeline.h" #include "llvm/Support/raw_ostream.h" @@ -26,6 +28,8 @@ namespace llvm { namespace mca { +class CodeRegion; + /// A printer class that knows how to collects statistics on the /// code analyzed by the llvm-mca tool. /// @@ -35,12 +39,21 @@ namespace mca { /// resource pressure. class PipelinePrinter { Pipeline &P; + const CodeRegion &Region; + unsigned RegionIdx; + const MCSubtargetInfo &STI; + const PipelineOptions &PO; llvm::SmallVector<std::unique_ptr<View>, 8> Views; - View::OutputKind OutputKind; + + void printRegionHeader(llvm::raw_ostream &OS) const; + json::Object getJSONReportRegion() const; + json::Object getJSONTargetInfo() const; + json::Object getJSONSimulationParameters() const; public: - PipelinePrinter(Pipeline &pipeline, View::OutputKind OutputKind) - : P(pipeline), OutputKind(OutputKind) {} + PipelinePrinter(Pipeline &Pipe, const CodeRegion &R, unsigned Idx, + const MCSubtargetInfo &STI, const PipelineOptions &PO) + : P(Pipe), Region(R), RegionIdx(Idx), STI(STI), PO(PO), Views() {} void addView(std::unique_ptr<View> V) { P.addEventListener(V.get()); @@ -48,6 +61,7 @@ public: } void printReport(llvm::raw_ostream &OS) const; + void printReport(json::Object &JO) const; }; } // namespace mca } // namespace llvm diff --git a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp index 38a8e2ef9c53..5b110d6602df 100644 --- a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp +++ b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp @@ -66,8 +66,6 @@ void PressureTracker::onInstructionExecuted(unsigned IID) { IPI.erase(IID); } void PressureTracker::handleInstructionIssuedEvent( const HWInstructionIssuedEvent &Event) { unsigned IID = Event.IR.getSourceIndex(); - using ResourceRef = HWInstructionIssuedEvent::ResourceRef; - using ResourceUse = std::pair<ResourceRef, ResourceCycles>; for (const ResourceUse &Use : Event.UsedResources) { const ResourceRef &RR = Use.first; unsigned Index = ProcResID2ResourceUsersIndex[RR.first]; @@ -200,8 +198,8 @@ void DependencyGraph::initializeRootSet( } } -void DependencyGraph::propagateThroughEdges( - SmallVectorImpl<unsigned> &RootSet, unsigned Iterations) { +void DependencyGraph::propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet, + unsigned Iterations) { SmallVector<unsigned, 8> ToVisit; // A critical sequence is computed as the longest path from a node of the @@ -223,14 +221,14 @@ void DependencyGraph::propagateThroughEdges( // The `unvisited nodes` set initially contains all the nodes from the // RootSet. A node N is added to the `unvisited nodes` if all its // predecessors have been visited already. - // + // // For simplicity, every node tracks the number of unvisited incoming edges in // field `NumVisitedPredecessors`. When the value of that field drops to // zero, then the corresponding node is added to a `ToVisit` set. // // At the end of every iteration of the outer loop, set `ToVisit` becomes our // new `unvisited nodes` set. - // + // // The algorithm terminates when the set of unvisited nodes (i.e. our RootSet) // is empty. This algorithm works under the assumption that the graph is // acyclic. @@ -269,8 +267,9 @@ void DependencyGraph::getCriticalSequence( // that node is the last instruction of our critical sequence. // Field N.Depth would tell us the total length of the sequence. // - // To obtain the sequence of critical edges, we simply follow the chain of critical - // predecessors starting from node N (field DGNode::CriticalPredecessor). + // To obtain the sequence of critical edges, we simply follow the chain of + // critical predecessors starting from node N (field + // DGNode::CriticalPredecessor). const auto It = std::max_element( Nodes.begin(), Nodes.end(), [](const DGNode &Lhs, const DGNode &Rhs) { return Lhs.Cost < Rhs.Cost; }); diff --git a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h index 427937d9e3d7..cd5af0afcf5b 100644 --- a/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h +++ b/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h @@ -33,9 +33,9 @@ /// In particular, this occurs when there is a delta between the number of uOps /// dispatched and the number of uOps issued to the underlying pipelines. /// -/// The bottleneck analysis view is also responsible for identifying and printing -/// the most "critical" sequence of dependent instructions according to the -/// simulated run. +/// The bottleneck analysis view is also responsible for identifying and +/// printing the most "critical" sequence of dependent instructions according to +/// the simulated run. /// /// Below is the critical sequence computed for the dot-product example on /// btver2: @@ -62,13 +62,14 @@ /// and edges of the graph represent data dependencies or processor resource /// interferences. /// -/// Edges are dynamically 'discovered' by observing instruction state transitions -/// and backend pressure increase events. Edges are internally ranked based on -/// their "criticality". A dependency is considered to be critical if it takes a -/// long time to execute, and if it contributes to backend pressure increases. -/// Criticality is internally measured in terms of cycles; it is computed for -/// every edge in the graph as a function of the edge latency and the number of -/// backend pressure increase cycles contributed by that edge. +/// Edges are dynamically 'discovered' by observing instruction state +/// transitions and backend pressure increase events. Edges are internally +/// ranked based on their "criticality". A dependency is considered to be +/// critical if it takes a long time to execute, and if it contributes to +/// backend pressure increases. Criticality is internally measured in terms of +/// cycles; it is computed for every edge in the graph as a function of the edge +/// latency and the number of backend pressure increase cycles contributed by +/// that edge. /// /// At the end of simulation, costs are propagated to nodes through the edges of /// the graph, and the most expensive path connecting the root-set (a @@ -217,8 +218,8 @@ struct DependencyEdge { // Loop carried dependencies are carefully expanded by the bottleneck analysis // to guarantee that the graph stays acyclic. To this end, extra nodes are // pre-allocated at construction time to describe instructions from "past and -// future" iterations. The graph is kept acyclic mainly because it simplifies the -// complexity of the algorithm that computes the critical sequence. +// future" iterations. The graph is kept acyclic mainly because it simplifies +// the complexity of the algorithm that computes the critical sequence. class DependencyGraph { struct DGNode { unsigned NumPredecessors; @@ -239,7 +240,8 @@ class DependencyGraph { void pruneEdges(unsigned Iterations); void initializeRootSet(SmallVectorImpl<unsigned> &RootSet) const; - void propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet, unsigned Iterations); + void propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet, + unsigned Iterations); #ifndef NDEBUG void dumpDependencyEdge(raw_ostream &OS, const DependencyEdge &DE, @@ -333,7 +335,7 @@ public: void printView(raw_ostream &OS) const override; StringRef getNameAsString() const override { return "BottleneckAnalysis"; } - json::Value toJSON() const override { return "not implemented"; } + bool isSerializable() const override { return false; } #ifndef NDEBUG void dump(raw_ostream &OS, MCInstPrinter &MCIP) const { DG.dump(OS, MCIP); } diff --git a/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp b/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp index a1c0cf208d35..3dc17c8754d8 100644 --- a/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp +++ b/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp @@ -1,5 +1,4 @@ -//===--------------------- DispatchStatistics.cpp ---------------------*- C++ -//-*-===// +//===--------------------- DispatchStatistics.cpp ---------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -77,10 +76,23 @@ void DispatchStatistics::printDispatchStalls(raw_ostream &OS) const { printStalls(SS, HWStalls[HWStallEvent::StoreQueueFull], NumCycles); SS << "\nGROUP - Static restrictions on the dispatch group: "; printStalls(SS, HWStalls[HWStallEvent::DispatchGroupStall], NumCycles); + SS << "\nUSH - Uncategorised Structural Hazard: "; + printStalls(SS, HWStalls[HWStallEvent::CustomBehaviourStall], NumCycles); SS << '\n'; SS.flush(); OS << Buffer; } +json::Value DispatchStatistics::toJSON() const { + json::Object JO({{"RAT", HWStalls[HWStallEvent::RegisterFileStall]}, + {"RCU", HWStalls[HWStallEvent::RetireControlUnitStall]}, + {"SCHEDQ", HWStalls[HWStallEvent::SchedulerQueueFull]}, + {"LQ", HWStalls[HWStallEvent::LoadQueueFull]}, + {"SQ", HWStalls[HWStallEvent::StoreQueueFull]}, + {"GROUP", HWStalls[HWStallEvent::DispatchGroupStall]}, + {"USH", HWStalls[HWStallEvent::CustomBehaviourStall]}}); + return JO; +} + } // namespace mca } // namespace llvm diff --git a/llvm/tools/llvm-mca/Views/DispatchStatistics.h b/llvm/tools/llvm-mca/Views/DispatchStatistics.h index 8d999fb0acfe..81b582f74a6b 100644 --- a/llvm/tools/llvm-mca/Views/DispatchStatistics.h +++ b/llvm/tools/llvm-mca/Views/DispatchStatistics.h @@ -79,6 +79,7 @@ public: printDispatchHistogram(OS); } StringRef getNameAsString() const override { return "DispatchStatistics"; } + json::Value toJSON() const override; }; } // namespace mca } // namespace llvm diff --git a/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp b/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp index 2248f63fe7e9..3f6abf4af2cf 100644 --- a/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp +++ b/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp @@ -93,7 +93,7 @@ void InstructionInfoView::collectData( MutableArrayRef<InstructionInfoViewData> IIVD) const { const llvm::MCSubtargetInfo &STI = getSubTargetInfo(); const MCSchedModel &SM = STI.getSchedModel(); - for (const auto &I : zip(getSource(), IIVD)) { + for (const auto I : zip(getSource(), IIVD)) { const MCInst &Inst = std::get<0>(I); InstructionInfoViewData &IIVDEntry = std::get<1>(I); const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode()); @@ -147,7 +147,7 @@ json::Value InstructionInfoView::toJSON() const { JO.try_emplace("Instruction", (unsigned)I.index()); InstInfo.push_back(std::move(JO)); } - return json::Value(std::move(InstInfo)); + return json::Object({{"InstructionList", json::Value(std::move(InstInfo))}}); } } // namespace mca. } // namespace llvm diff --git a/llvm/tools/llvm-mca/Views/InstructionView.cpp b/llvm/tools/llvm-mca/Views/InstructionView.cpp index 7f7a5b7cdbbb..3b174a064985 100644 --- a/llvm/tools/llvm-mca/Views/InstructionView.cpp +++ b/llvm/tools/llvm-mca/Views/InstructionView.cpp @@ -1,4 +1,4 @@ -//===----------------------- View.cpp ---------------------------*- C++ -*-===// +//===----------------------- InstructionView.cpp ----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,15 +11,18 @@ /// //===----------------------------------------------------------------------===// -#include <sstream> #include "Views/InstructionView.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCSubtargetInfo.h" namespace llvm { namespace mca { -StringRef InstructionView::printInstructionString(const llvm::MCInst &MCI) const { +InstructionView::~InstructionView() = default; + +StringRef +InstructionView::printInstructionString(const llvm::MCInst &MCI) const { InstructionString = ""; MCIP.printInst(&MCI, 0, "", STI, InstrStream); InstrStream.flush(); @@ -28,33 +31,13 @@ StringRef InstructionView::printInstructionString(const llvm::MCInst &MCI) const } json::Value InstructionView::toJSON() const { - json::Object JO; json::Array SourceInfo; for (const auto &MCI : getSource()) { StringRef Instruction = printInstructionString(MCI); SourceInfo.push_back(Instruction.str()); } - JO.try_emplace("Instructions", std::move(SourceInfo)); - - json::Array Resources; - const MCSchedModel &SM = STI.getSchedModel(); - for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) { - const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); - unsigned NumUnits = ProcResource.NumUnits; - // Skip groups and invalid resources with zero units. - if (ProcResource.SubUnitsIdxBegin || !NumUnits) - continue; - for (unsigned J = 0; J < NumUnits; ++J) { - std::stringstream ResNameStream; - ResNameStream << ProcResource.Name; - if (NumUnits > 1) - ResNameStream << "." << J; - Resources.push_back(ResNameStream.str()); - } - } - JO.try_emplace("Resources", json::Object({{"CPUName", MCPU}, {"Resources", std::move(Resources)}})); - - return JO; + return SourceInfo; } + } // namespace mca } // namespace llvm diff --git a/llvm/tools/llvm-mca/Views/InstructionView.h b/llvm/tools/llvm-mca/Views/InstructionView.h index 2a260b97d8fb..1843b0513dfc 100644 --- a/llvm/tools/llvm-mca/Views/InstructionView.h +++ b/llvm/tools/llvm-mca/Views/InstructionView.h @@ -1,4 +1,4 @@ -//===----------------------- InstrucionView.h -----------------------------*- C++ -*-===// +//===----------------------- InstructionView.h ------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -16,9 +16,8 @@ #define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONVIEW_H #include "Views/View.h" -#include "llvm/MC/MCInstPrinter.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" namespace llvm { namespace mca { @@ -28,7 +27,6 @@ class InstructionView : public View { const llvm::MCSubtargetInfo &STI; llvm::MCInstPrinter &MCIP; llvm::ArrayRef<llvm::MCInst> Source; - StringRef MCPU; mutable std::string InstructionString; mutable raw_string_ostream InstrStream; @@ -36,17 +34,13 @@ class InstructionView : public View { public: void printView(llvm::raw_ostream &) const override {} InstructionView(const llvm::MCSubtargetInfo &STI, - llvm::MCInstPrinter &Printer, - llvm::ArrayRef<llvm::MCInst> S, - StringRef MCPU = StringRef()) - : STI(STI), MCIP(Printer), Source(S), MCPU(MCPU), - InstrStream(InstructionString) {} + llvm::MCInstPrinter &Printer, llvm::ArrayRef<llvm::MCInst> S) + : STI(STI), MCIP(Printer), Source(S), InstrStream(InstructionString) {} + + virtual ~InstructionView(); - virtual ~InstructionView() = default; + StringRef getNameAsString() const override { return "Instructions"; } - StringRef getNameAsString() const override { - return "Instructions and CPU resources"; - } // Return a reference to a string representing a given machine instruction. // The result should be used or copied before the next call to // printInstructionString() as it will overwrite the previous result. @@ -55,12 +49,10 @@ public: llvm::MCInstPrinter &getInstPrinter() const { return MCIP; } llvm::ArrayRef<llvm::MCInst> getSource() const { return Source; } + json::Value toJSON() const override; - virtual void printViewJSON(llvm::raw_ostream &OS) override { - json::Value JV = toJSON(); - OS << formatv("{0:2}", JV) << "\n"; - } }; + } // namespace mca } // namespace llvm diff --git a/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp b/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp index 58736ee0d18c..4ef8053bff41 100644 --- a/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp +++ b/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp @@ -60,18 +60,21 @@ void RegisterFileStatistics::updateMoveElimInfo(const Instruction &Inst) { if (!Inst.isOptimizableMove()) return; - assert(Inst.getDefs().size() == 1 && "Expected a single definition!"); - assert(Inst.getUses().size() == 1 && "Expected a single register use!"); - const WriteState &WS = Inst.getDefs()[0]; - const ReadState &RS = Inst.getUses()[0]; - - MoveEliminationInfo &Info = - MoveElimInfo[Inst.getDefs()[0].getRegisterFileID()]; - Info.TotalMoveEliminationCandidates++; - if (WS.isEliminated()) - Info.CurrentMovesEliminated++; - if (WS.isWriteZero() && RS.isReadZero()) - Info.TotalMovesThatPropagateZero++; + if (Inst.getDefs().size() != Inst.getUses().size()) + return; + + for (size_t I = 0, E = Inst.getDefs().size(); I < E; ++I) { + const WriteState &WS = Inst.getDefs()[I]; + const ReadState &RS = Inst.getUses()[E - (I + 1)]; + + MoveEliminationInfo &Info = + MoveElimInfo[Inst.getDefs()[0].getRegisterFileID()]; + Info.TotalMoveEliminationCandidates++; + if (WS.isEliminated()) + Info.CurrentMovesEliminated++; + if (WS.isWriteZero() && RS.isReadZero()) + Info.TotalMovesThatPropagateZero++; + } } void RegisterFileStatistics::onEvent(const HWInstructionEvent &Event) { diff --git a/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h b/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h index cf384dbfe337..ec5c5f431e12 100644 --- a/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h +++ b/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h @@ -76,6 +76,7 @@ public: StringRef getNameAsString() const override { return "RegisterFileStatistics"; } + bool isSerializable() const override { return false; } }; } // namespace mca } // namespace llvm diff --git a/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp b/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp index 61c115b27be1..1c40428fb018 100644 --- a/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp +++ b/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp @@ -71,7 +71,8 @@ void RetireControlUnitStatistics::printView(raw_ostream &OS) const { } unsigned AvgUsage = (double)SumOfUsedEntries / NumCycles; - double MaxUsagePercentage = ((double)MaxUsedEntries / TotalROBEntries) * 100.0; + double MaxUsagePercentage = + ((double)MaxUsedEntries / TotalROBEntries) * 100.0; double NormalizedMaxPercentage = floor((MaxUsagePercentage * 10) + 0.5) / 10; double AvgUsagePercentage = ((double)AvgUsage / TotalROBEntries) * 100.0; double NormalizedAvgPercentage = floor((AvgUsagePercentage * 10) + 0.5) / 10; diff --git a/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h b/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h index 662a223662e6..86b46e93aa7c 100644 --- a/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h +++ b/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h @@ -55,6 +55,7 @@ public: StringRef getNameAsString() const override { return "RetireControlUnitStatistics"; } + bool isSerializable() const override { return false; } }; } // namespace mca diff --git a/llvm/tools/llvm-mca/Views/SchedulerStatistics.h b/llvm/tools/llvm-mca/Views/SchedulerStatistics.h index 734046c3112f..66f4b0011866 100644 --- a/llvm/tools/llvm-mca/Views/SchedulerStatistics.h +++ b/llvm/tools/llvm-mca/Views/SchedulerStatistics.h @@ -89,6 +89,7 @@ public: void printView(llvm::raw_ostream &OS) const override; StringRef getNameAsString() const override { return "SchedulerStatistics"; } + bool isSerializable() const override { return false; } }; } // namespace mca } // namespace llvm diff --git a/llvm/tools/llvm-mca/Views/SummaryView.cpp b/llvm/tools/llvm-mca/Views/SummaryView.cpp index c0fe3b5193a7..bf258b4c26b1 100644 --- a/llvm/tools/llvm-mca/Views/SummaryView.cpp +++ b/llvm/tools/llvm-mca/Views/SummaryView.cpp @@ -1,4 +1,4 @@ -//===--------------------- SummaryView.cpp -------------------*- C++ -*-===// +//===--------------------- SummaryView.cpp ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -24,9 +24,8 @@ namespace mca { SummaryView::SummaryView(const MCSchedModel &Model, ArrayRef<MCInst> S, unsigned Width) - : SM(Model), Source(S), DispatchWidth(Width?Width: Model.IssueWidth), - LastInstructionIdx(0), - TotalCycles(0), NumMicroOps(0), + : SM(Model), Source(S), DispatchWidth(Width ? Width : Model.IssueWidth), + LastInstructionIdx(0), TotalCycles(0), NumMicroOps(0), ProcResourceUsage(Model.getNumProcResourceKinds(), 0), ProcResourceMasks(Model.getNumProcResourceKinds()), ResIdx2ProcResID(Model.getNumProcResourceKinds(), 0) { diff --git a/llvm/tools/llvm-mca/Views/SummaryView.h b/llvm/tools/llvm-mca/Views/SummaryView.h index 2622e869ef23..e2c7cfd19e94 100644 --- a/llvm/tools/llvm-mca/Views/SummaryView.h +++ b/llvm/tools/llvm-mca/Views/SummaryView.h @@ -1,4 +1,4 @@ -//===--------------------- SummaryView.h ---------------------*- C++ -*-===// +//===--------------------- SummaryView.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/tools/llvm-mca/Views/TimelineView.cpp b/llvm/tools/llvm-mca/Views/TimelineView.cpp index c8b481bc7ce6..9a949761bb75 100644 --- a/llvm/tools/llvm-mca/Views/TimelineView.cpp +++ b/llvm/tools/llvm-mca/Views/TimelineView.cpp @@ -21,8 +21,8 @@ TimelineView::TimelineView(const MCSubtargetInfo &sti, MCInstPrinter &Printer, llvm::ArrayRef<llvm::MCInst> S, unsigned Iterations, unsigned Cycles) : InstructionView(sti, Printer, S), CurrentCycle(0), - MaxCycle(Cycles == 0 ? 80 : Cycles), LastCycle(0), WaitTime(S.size()), - UsedBuffer(S.size()) { + MaxCycle(Cycles == 0 ? std::numeric_limits<unsigned>::max() : Cycles), + LastCycle(0), WaitTime(S.size()), UsedBuffer(S.size()) { unsigned NumInstructions = getSource().size(); assert(Iterations && "Invalid number of iterations specified!"); NumInstructions *= Iterations; @@ -77,8 +77,10 @@ void TimelineView::onEvent(const HWInstructionEvent &Event) { "Instruction cannot be ready if it hasn't been dispatched yet!"); WTEntry.CyclesSpentInSQWhileReady += TVEntry.CycleIssued - TVEntry.CycleReady; - WTEntry.CyclesSpentAfterWBAndBeforeRetire += - (CurrentCycle - 1) - TVEntry.CycleExecuted; + if (CurrentCycle > TVEntry.CycleExecuted) { + WTEntry.CyclesSpentAfterWBAndBeforeRetire += + (CurrentCycle - 1) - TVEntry.CycleExecuted; + } break; } case HWInstructionEvent::Ready: @@ -243,7 +245,8 @@ void TimelineView::printTimelineViewEntry(formatted_raw_ostream &OS, for (unsigned I = Entry.CycleExecuted + 1, E = Entry.CycleRetired; I < E; ++I) OS << TimelineView::DisplayChar::RetireLag; - OS << TimelineView::DisplayChar::Retired; + if (Entry.CycleExecuted < Entry.CycleRetired) + OS << TimelineView::DisplayChar::Retired; // Skip other columns. for (unsigned I = Entry.CycleRetired + 1, E = LastCycle; I <= E; ++I) @@ -285,7 +288,14 @@ void TimelineView::printTimeline(raw_ostream &OS) const { for (unsigned Iteration = 0; Iteration < Iterations; ++Iteration) { for (const MCInst &Inst : Source) { const TimelineViewEntry &Entry = Timeline[IID]; - if (Entry.CycleRetired == 0) + // When an instruction is retired after timeline-max-cycles, + // its CycleRetired is left at 0. However, it's possible for + // a 0 latency instruction to be retired during cycle 0 and we + // don't want to early exit in that case. The CycleExecuted + // attribute is set correctly whether or not it is greater + // than timeline-max-cycles so we can use that to ensure + // we don't early exit because of a 0 latency instruction. + if (Entry.CycleRetired == 0 && Entry.CycleExecuted != 0) return; unsigned SourceIndex = IID % Source.size(); diff --git a/llvm/tools/llvm-mca/Views/TimelineView.h b/llvm/tools/llvm-mca/Views/TimelineView.h index 81f2b0335081..81be8244b779 100644 --- a/llvm/tools/llvm-mca/Views/TimelineView.h +++ b/llvm/tools/llvm-mca/Views/TimelineView.h @@ -125,7 +125,7 @@ class TimelineView : public InstructionView { unsigned LastCycle; struct TimelineViewEntry { - int CycleDispatched; // A negative value is an "invalid cycle". + int CycleDispatched; // A negative value is an "invalid cycle". unsigned CycleReady; unsigned CycleIssued; unsigned CycleExecuted; diff --git a/llvm/tools/llvm-mca/Views/View.h b/llvm/tools/llvm-mca/Views/View.h index 85464bfda662..c604733d4ec9 100644 --- a/llvm/tools/llvm-mca/Views/View.h +++ b/llvm/tools/llvm-mca/Views/View.h @@ -17,32 +17,22 @@ #include "llvm/MC/MCInstPrinter.h" #include "llvm/MCA/HWEventListener.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" namespace llvm { namespace mca { class View : public HWEventListener { public: - enum OutputKind { OK_READABLE, OK_JSON }; - - void printView(OutputKind OutputKind, llvm::raw_ostream &OS) { - if (OutputKind == OK_JSON) - printViewJSON(OS); - else - printView(OS); - } + virtual ~View() = default; virtual void printView(llvm::raw_ostream &OS) const = 0; - virtual void printViewJSON(llvm::raw_ostream &OS) { - json::Object JO; - JO.try_emplace(getNameAsString().str(), toJSON()); - OS << formatv("{0:2}", json::Value(std::move(JO))) << "\n"; - } - virtual ~View() = default; virtual StringRef getNameAsString() const = 0; + virtual json::Value toJSON() const { return "not implemented"; } + virtual bool isSerializable() const { return true; } + void anchor() override; }; } // namespace mca diff --git a/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.cpp b/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.cpp new file mode 100644 index 000000000000..a655f3faf1bf --- /dev/null +++ b/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.cpp @@ -0,0 +1,33 @@ +//===------------------ AMDGPUCustomBehaviour.cpp ---------------*-C++ -* -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file implements methods from the AMDGPUCustomBehaviour class. +/// +//===----------------------------------------------------------------------===// + +#include "AMDGPUCustomBehaviour.h" +#include "MCTargetDesc/AMDGPUMCTargetDesc.h" +#include "SIInstrInfo.h" +#include "llvm/Support/WithColor.h" + +namespace llvm { +namespace mca { + +AMDGPUCustomBehaviour::AMDGPUCustomBehaviour(const MCSubtargetInfo &STI, + const SourceMgr &SrcMgr, + const MCInstrInfo &MCII) + : CustomBehaviour(STI, SrcMgr, MCII) {} + +unsigned AMDGPUCustomBehaviour::checkCustomHazard(ArrayRef<InstRef> IssuedInst, + const InstRef &IR) { + return 0; +} + +} // namespace mca +} // namespace llvm diff --git a/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.h b/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.h new file mode 100644 index 000000000000..0dd21c7b4c44 --- /dev/null +++ b/llvm/tools/llvm-mca/lib/AMDGPU/AMDGPUCustomBehaviour.h @@ -0,0 +1,57 @@ +//===------------------- AMDGPUCustomBehaviour.h ----------------*-C++ -* -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines the AMDGPUCustomBehaviour class which inherits from +/// CustomBehaviour. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_MCA_LIB_AMDGPU_AMDGPUCUSTOMBEHAVIOUR_H +#define LLVM_TOOLS_LLVM_MCA_LIB_AMDGPU_AMDGPUCUSTOMBEHAVIOUR_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/MCA/CustomBehaviour.h" +#include "llvm/Support/TargetParser.h" + +namespace llvm { +namespace mca { + +class AMDGPUInstrPostProcess : public InstrPostProcess { +public: + AMDGPUInstrPostProcess(const MCSubtargetInfo &STI, const MCInstrInfo &MCII) + : InstrPostProcess(STI, MCII) {} + + ~AMDGPUInstrPostProcess() {} + + void postProcessInstruction(std::unique_ptr<Instruction> &Inst, + const MCInst &MCI) override {} +}; + +class AMDGPUCustomBehaviour : public CustomBehaviour { +public: + AMDGPUCustomBehaviour(const MCSubtargetInfo &STI, const SourceMgr &SrcMgr, + const MCInstrInfo &MCII); + + ~AMDGPUCustomBehaviour() {} + + /// This method is used to determine if an instruction + /// should be allowed to be dispatched. The return value is + /// how many cycles until the instruction can be dispatched. + /// This method is called after MCA has already checked for + /// register and hardware dependencies so this method should only + /// implement custom behaviour and dependencies that are not picked up + /// by MCA naturally. + unsigned checkCustomHazard(ArrayRef<InstRef> IssuedInst, + const InstRef &IR) override; +}; + +} // namespace mca +} // namespace llvm + +#endif /* LLVM_TOOLS_LLVM_MCA_LIB_AMDGPU_AMDGPUCUSTOMBEHAVIOUR_H */ diff --git a/llvm/tools/llvm-mca/llvm-mca.cpp b/llvm/tools/llvm-mca/llvm-mca.cpp index 13a2c6363579..a473cd8f1719 100644 --- a/llvm/tools/llvm-mca/llvm-mca.cpp +++ b/llvm/tools/llvm-mca/llvm-mca.cpp @@ -32,6 +32,9 @@ #include "Views/SchedulerStatistics.h" #include "Views/SummaryView.h" #include "Views/TimelineView.h" +#ifdef HAS_AMDGPU +#include "lib/AMDGPU/AMDGPUCustomBehaviour.h" +#endif #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" @@ -42,6 +45,7 @@ #include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/MCA/CodeEmitter.h" #include "llvm/MCA/Context.h" +#include "llvm/MCA/CustomBehaviour.h" #include "llvm/MCA/InstrBuilder.h" #include "llvm/MCA/Pipeline.h" #include "llvm/MCA/Stages/EntryStage.h" @@ -91,15 +95,13 @@ static cl::opt<std::string> cl::desc("Target a specific cpu type (-mcpu=help for details)"), cl::value_desc("cpu-name"), cl::cat(ToolOptions), cl::init("native")); -static cl::opt<std::string> - MATTR("mattr", - cl::desc("Additional target features."), - cl::cat(ToolOptions)); +static cl::opt<std::string> MATTR("mattr", + cl::desc("Additional target features."), + cl::cat(ToolOptions)); -static cl::opt<bool> - PrintJson("json", - cl::desc("Print the output in json format"), - cl::cat(ToolOptions), cl::init(false)); +static cl::opt<bool> PrintJson("json", + cl::desc("Print the output in json format"), + cl::cat(ToolOptions), cl::init(false)); static cl::opt<int> OutputAsmVariant("output-asm-variant", @@ -172,11 +174,11 @@ static cl::opt<unsigned> TimelineMaxIterations( cl::desc("Maximum number of iterations to print in timeline view"), cl::cat(ViewOptions), cl::init(0)); -static cl::opt<unsigned> TimelineMaxCycles( - "timeline-max-cycles", - cl::desc( - "Maximum number of cycles in the timeline view. Defaults to 80 cycles"), - cl::cat(ViewOptions), cl::init(80)); +static cl::opt<unsigned> + TimelineMaxCycles("timeline-max-cycles", + cl::desc("Maximum number of cycles in the timeline view, " + "or 0 for unlimited. Defaults to 80 cycles"), + cl::cat(ViewOptions), cl::init(80)); static cl::opt<bool> AssumeNoAlias("noalias", @@ -220,6 +222,12 @@ static cl::opt<bool> ShowEncoding( cl::desc("Print encoding information in the instruction info view"), cl::cat(ViewOptions), cl::init(false)); +static cl::opt<bool> DisableCustomBehaviour( + "disable-cb", + cl::desc( + "Disable custom behaviour (use the default class which does nothing)."), + cl::cat(ViewOptions), cl::init(false)); + namespace { const Target *getTarget(const char *ProgName) { @@ -236,6 +244,9 @@ const Target *getTarget(const char *ProgName) { return nullptr; } + // Update TripleName with the updated triple from the target lookup. + TripleName = TheTriple.str(); + // Return the found target. return TheTarget; } @@ -244,8 +255,8 @@ ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() { if (OutputFilename == "") OutputFilename = "-"; std::error_code EC; - auto Out = - std::make_unique<ToolOutputFile>(OutputFilename, EC, sys::fs::OF_Text); + auto Out = std::make_unique<ToolOutputFile>(OutputFilename, EC, + sys::fs::OF_TextWithCRLF); if (!EC) return std::move(Out); return EC; @@ -257,14 +268,15 @@ static void processOptionImpl(cl::opt<bool> &O, const cl::opt<bool> &Default) { O = Default.getValue(); } -static void processViewOptions() { +static void processViewOptions(bool IsOutOfOrder) { if (!EnableAllViews.getNumOccurrences() && !EnableAllStats.getNumOccurrences()) return; if (EnableAllViews.getNumOccurrences()) { processOptionImpl(PrintSummaryView, EnableAllViews); - processOptionImpl(EnableBottleneckAnalysis, EnableAllViews); + if (IsOutOfOrder) + processOptionImpl(EnableBottleneckAnalysis, EnableAllViews); processOptionImpl(PrintResourcePressureView, EnableAllViews); processOptionImpl(PrintTimelineView, EnableAllViews); processOptionImpl(PrintInstructionInfoView, EnableAllViews); @@ -277,7 +289,41 @@ static void processViewOptions() { processOptionImpl(PrintRegisterFileStats, Default); processOptionImpl(PrintDispatchStats, Default); processOptionImpl(PrintSchedulerStats, Default); - processOptionImpl(PrintRetireStats, Default); + if (IsOutOfOrder) + processOptionImpl(PrintRetireStats, Default); +} + +std::unique_ptr<mca::InstrPostProcess> +createInstrPostProcess(const Triple &TheTriple, const MCSubtargetInfo &STI, + const MCInstrInfo &MCII) { + // Might be a good idea to have a separate flag so that InstrPostProcess + // can be used with or without CustomBehaviour + if (DisableCustomBehaviour) + return std::make_unique<mca::InstrPostProcess>(STI, MCII); +#ifdef HAS_AMDGPU + if (TheTriple.isAMDGPU()) + return std::make_unique<mca::AMDGPUInstrPostProcess>(STI, MCII); +#endif + return std::make_unique<mca::InstrPostProcess>(STI, MCII); +} + +std::unique_ptr<mca::CustomBehaviour> +createCustomBehaviour(const Triple &TheTriple, const MCSubtargetInfo &STI, + const mca::SourceMgr &SrcMgr, const MCInstrInfo &MCII) { + // Build the appropriate CustomBehaviour object for the current target. + // The CustomBehaviour class should never depend on the source code, + // but it can depend on the list of mca::Instruction and any classes + // that can be built using just the target info. If you need extra + // information from the source code or the list of MCInst, consider + // adding that information to the mca::Instruction class and setting + // it during InstrBuilder::createInstruction(). + if (DisableCustomBehaviour) + return std::make_unique<mca::CustomBehaviour>(STI, SrcMgr, MCII); +#ifdef HAS_AMDGPU + if (TheTriple.isAMDGPU()) + return std::make_unique<mca::AMDGPUCustomBehaviour>(STI, SrcMgr, MCII); +#endif + return std::make_unique<mca::CustomBehaviour>(STI, SrcMgr, MCII); } // Returns true on success. @@ -327,9 +373,6 @@ int main(int argc, char **argv) { return 1; } - // Apply overrides to llvm-mca specific options. - processViewOptions(); - if (MCPU == "native") MCPU = std::string(llvm::sys::getHostCPUName()); @@ -339,10 +382,10 @@ int main(int argc, char **argv) { if (!STI->isCPUStringValid(MCPU)) return 1; - if (!PrintInstructionTables && !STI->getSchedModel().isOutOfOrder()) { - WithColor::error() << "please specify an out-of-order cpu. '" << MCPU - << "' is an in-order cpu.\n"; - return 1; + bool IsOutOfOrder = STI->getSchedModel().isOutOfOrder(); + if (!PrintInstructionTables && !IsOutOfOrder) { + WithColor::warning() << "support for in-order CPU '" << MCPU + << "' is experimental.\n"; } if (!STI->getSchedModel().hasInstrSchedModel()) { @@ -358,6 +401,9 @@ int main(int argc, char **argv) { return 1; } + // Apply overrides to llvm-mca specific options. + processViewOptions(IsOutOfOrder); + std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); assert(MRI && "Unable to create target register info!"); @@ -366,15 +412,15 @@ int main(int argc, char **argv) { TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); assert(MAI && "Unable to create target asm info!"); - MCObjectFileInfo MOFI; SourceMgr SrcMgr; // Tell SrcMgr about this buffer, which is what the parser will pick up. SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); - MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); - - MOFI.InitMCObjectFileInfo(TheTriple, /* PIC= */ false, Ctx); + MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); + std::unique_ptr<MCObjectFileInfo> MOFI( + TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); + Ctx.setObjectFileInfo(MOFI.get()); std::unique_ptr<buffer_ostream> BOS; @@ -384,9 +430,28 @@ int main(int argc, char **argv) { std::unique_ptr<MCInstrAnalysis> MCIA( TheTarget->createMCInstrAnalysis(MCII.get())); + // Need to initialize an MCInstPrinter as it is + // required for initializing the MCTargetStreamer + // which needs to happen within the CRG.parseCodeRegions() call below. + // Without an MCTargetStreamer, certain assembly directives can trigger a + // segfault. (For example, the .cv_fpo_proc directive on x86 will segfault if + // we don't initialize the MCTargetStreamer.) + unsigned IPtempOutputAsmVariant = + OutputAsmVariant == -1 ? 0 : OutputAsmVariant; + std::unique_ptr<MCInstPrinter> IPtemp(TheTarget->createMCInstPrinter( + Triple(TripleName), IPtempOutputAsmVariant, *MAI, *MCII, *MRI)); + if (!IPtemp) { + WithColor::error() + << "unable to create instruction printer for target triple '" + << TheTriple.normalize() << "' with assembly variant " + << IPtempOutputAsmVariant << ".\n"; + return 1; + } + // Parse the input and create CodeRegions that llvm-mca can analyze. mca::AsmCodeRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, *MCII); - Expected<const mca::CodeRegions &> RegionsOrErr = CRG.parseCodeRegions(); + Expected<const mca::CodeRegions &> RegionsOrErr = + CRG.parseCodeRegions(std::move(IPtemp)); if (!RegionsOrErr) { if (auto Err = handleErrors(RegionsOrErr.takeError(), [](const StringError &E) { @@ -456,24 +521,19 @@ int main(int argc, char **argv) { *STI, *MRI, mc::InitMCTargetOptionsFromFlags())); assert(MAB && "Unable to create asm backend!"); + json::Object JSONOutput; for (const std::unique_ptr<mca::CodeRegion> &Region : Regions) { // Skip empty code regions. if (Region->empty()) continue; - // Don't print the header of this region if it is the default region, and - // it doesn't have an end location. - if (Region->startLoc().isValid() || Region->endLoc().isValid()) { - TOF->os() << "\n[" << RegionIdx++ << "] Code Region"; - StringRef Desc = Region->getDescription(); - if (!Desc.empty()) - TOF->os() << " - " << Desc; - TOF->os() << "\n\n"; - } + IB.clear(); // Lower the MCInst sequence into an mca::Instruction sequence. ArrayRef<MCInst> Insts = Region->getInstructions(); mca::CodeEmitter CE(*STI, *MAB, *MCE, Insts); + std::unique_ptr<mca::InstrPostProcess> IPP = + createInstrPostProcess(TheTriple, *STI, *MCII); std::vector<std::unique_ptr<mca::Instruction>> LoweredSequence; for (const MCInst &MCI : Insts) { Expected<std::unique_ptr<mca::Instruction>> Inst = @@ -496,6 +556,8 @@ int main(int argc, char **argv) { return 1; } + IPP->postProcessInstruction(Inst.get(), MCI); + LoweredSequence.emplace_back(std::move(Inst.get())); } @@ -506,7 +568,12 @@ int main(int argc, char **argv) { auto P = std::make_unique<mca::Pipeline>(); P->appendStage(std::make_unique<mca::EntryStage>(S)); P->appendStage(std::make_unique<mca::InstructionTables>(SM)); - mca::PipelinePrinter Printer(*P, mca::View::OK_READABLE); + + mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO); + if (PrintJson) { + Printer.addView( + std::make_unique<mca::InstructionView>(*STI, *IP, Insts)); + } // Create the views for this pipeline, execute, and emit a report. if (PrintInstructionInfoView) { @@ -519,26 +586,47 @@ int main(int argc, char **argv) { if (!runPipeline(*P)) return 1; - Printer.printReport(TOF->os()); + if (PrintJson) { + Printer.printReport(JSONOutput); + } else { + Printer.printReport(TOF->os()); + } + + ++RegionIdx; continue; } + // Create the CustomBehaviour object for enforcing Target Specific + // behaviours and dependencies that aren't expressed well enough + // in the tablegen. CB cannot depend on the list of MCInst or + // the source code (but it can depend on the list of + // mca::Instruction or any objects that can be reconstructed + // from the target information). + std::unique_ptr<mca::CustomBehaviour> CB = + createCustomBehaviour(TheTriple, *STI, S, *MCII); + // Create a basic pipeline simulating an out-of-order backend. - auto P = MCA.createDefaultPipeline(PO, S); - mca::PipelinePrinter Printer(*P, PrintJson ? mca::View::OK_JSON - : mca::View::OK_READABLE); + auto P = MCA.createDefaultPipeline(PO, S, *CB); + + mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO); // When we output JSON, we add a view that contains the instructions // and CPU resource information. - if (PrintJson) - Printer.addView( - std::make_unique<mca::InstructionView>(*STI, *IP, Insts, MCPU)); + if (PrintJson) { + auto IV = std::make_unique<mca::InstructionView>(*STI, *IP, Insts); + Printer.addView(std::move(IV)); + } if (PrintSummaryView) Printer.addView( std::make_unique<mca::SummaryView>(SM, Insts, DispatchWidth)); if (EnableBottleneckAnalysis) { + if (!IsOutOfOrder) { + WithColor::warning() + << "bottleneck analysis is not supported for in-order CPU '" << MCPU + << "'.\n"; + } Printer.addView(std::make_unique<mca::BottleneckAnalysis>( *STI, *IP, Insts, S.getNumIterations())); } @@ -574,12 +662,18 @@ int main(int argc, char **argv) { if (!runPipeline(*P)) return 1; - Printer.printReport(TOF->os()); + if (PrintJson) { + Printer.printReport(JSONOutput); + } else { + Printer.printReport(TOF->os()); + } - // Clear the InstrBuilder internal state in preparation for another round. - IB.clear(); + ++RegionIdx; } + if (PrintJson) + TOF->os() << formatv("{0:2}", json::Value(std::move(JSONOutput))) << "\n"; + TOF->keep(); return 0; } diff --git a/llvm/tools/llvm-modextract/llvm-modextract.cpp b/llvm/tools/llvm-modextract/llvm-modextract.cpp index 7c4099625842..9a44cbf68d0d 100644 --- a/llvm/tools/llvm-modextract/llvm-modextract.cpp +++ b/llvm/tools/llvm-modextract/llvm-modextract.cpp @@ -21,21 +21,29 @@ using namespace llvm; +static cl::OptionCategory ModextractCategory("Modextract Options"); + static cl::opt<bool> - BinaryExtract("b", cl::desc("Whether to perform binary extraction")); + BinaryExtract("b", cl::desc("Whether to perform binary extraction"), + cl::cat(ModextractCategory)); static cl::opt<std::string> OutputFilename("o", cl::Required, cl::desc("Output filename"), - cl::value_desc("filename")); + cl::value_desc("filename"), + cl::cat(ModextractCategory)); -static cl::opt<std::string> - InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); +static cl::opt<std::string> InputFilename(cl::Positional, + cl::desc("<input bitcode>"), + cl::init("-"), + cl::cat(ModextractCategory)); static cl::opt<unsigned> ModuleIndex("n", cl::Required, cl::desc("Index of module to extract"), - cl::value_desc("index")); + cl::value_desc("index"), + cl::cat(ModextractCategory)); int main(int argc, char **argv) { + cl::HideUnrelatedOptions({&ModextractCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "Module extractor"); ExitOnError ExitOnErr("llvm-modextract: error: "); diff --git a/llvm/tools/llvm-nm/Opts.td b/llvm/tools/llvm-nm/Opts.td new file mode 100644 index 000000000000..3a790890909a --- /dev/null +++ b/llvm/tools/llvm-nm/Opts.td @@ -0,0 +1,76 @@ +include "llvm/Option/OptParser.td" + +class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>; +class FF<string name, string help> : Flag<["--"], name>, HelpText<help>; + +multiclass BB<string name, string help1, string help2> { + def NAME: Flag<["--"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; +} + +multiclass Eq<string name, string help> { + def NAME #_EQ : Joined<["--"], name #"=">, HelpText<help>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; +} + +def debug_syms : FF<"debug-syms", "Show all symbols, even debugger only">; +def defined_only : FF<"defined-only", "Show only defined symbols">; +defm demangle : BB<"demangle", "Demangle C++ symbol names", "Don't demangle symbol names">; +def dynamic : FF<"dynamic", "Display dynamic symbols instead of normal symbols">; +def extern_only : FF<"extern-only", "Show only external symbols">; +defm format : Eq<"format", "Specify output format: bsd (default), posix, sysv, darwin, just-symbols">, MetaVarName<"<format>">; +def help : FF<"help", "Display this help">; +def no_llvm_bc : FF<"no-llvm-bc", "Disable LLVM bitcode reader">; +def no_sort : FF<"no-sort", "Show symbols in order encountered">; +def no_weak : FF<"no-weak", "Show only non-weak symbols">; +def numeric_sort : FF<"numeric-sort", "Sort symbols by address">; +def print_armap : FF<"print-armap", "Print the archive map">; +def print_file_name : FF<"print-file-name", "Precede each symbol with the object file it came from">; +def print_size : FF<"print-size", "Show symbol size as well as address">; +def quiet : FF<"quiet", "Suppress 'no symbols' diagnostic">; +defm radix : Eq<"radix", "Radix (o/d/x) for printing symbol Values">, MetaVarName<"<radix>">; +def reverse_sort : FF<"reverse-sort", "Sort in reverse order">; +def size_sort : FF<"size-sort", "Sort symbols by size">; +def special_syms : FF<"special-syms", "Do not filter special symbols from the output">; +def undefined_only : FF<"undefined-only", "Show only undefined symbols">; +def version : FF<"version", "Display the version">; +def without_aliases : FF<"without-aliases", "Exclude aliases from output">, Flags<[HelpHidden]>; + +// Mach-O specific options. +def grp_mach_o : OptionGroup<"kind">, HelpText<"llvm-nm Mach-O Specific Options">; + +def add_dyldinfo : FF<"add-dyldinfo", "Add symbols from the dyldinfo not already in the symbol table">, Group<grp_mach_o>; +def add_inlinedinfo : FF<"add-inlinedinfo", "Add symbols from the inlined libraries, TBD only">, Group<grp_mach_o>; +def arch_EQ : Joined<["--"], "arch=">, HelpText<"architecture(s) from a Mach-O file to dump">, Group<grp_mach_o>; +def : Separate<["--", "-"], "arch">, Alias<arch_EQ>; +def dyldinfo_only : FF<"dyldinfo-only", "Show only symbols from the dyldinfo">, Group<grp_mach_o>; +def no_dyldinfo : FF<"no-dyldinfo", "Don't add any symbols from the dyldinfo">, Group<grp_mach_o>; +def s : F<"s", "Dump only symbols from this segment and section name">, Group<grp_mach_o>; +def x : F<"x", "Print symbol entry in hex">, Group<grp_mach_o>; + +def : FF<"just-symbol-name", "Alias for --format=just-symbols">, Alias<format_EQ>, AliasArgs<["just-symbols"]>, Flags<[HelpHidden]>; +def : FF<"portability", "Alias for --format=posix">, Alias<format_EQ>, AliasArgs<["posix"]>; + +def : F<"a", "Alias for --debug-syms">, Alias<debug_syms>; +def : F<"A", "Alias for --print-file-name">, Alias<print_file_name>; +def : F<"B", "Alias for --format=bsd">, Alias<format_EQ>, AliasArgs<["bsd"]>; +def : F<"C", "Alias for --demangle">, Alias<demangle>; +def : F<"D", "Alias for --dynamic">, Alias<dynamic>; +def : JoinedOrSeparate<["-"], "f">, HelpText<"Alias for --format">, Alias<format_EQ>, MetaVarName<"<format>">; +def : F<"h", "Alias for --help">, Alias<help>; +def : F<"g", "Alias for --extern-only">, Alias<extern_only>; +def : F<"j", "Alias for --format=just-symbols">, Alias<format_EQ>, AliasArgs<["just-symbols"]>; +def : F<"m", "Alias for --format=darwin">, Alias<format_EQ>, AliasArgs<["darwin"]>; +def : F<"M", "Deprecated alias for --print-armap">, Alias<print_armap>, Flags<[HelpHidden]>; +def : F<"n", "Alias for --numeric-sort">, Alias<numeric_sort>; +def : F<"o", "Alias for --print-file-name">, Alias<print_file_name>; +def : F<"p", "Alias for --no-sort">, Alias<no_sort>; +def : F<"P", "Alias for --format=posix">, Alias<format_EQ>, AliasArgs<["posix"]>; +def : F<"r", "Alias for --reverse-sort">, Alias<reverse_sort>; +def : F<"S", "Alias for --print-size">, Alias<print_size>; +def : JoinedOrSeparate<["-"], "t">, HelpText<"Alias for --radix">, Alias<radix_EQ>, MetaVarName<"<radix>">; +def : F<"u", "Alias for --undefined-only">, Alias<undefined_only>; +def : F<"U", "Deprecated alias for --defined-only">, Alias<defined_only>, Flags<[HelpHidden]>; +def : F<"v", "Alias for --numeric-sort">, Alias<numeric_sort>; +def : F<"V", "Alias for --version">, Alias<version>; +def : F<"W", "Deprecated alias for --no-weak">, Alias<no_weak>, Flags<[HelpHidden]>; diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp index c678108807c5..ffb427a3f2bd 100644 --- a/llvm/tools/llvm-nm/llvm-nm.cpp +++ b/llvm/tools/llvm-nm/llvm-nm.cpp @@ -31,6 +31,9 @@ #include "llvm/Object/TapiFile.h" #include "llvm/Object/TapiUniversal.h" #include "llvm/Object/Wasm.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" @@ -47,192 +50,86 @@ using namespace llvm; using namespace object; namespace { -enum OutputFormatTy { bsd, sysv, posix, darwin }; - -cl::OptionCategory NMCat("llvm-nm Options"); - -cl::opt<OutputFormatTy> OutputFormat( - "format", cl::desc("Specify output format"), - cl::values(clEnumVal(bsd, "BSD format"), clEnumVal(sysv, "System V format"), - clEnumVal(posix, "POSIX.2 format"), - clEnumVal(darwin, "Darwin -m format")), - cl::init(bsd), cl::cat(NMCat)); -cl::alias OutputFormat2("f", cl::desc("Alias for --format"), - cl::aliasopt(OutputFormat)); - -cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), - cl::ZeroOrMore); - -cl::opt<bool> UndefinedOnly("undefined-only", - cl::desc("Show only undefined symbols"), - cl::cat(NMCat)); -cl::alias UndefinedOnly2("u", cl::desc("Alias for --undefined-only"), - cl::aliasopt(UndefinedOnly), cl::Grouping); - -cl::opt<bool> DynamicSyms("dynamic", - cl::desc("Display the dynamic symbols instead " - "of normal symbols."), - cl::cat(NMCat)); -cl::alias DynamicSyms2("D", cl::desc("Alias for --dynamic"), - cl::aliasopt(DynamicSyms), cl::Grouping); - -cl::opt<bool> DefinedOnly("defined-only", cl::desc("Show only defined symbols"), - cl::cat(NMCat)); -cl::alias DefinedOnly2("U", cl::desc("Alias for --defined-only"), - cl::aliasopt(DefinedOnly), cl::Grouping); - -cl::opt<bool> ExternalOnly("extern-only", - cl::desc("Show only external symbols"), - cl::ZeroOrMore, cl::cat(NMCat)); -cl::alias ExternalOnly2("g", cl::desc("Alias for --extern-only"), - cl::aliasopt(ExternalOnly), cl::Grouping, - cl::ZeroOrMore); - -cl::opt<bool> NoWeakSymbols("no-weak", cl::desc("Show only non-weak symbols"), - cl::cat(NMCat)); -cl::alias NoWeakSymbols2("W", cl::desc("Alias for --no-weak"), - cl::aliasopt(NoWeakSymbols), cl::Grouping); - -cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd"), cl::Grouping, - cl::cat(NMCat)); -cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix"), - cl::Grouping, cl::cat(NMCat)); -cl::alias Portability("portability", cl::desc("Alias for --format=posix"), - cl::aliasopt(POSIXFormat), cl::NotHidden); -cl::opt<bool> DarwinFormat("m", cl::desc("Alias for --format=darwin"), - cl::Grouping, cl::cat(NMCat)); - -static cl::list<std::string> - ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), - cl::ZeroOrMore, cl::cat(NMCat)); -bool ArchAll = false; - -cl::opt<bool> PrintFileName( - "print-file-name", - cl::desc("Precede each symbol with the object file it came from"), - cl::cat(NMCat)); - -cl::alias PrintFileNameA("A", cl::desc("Alias for --print-file-name"), - cl::aliasopt(PrintFileName), cl::Grouping); -cl::alias PrintFileNameo("o", cl::desc("Alias for --print-file-name"), - cl::aliasopt(PrintFileName), cl::Grouping); - -cl::opt<bool> DebugSyms("debug-syms", - cl::desc("Show all symbols, even debugger only"), - cl::cat(NMCat)); -cl::alias DebugSymsa("a", cl::desc("Alias for --debug-syms"), - cl::aliasopt(DebugSyms), cl::Grouping); - -cl::opt<bool> NumericSort("numeric-sort", cl::desc("Sort symbols by address"), - cl::cat(NMCat)); -cl::alias NumericSortn("n", cl::desc("Alias for --numeric-sort"), - cl::aliasopt(NumericSort), cl::Grouping); -cl::alias NumericSortv("v", cl::desc("Alias for --numeric-sort"), - cl::aliasopt(NumericSort), cl::Grouping); - -cl::opt<bool> NoSort("no-sort", cl::desc("Show symbols in order encountered"), - cl::cat(NMCat)); -cl::alias NoSortp("p", cl::desc("Alias for --no-sort"), cl::aliasopt(NoSort), - cl::Grouping); - -cl::opt<bool> Demangle("demangle", cl::ZeroOrMore, - cl::desc("Demangle C++ symbol names"), cl::cat(NMCat)); -cl::alias DemangleC("C", cl::desc("Alias for --demangle"), - cl::aliasopt(Demangle), cl::Grouping); -cl::opt<bool> NoDemangle("no-demangle", cl::init(false), cl::ZeroOrMore, - cl::desc("Don't demangle symbol names"), - cl::cat(NMCat)); - -cl::opt<bool> ReverseSort("reverse-sort", cl::desc("Sort in reverse order"), - cl::cat(NMCat)); -cl::alias ReverseSortr("r", cl::desc("Alias for --reverse-sort"), - cl::aliasopt(ReverseSort), cl::Grouping); - -cl::opt<bool> PrintSize("print-size", - cl::desc("Show symbol size as well as address"), - cl::cat(NMCat)); -cl::alias PrintSizeS("S", cl::desc("Alias for --print-size"), - cl::aliasopt(PrintSize), cl::Grouping); -bool MachOPrintSizeWarning = false; - -cl::opt<bool> SizeSort("size-sort", cl::desc("Sort symbols by size"), - cl::cat(NMCat)); - -cl::opt<bool> WithoutAliases("without-aliases", cl::Hidden, - cl::desc("Exclude aliases from output"), - cl::cat(NMCat)); - -cl::opt<bool> ArchiveMap("print-armap", cl::desc("Print the archive map"), - cl::cat(NMCat)); -cl::alias ArchiveMaps("M", cl::desc("Alias for --print-armap"), - cl::aliasopt(ArchiveMap), cl::Grouping); +using namespace llvm::opt; // for HelpHidden in Opts.inc +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class NmOptTable : public opt::OptTable { +public: + NmOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } +}; + +enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols }; +} // namespace +static bool ArchiveMap; +static bool DebugSyms; +static bool DefinedOnly; +static bool Demangle; +static bool DynamicSyms; +static bool ExternalOnly; +static OutputFormatTy OutputFormat; +static bool NoLLVMBitcode; +static bool NoSort; +static bool NoWeakSymbols; +static bool NumericSort; +static bool PrintFileName; +static bool PrintSize; +static bool Quiet; +static bool ReverseSort; +static bool SpecialSyms; +static bool SizeSort; +static bool UndefinedOnly; +static bool WithoutAliases; + +namespace { enum Radix { d, o, x }; -cl::opt<Radix> - AddressRadix("radix", cl::desc("Radix (o/d/x) for printing symbol Values"), - cl::values(clEnumVal(d, "decimal"), clEnumVal(o, "octal"), - clEnumVal(x, "hexadecimal")), - cl::init(x), cl::cat(NMCat)); -cl::alias RadixAlias("t", cl::desc("Alias for --radix"), - cl::aliasopt(AddressRadix)); - -cl::opt<bool> JustSymbolName("just-symbol-name", - cl::desc("Print just the symbol's name"), - cl::cat(NMCat)); -cl::alias JustSymbolNames("j", cl::desc("Alias for --just-symbol-name"), - cl::aliasopt(JustSymbolName), cl::Grouping); - -cl::opt<bool> - SpecialSyms("special-syms", - cl::desc("Do not filter special symbols from the output"), - cl::cat(NMCat)); - -cl::list<std::string> SegSect("s", cl::multi_val(2), cl::ZeroOrMore, - cl::value_desc("segment section"), cl::Hidden, - cl::desc("Dump only symbols from this segment " - "and section name, Mach-O only"), - cl::cat(NMCat)); - -cl::opt<bool> FormatMachOasHex("x", - cl::desc("Print symbol entry in hex, " - "Mach-O only"), - cl::Grouping, cl::cat(NMCat)); -cl::opt<bool> AddDyldInfo("add-dyldinfo", - cl::desc("Add symbols from the dyldinfo not already " - "in the symbol table, Mach-O only"), - cl::cat(NMCat)); -cl::opt<bool> NoDyldInfo("no-dyldinfo", - cl::desc("Don't add any symbols from the dyldinfo, " - "Mach-O only"), - cl::cat(NMCat)); -cl::opt<bool> DyldInfoOnly("dyldinfo-only", - cl::desc("Show only symbols from the dyldinfo, " - "Mach-O only"), - cl::cat(NMCat)); - -cl::opt<bool> NoLLVMBitcode("no-llvm-bc", - cl::desc("Disable LLVM bitcode reader"), - cl::cat(NMCat)); - -cl::opt<bool> AddInlinedInfo("add-inlinedinfo", - cl::desc("Add symbols from the inlined libraries, " - "TBD(Mach-O) only"), - cl::cat(NMCat)); - -cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); - -bool PrintAddress = true; - -bool MultipleFiles = false; - -bool HadError = false; - -std::string ToolName; -} // anonymous namespace +} // namespace +static Radix AddressRadix; + +// Mach-O specific options. +static bool ArchAll = false; +static std::vector<StringRef> ArchFlags; +static bool AddDyldInfo; +static bool AddInlinedInfo; +static bool DyldInfoOnly; +static bool FormatMachOasHex; +static bool NoDyldInfo; +static std::vector<StringRef> SegSect; +static bool MachOPrintSizeWarning = false; + +// Miscellaneous states. +static bool PrintAddress = true; +static bool MultipleFiles = false; +static bool HadError = false; + +static StringRef ToolName; static void error(Twine Message, Twine Path = Twine()) { HadError = true; - WithColor::error(errs(), ToolName) << Path << ": " << Message << ".\n"; + WithColor::error(errs(), ToolName) << Path << ": " << Message << "\n"; } static bool error(std::error_code EC, Twine Path = Twine()) { @@ -262,13 +159,13 @@ static void error(llvm::Error E, StringRef FileName, const Archive::Child &C, errs() << "(" << NameOrErr.get() << ")"; if (!ArchitectureName.empty()) - errs() << " (for architecture " << ArchitectureName << ") "; + errs() << " (for architecture " << ArchitectureName << ")"; std::string Buf; raw_string_ostream OS(Buf); logAllUnhandledErrors(std::move(E), OS); OS.flush(); - errs() << " " << Buf << "\n"; + errs() << ": " << Buf << "\n"; } // This version of error() prints the file name and which architecture slice it @@ -281,13 +178,13 @@ static void error(llvm::Error E, StringRef FileName, WithColor::error(errs(), ToolName) << FileName; if (!ArchitectureName.empty()) - errs() << " (for architecture " << ArchitectureName << ") "; + errs() << " (for architecture " << ArchitectureName << ")"; std::string Buf; raw_string_ostream OS(Buf); logAllUnhandledErrors(std::move(E), OS); OS.flush(); - errs() << " " << Buf << "\n"; + errs() << ": " << Buf << "\n"; } namespace { @@ -737,16 +634,6 @@ static void writeFileName(raw_ostream &S, StringRef ArchiveName, } } -static bool isSpecialSym(SymbolicFile &Obj, StringRef Name) { - auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj); - if (!ELFObj) - return false; - uint16_t EMachine = ELFObj->getEMachine(); - if (EMachine != ELF::EM_ARM && EMachine != ELF::EM_AARCH64) - return false; - return !Name.empty() && Name[0] == '$'; -} - static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, StringRef ArchiveName, StringRef ArchitectureName) { @@ -769,10 +656,10 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, } if (!PrintFileName) { - if (OutputFormat == posix && MultipleFiles && printName) { + if ((OutputFormat == bsd || OutputFormat == posix || + OutputFormat == just_symbols) && + MultipleFiles && printName) { outs() << '\n' << CurrentFilename << ":\n"; - } else if (OutputFormat == bsd && MultipleFiles && printName) { - outs() << "\n" << CurrentFilename << ":\n"; } else if (OutputFormat == sysv) { outs() << "\n\nSymbols from " << CurrentFilename << ":\n\n"; if (isSymbolList64Bit(Obj)) @@ -835,13 +722,14 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, bool Undefined = SymFlags & SymbolRef::SF_Undefined; bool Global = SymFlags & SymbolRef::SF_Global; bool Weak = SymFlags & SymbolRef::SF_Weak; + bool FormatSpecific = SymFlags & SymbolRef::SF_FormatSpecific; if ((!Undefined && UndefinedOnly) || (Undefined && DefinedOnly) || (!Global && ExternalOnly) || (Weak && NoWeakSymbols) || - (!SpecialSyms && isSpecialSym(Obj, Name))) + (FormatSpecific && !(SpecialSyms || DebugSyms))) continue; if (PrintFileName) writeFileName(outs(), ArchiveName, ArchitectureName); - if ((JustSymbolName || + if ((OutputFormat == just_symbols || (UndefinedOnly && MachO && OutputFormat != darwin)) && OutputFormat != posix) { outs() << Name << "\n"; @@ -1142,13 +1030,16 @@ static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I, } } - if ((Symflags & object::SymbolRef::SF_Weak) && !isa<MachOObjectFile>(Obj)) { - char Ret = isObject(Obj, I) ? 'v' : 'w'; - return (!(Symflags & object::SymbolRef::SF_Undefined)) ? toupper(Ret) : Ret; + if (Symflags & object::SymbolRef::SF_Undefined) { + if (isa<MachOObjectFile>(Obj) || !(Symflags & object::SymbolRef::SF_Weak)) + return 'U'; + return isObject(Obj, I) ? 'v' : 'w'; } - - if (Symflags & object::SymbolRef::SF_Undefined) - return 'U'; + if (isa<ELFObjectFileBase>(&Obj)) + if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC) + return 'i'; + if (!isa<MachOObjectFile>(Obj) && (Symflags & object::SymbolRef::SF_Weak)) + return isObject(Obj, I) ? 'V' : 'W'; if (Symflags & object::SymbolRef::SF_Common) return 'C'; @@ -1169,8 +1060,6 @@ static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I, else if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj)) Ret = getSymbolNMTypeChar(*Tapi, I); else if (ELFObjectFileBase *ELF = dyn_cast<ELFObjectFileBase>(&Obj)) { - if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC) - return 'i'; Ret = getSymbolNMTypeChar(*ELF, I); if (ELFSymbolRef(*I).getBinding() == ELF::STB_GNU_UNIQUE) return Ret; @@ -1806,7 +1695,14 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, error(SymFlagsOrErr.takeError(), Obj.getFileName()); return; } - if (!DebugSyms && (*SymFlagsOrErr & SymbolRef::SF_FormatSpecific)) + + // Don't drop format specifc symbols for ARM and AArch64 ELF targets, they + // are used to repesent mapping symbols and needed to honor the + // --special-syms option. + auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj); + if ((!ELFObj || (ELFObj->getEMachine() != ELF::EM_ARM && + ELFObj->getEMachine() != ELF::EM_AARCH64)) && + !DebugSyms && (*SymFlagsOrErr & SymbolRef::SF_FormatSpecific)) continue; if (WithoutAliases && (*SymFlagsOrErr & SymbolRef::SF_Indirect)) continue; @@ -1860,7 +1756,7 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, CurrentFilename = Obj.getFileName(); - if (Symbols.empty() && SymbolList.empty()) { + if (Symbols.empty() && SymbolList.empty() && !Quiet) { writeFileName(errs(), ArchiveName, ArchitectureName); errs() << "no symbols\n"; } @@ -2228,8 +2124,80 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { int main(int argc, char **argv) { InitLLVM X(argc, argv); - cl::HideUnrelatedOptions(NMCat); - cl::ParseCommandLineOptions(argc, argv, "llvm symbol table dumper\n"); + BumpPtrAllocator A; + StringSaver Saver(A); + NmOptTable Tbl; + ToolName = argv[0]; + opt::InputArgList Args = + Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { + error(Msg); + exit(1); + }); + if (Args.hasArg(OPT_help)) { + Tbl.printHelp( + outs(), + (Twine(ToolName) + " [options] <input object files>").str().c_str(), + "LLVM symbol table dumper"); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + return 0; + } + if (Args.hasArg(OPT_version)) { + // This needs to contain the word "GNU", libtool looks for that string. + outs() << "llvm-nm, compatible with GNU nm" << '\n'; + cl::PrintVersionMessage(); + return 0; + } + + DebugSyms = Args.hasArg(OPT_debug_syms); + DefinedOnly = Args.hasArg(OPT_defined_only); + Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false); + DynamicSyms = Args.hasArg(OPT_dynamic); + ExternalOnly = Args.hasArg(OPT_extern_only); + StringRef V = Args.getLastArgValue(OPT_format_EQ, "bsd"); + if (V == "bsd") + OutputFormat = bsd; + else if (V == "posix") + OutputFormat = posix; + else if (V == "sysv") + OutputFormat = sysv; + else if (V == "darwin") + OutputFormat = darwin; + else if (V == "just-symbols") + OutputFormat = just_symbols; + else + error("--format value should be one of: bsd, posix, sysv, darwin, " + "just-symbols"); + NoLLVMBitcode = Args.hasArg(OPT_no_llvm_bc); + NoSort = Args.hasArg(OPT_no_sort); + NoWeakSymbols = Args.hasArg(OPT_no_weak); + NumericSort = Args.hasArg(OPT_numeric_sort); + ArchiveMap = Args.hasArg(OPT_print_armap); + PrintFileName = Args.hasArg(OPT_print_file_name); + PrintSize = Args.hasArg(OPT_print_size); + ReverseSort = Args.hasArg(OPT_reverse_sort); + Quiet = Args.hasArg(OPT_quiet); + V = Args.getLastArgValue(OPT_radix_EQ, "x"); + if (V == "o") + AddressRadix = Radix::o; + else if (V == "d") + AddressRadix = Radix::d; + else if (V == "x") + AddressRadix = Radix::x; + else + error("--radix value should be one of: 'o' (octal), 'd' (decimal), 'x' " + "(hexadecimal)"); + SizeSort = Args.hasArg(OPT_size_sort); + SpecialSyms = Args.hasArg(OPT_special_syms); + UndefinedOnly = Args.hasArg(OPT_undefined_only); + WithoutAliases = Args.hasArg(OPT_without_aliases); + + // Mach-O specific options. + FormatMachOasHex = Args.hasArg(OPT_x); + AddDyldInfo = Args.hasArg(OPT_add_dyldinfo); + AddInlinedInfo = Args.hasArg(OPT_add_inlinedinfo); + DyldInfoOnly = Args.hasArg(OPT_dyldinfo_only); + NoDyldInfo = Args.hasArg(OPT_no_dyldinfo); // llvm-nm only reads binary files. if (error(sys::ChangeStdinToBinary())) @@ -2240,14 +2208,6 @@ int main(int argc, char **argv) { llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmParsers(); - ToolName = argv[0]; - if (BSDFormat) - OutputFormat = bsd; - if (POSIXFormat) - OutputFormat = posix; - if (DarwinFormat) - OutputFormat = darwin; - // The relative order of these is important. If you pass --size-sort it should // only print out the size. However, if you pass -S --size-sort, it should // print out both the size and address. @@ -2255,29 +2215,44 @@ int main(int argc, char **argv) { PrintAddress = false; if (OutputFormat == sysv || SizeSort) PrintSize = true; - if (InputFilenames.empty()) - InputFilenames.push_back("a.out"); - if (InputFilenames.size() > 1) - MultipleFiles = true; - - // If both --demangle and --no-demangle are specified then pick the last one. - if (NoDemangle.getPosition() > Demangle.getPosition()) - Demangle = !NoDemangle; - for (unsigned i = 0; i < ArchFlags.size(); ++i) { - if (ArchFlags[i] == "all") { - ArchAll = true; - } else { - if (!MachOObjectFile::isValidArch(ArchFlags[i])) - error("Unknown architecture named '" + ArchFlags[i] + "'", + for (const auto *A : Args.filtered(OPT_arch_EQ)) { + SmallVector<StringRef, 2> Values; + llvm::SplitString(A->getValue(), Values, ","); + for (StringRef V : Values) { + if (V == "all") + ArchAll = true; + else if (MachOObjectFile::isValidArch(V)) + ArchFlags.push_back(V); + else + error("Unknown architecture named '" + V + "'", "for the --arch option"); } } + // Mach-O takes -s to accept two arguments. We emulate this by iterating over + // both OPT_s and OPT_INPUT. + std::vector<std::string> InputFilenames; + int SegSectArgs = 0; + for (opt::Arg *A : Args.filtered(OPT_s, OPT_INPUT)) { + if (SegSectArgs > 0) { + --SegSectArgs; + SegSect.push_back(A->getValue()); + } else if (A->getOption().matches(OPT_s)) { + SegSectArgs = 2; + } else { + InputFilenames.push_back(A->getValue()); + } + } if (!SegSect.empty() && SegSect.size() != 2) error("bad number of arguments (must be two arguments)", "for the -s option"); + if (InputFilenames.empty()) + InputFilenames.push_back("a.out"); + if (InputFilenames.size() > 1) + MultipleFiles = true; + if (NoDyldInfo && (AddDyldInfo || DyldInfoOnly)) error("--no-dyldinfo can't be used with --add-dyldinfo or --dyldinfo-only"); diff --git a/llvm/tools/llvm-objcopy/Buffer.cpp b/llvm/tools/llvm-objcopy/Buffer.cpp deleted file mode 100644 index 06b2a20a762f..000000000000 --- a/llvm/tools/llvm-objcopy/Buffer.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//===- Buffer.cpp ---------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Buffer.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Process.h" -#include <memory> - -namespace llvm { -namespace objcopy { - -Buffer::~Buffer() {} - -static Error createEmptyFile(StringRef FileName) { - // Create an empty tempfile and atomically swap it in place with the desired - // output file. - Expected<sys::fs::TempFile> Temp = - sys::fs::TempFile::create(FileName + ".temp-empty-%%%%%%%"); - return Temp ? Temp->keep(FileName) : Temp.takeError(); -} - -Error FileBuffer::allocate(size_t Size) { - // When a 0-sized file is requested, skip allocation but defer file - // creation/truncation until commit() to avoid side effects if something - // happens between allocate() and commit(). - if (Size == 0) { - EmptyFile = true; - return Error::success(); - } - - Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = - FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable); - // FileOutputBuffer::create() returns an Error that is just a wrapper around - // std::error_code. Wrap it in FileError to include the actual filename. - if (!BufferOrErr) - return createFileError(getName(), BufferOrErr.takeError()); - Buf = std::move(*BufferOrErr); - return Error::success(); -} - -Error FileBuffer::commit() { - if (EmptyFile) - return createEmptyFile(getName()); - - assert(Buf && "allocate() not called before commit()!"); - Error Err = Buf->commit(); - // FileOutputBuffer::commit() returns an Error that is just a wrapper around - // std::error_code. Wrap it in FileError to include the actual filename. - return Err ? createFileError(getName(), std::move(Err)) : std::move(Err); -} - -uint8_t *FileBuffer::getBufferStart() { - return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); -} - -Error MemBuffer::allocate(size_t Size) { - Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName()); - return Error::success(); -} - -Error MemBuffer::commit() { return Error::success(); } - -uint8_t *MemBuffer::getBufferStart() { - return reinterpret_cast<uint8_t *>(Buf->getBufferStart()); -} - -std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() { - return std::move(Buf); -} - -} // end namespace objcopy -} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/Buffer.h b/llvm/tools/llvm-objcopy/Buffer.h deleted file mode 100644 index 487d5585c364..000000000000 --- a/llvm/tools/llvm-objcopy/Buffer.h +++ /dev/null @@ -1,68 +0,0 @@ -//===- Buffer.h -------------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OBJCOPY_BUFFER_H -#define LLVM_TOOLS_OBJCOPY_BUFFER_H - -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/MemoryBuffer.h" -#include <memory> - -namespace llvm { -namespace objcopy { - -// The class Buffer abstracts out the common interface of FileOutputBuffer and -// WritableMemoryBuffer so that the hierarchy of Writers depends on this -// abstract interface and doesn't depend on a particular implementation. -// TODO: refactor the buffer classes in LLVM to enable us to use them here -// directly. -class Buffer { - StringRef Name; - -public: - virtual ~Buffer(); - virtual Error allocate(size_t Size) = 0; - virtual uint8_t *getBufferStart() = 0; - virtual Error commit() = 0; - - explicit Buffer(StringRef Name) : Name(Name) {} - StringRef getName() const { return Name; } -}; - -class FileBuffer : public Buffer { - std::unique_ptr<FileOutputBuffer> Buf; - // Indicates that allocate(0) was called, and commit() should create or - // truncate a file instead of using a FileOutputBuffer. - bool EmptyFile = false; - -public: - Error allocate(size_t Size) override; - uint8_t *getBufferStart() override; - Error commit() override; - - explicit FileBuffer(StringRef FileName) : Buffer(FileName) {} -}; - -class MemBuffer : public Buffer { - std::unique_ptr<WritableMemoryBuffer> Buf; - -public: - Error allocate(size_t Size) override; - uint8_t *getBufferStart() override; - Error commit() override; - - explicit MemBuffer(StringRef Name) : Buffer(Name) {} - - std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer(); -}; - -} // end namespace objcopy -} // end namespace llvm - -#endif // LLVM_TOOLS_OBJCOPY_BUFFER_H diff --git a/llvm/tools/llvm-objcopy/COFF/COFFConfig.h b/llvm/tools/llvm-objcopy/COFF/COFFConfig.h new file mode 100644 index 000000000000..3897ff47724b --- /dev/null +++ b/llvm/tools/llvm-objcopy/COFF/COFFConfig.h @@ -0,0 +1,21 @@ +//===- COFFConfig.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H + +namespace llvm { +namespace objcopy { + +// Coff specific configuration for copying/stripping a single file. +struct COFFConfig {}; + +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_COFF_COFFCONFIG_H diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp index b5de8a45a80f..e50ac2e12e2f 100644 --- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "COFFObjcopy.h" -#include "Buffer.h" -#include "CopyConfig.h" +#include "COFFConfig.h" +#include "CommonConfig.h" #include "Object.h" #include "Reader.h" #include "Writer.h" @@ -131,7 +131,7 @@ static void setSectionFlags(Section &Sec, SectionFlag AllFlags) { Sec.Header.Characteristics = NewCharacteristics; } -static Error handleArgs(const CopyConfig &Config, Object &Obj) { +static Error handleArgs(const CommonConfig &Config, Object &Obj) { // Perform the actual section removals. Obj.removeSections([&Config](const Section &Sec) { // Contrary to --only-keep-debug, --only-section fully removes sections that @@ -249,30 +249,11 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { if (Error E = addGnuDebugLink(Obj, Config.AddGnuDebugLink)) return E; - if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || - Config.BuildIdLinkInput || Config.BuildIdLinkOutput || - !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || - !Config.AllocSectionsPrefix.empty() || !Config.DumpSection.empty() || - !Config.KeepSection.empty() || Config.NewSymbolVisibility || - !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || - !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || - !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || - !Config.SetSectionAlignment.empty() || Config.ExtractDWO || - Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO || - Config.StripNonAlloc || Config.StripSections || - Config.StripSwiftSymbols || Config.Weaken || - Config.DecompressDebugSections || - Config.DiscardMode == DiscardType::Locals || - !Config.SymbolsToAdd.empty() || Config.EntryExpr) { - return createStringError(llvm::errc::invalid_argument, - "option not supported by llvm-objcopy for COFF"); - } - return Error::success(); } -Error executeObjcopyOnBinary(const CopyConfig &Config, COFFObjectFile &In, - Buffer &Out) { +Error executeObjcopyOnBinary(const CommonConfig &Config, const COFFConfig &, + COFFObjectFile &In, raw_ostream &Out) { COFFReader Reader(In); Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create(); if (!ObjOrErr) diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h index 858759e52c4a..2c7ccd34653d 100644 --- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h @@ -11,18 +11,20 @@ namespace llvm { class Error; +class raw_ostream; namespace object { class COFFObjectFile; } // end namespace object namespace objcopy { -struct CopyConfig; -class Buffer; +struct CommonConfig; +struct COFFConfig; namespace coff { -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::COFFObjectFile &In, Buffer &Out); + +Error executeObjcopyOnBinary(const CommonConfig &Config, const COFFConfig &, + object::COFFObjectFile &In, raw_ostream &Out); } // end namespace coff } // end namespace objcopy diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.h b/llvm/tools/llvm-objcopy/COFF/Reader.h index ec15369db0b8..48c050b6ea11 100644 --- a/llvm/tools/llvm-objcopy/COFF/Reader.h +++ b/llvm/tools/llvm-objcopy/COFF/Reader.h @@ -9,7 +9,6 @@ #ifndef LLVM_TOOLS_OBJCOPY_COFF_READER_H #define LLVM_TOOLS_OBJCOPY_COFF_READER_H -#include "Buffer.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Error.h" diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp index 6b560890a4c1..e7be64faab65 100644 --- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp +++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp @@ -12,6 +12,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include <cstddef> #include <cstdint> @@ -240,7 +241,7 @@ Error COFFWriter::finalize(bool IsBigObj) { } void COFFWriter::writeHeaders(bool IsBigObj) { - uint8_t *Ptr = Buf.getBufferStart(); + uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()); if (Obj.IsPE) { memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader)); Ptr += sizeof(Obj.DosHeader); @@ -302,7 +303,8 @@ void COFFWriter::writeHeaders(bool IsBigObj) { void COFFWriter::writeSections() { for (const auto &S : Obj.getSections()) { - uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData; + uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + + S.Header.PointerToRawData; ArrayRef<uint8_t> Contents = S.getContents(); std::copy(Contents.begin(), Contents.end(), Ptr); @@ -331,7 +333,8 @@ void COFFWriter::writeSections() { } template <class SymbolTy> void COFFWriter::writeSymbolStringTables() { - uint8_t *Ptr = Buf.getBufferStart() + Obj.CoffFileHeader.PointerToSymbolTable; + uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + + Obj.CoffFileHeader.PointerToSymbolTable; for (const auto &S : Obj.getSymbols()) { // Convert symbols back to the right size, from coff_symbol32. copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr), @@ -366,8 +369,11 @@ Error COFFWriter::write(bool IsBigObj) { if (Error E = finalize(IsBigObj)) return E; - if (Error E = Buf.allocate(FileSize)) - return E; + Buf = WritableMemoryBuffer::getNewMemBuffer(FileSize); + if (!Buf) + return createStringError(llvm::errc::not_enough_memory, + "failed to allocate memory buffer of " + + Twine::utohexstr(FileSize) + " bytes."); writeHeaders(IsBigObj); writeSections(); @@ -380,7 +386,10 @@ Error COFFWriter::write(bool IsBigObj) { if (Error E = patchDebugDirectory()) return E; - return Buf.commit(); + // TODO: Implement direct writing to the output stream (without intermediate + // memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + return Error::success(); } Expected<uint32_t> COFFWriter::virtualAddressToFileAddress(uint32_t RVA) { @@ -412,7 +421,8 @@ Error COFFWriter::patchDebugDirectory() { "debug directory extends past end of section"); size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress; - uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset; + uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + + S.Header.PointerToRawData + Offset; uint8_t *End = Ptr + Dir->Size; while (Ptr < End) { debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr); diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.h b/llvm/tools/llvm-objcopy/COFF/Writer.h index 3c0bdcbd5d6f..eed43b3e5814 100644 --- a/llvm/tools/llvm-objcopy/COFF/Writer.h +++ b/llvm/tools/llvm-objcopy/COFF/Writer.h @@ -9,9 +9,9 @@ #ifndef LLVM_TOOLS_OBJCOPY_COFF_WRITER_H #define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H -#include "Buffer.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" #include <cstddef> #include <utility> @@ -23,7 +23,8 @@ struct Object; class COFFWriter { Object &Obj; - Buffer &Buf; + std::unique_ptr<WritableMemoryBuffer> Buf; + raw_ostream &Out; size_t FileSize; size_t FileAlignment; @@ -51,8 +52,8 @@ public: virtual ~COFFWriter() {} Error write(); - COFFWriter(Object &Obj, Buffer &Buf) - : Obj(Obj), Buf(Buf), StrTabBuilder(StringTableBuilder::WinCOFF) {} + COFFWriter(Object &Obj, raw_ostream &Out) + : Obj(Obj), Out(Out), StrTabBuilder(StringTableBuilder::WinCOFF) {} }; } // end namespace coff diff --git a/llvm/tools/llvm-objcopy/CopyConfig.h b/llvm/tools/llvm-objcopy/CommonConfig.h index 07eac9d2bb1b..131ce5c59114 100644 --- a/llvm/tools/llvm-objcopy/CopyConfig.h +++ b/llvm/tools/llvm-objcopy/CommonConfig.h @@ -1,4 +1,4 @@ -//===- CopyConfig.h -------------------------------------------------------===// +//===- CommonConfig.h -------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,20 +6,17 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H -#define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H -#include "ELF/ELFConfig.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/ELFTypes.h" -#include "llvm/Support/Allocator.h" -#include "llvm/Support/Error.h" #include "llvm/Support/GlobPattern.h" #include "llvm/Support/Regex.h" // Necessary for llvm::DebugCompressionType::None @@ -99,7 +96,7 @@ enum class MatchStyle { class NameOrPattern { StringRef Name; - // Regex is shared between multiple CopyConfig instances. + // Regex is shared between multiple CommonConfig instances. std::shared_ptr<Regex> R; std::shared_ptr<GlobPattern> G; bool IsPositiveMatch = true; @@ -117,6 +114,11 @@ public: llvm::function_ref<Error(Error)> ErrorCallback); bool isPositiveMatch() const { return IsPositiveMatch; } + Optional<StringRef> getName() const { + if (!R && !G) + return Name; + return None; + } bool operator==(StringRef S) const { return R ? R->match(S) : G ? G->match(S) : Name == S; } @@ -126,30 +128,66 @@ public: // Matcher that checks symbol or section names against the command line flags // provided for that option. class NameMatcher { - std::vector<NameOrPattern> PosMatchers; + DenseSet<CachedHashStringRef> PosNames; + std::vector<NameOrPattern> PosPatterns; std::vector<NameOrPattern> NegMatchers; public: Error addMatcher(Expected<NameOrPattern> Matcher) { if (!Matcher) return Matcher.takeError(); - if (Matcher->isPositiveMatch()) - PosMatchers.push_back(std::move(*Matcher)); - else + if (Matcher->isPositiveMatch()) { + if (Optional<StringRef> MaybeName = Matcher->getName()) + PosNames.insert(CachedHashStringRef(*MaybeName)); + else + PosPatterns.push_back(std::move(*Matcher)); + } else { NegMatchers.push_back(std::move(*Matcher)); + } return Error::success(); } bool matches(StringRef S) const { - return is_contained(PosMatchers, S) && !is_contained(NegMatchers, S); + return (PosNames.contains(CachedHashStringRef(S)) || + is_contained(PosPatterns, S)) && + !is_contained(NegMatchers, S); + } + bool empty() const { + return PosNames.empty() && PosPatterns.empty() && NegMatchers.empty(); } - bool empty() const { return PosMatchers.empty() && NegMatchers.empty(); } }; -// Configuration for copying/stripping a single file. -struct CopyConfig { - // Format-specific options to be initialized lazily when needed. - Optional<elf::ELFCopyConfig> ELF; +enum class SymbolFlag { + Global, + Local, + Weak, + Default, + Hidden, + Protected, + File, + Section, + Object, + Function, + IndirectFunction, + Debug, + Constructor, + Warning, + Indirect, + Synthetic, + UniqueObject, +}; + +// Symbol info specified by --add-symbol option. Symbol flags not supported +// by a concrete format should be ignored. +struct NewSymbolInfo { + StringRef SymbolName; + StringRef SectionName; + uint64_t Value = 0; + std::vector<SymbolFlag> Flags; + std::vector<StringRef> BeforeSyms; +}; +// Configuration for copying/stripping a single file. +struct CommonConfig { // Main input/output options StringRef InputFilename; FileFormat InputFormat = FileFormat::Unspecified; @@ -163,20 +201,15 @@ struct CopyConfig { StringRef AddGnuDebugLink; // Cached gnu_debuglink's target CRC uint32_t GnuDebugLinkCRC32; - StringRef BuildIdLinkDir; - Optional<StringRef> BuildIdLinkInput; - Optional<StringRef> BuildIdLinkOutput; Optional<StringRef> ExtractPartition; StringRef SplitDWO; StringRef SymbolsPrefix; StringRef AllocSectionsPrefix; DiscardType DiscardMode = DiscardType::None; - Optional<StringRef> NewSymbolVisibility; // Repeated options std::vector<StringRef> AddSection; std::vector<StringRef> DumpSection; - std::vector<StringRef> SymbolsToAdd; std::vector<StringRef> RPathToAdd; std::vector<StringRef> RPathToPrepend; DenseMap<StringRef, StringRef> RPathsToUpdate; @@ -212,12 +245,16 @@ struct CopyConfig { // --change-start is used. std::function<uint64_t(uint64_t)> EntryExpr; + // Symbol info specified by --add-symbol option. + std::vector<NewSymbolInfo> SymbolsToAdd; + // Boolean options bool AllowBrokenLinks = false; bool DeterministicArchives = true; bool ExtractDWO = false; bool ExtractMainPartition = false; bool KeepFileSymbols = false; + bool KeepUndefined = false; bool LocalizeHidden = false; bool OnlyKeepDebug = false; bool PreserveDates = false; @@ -235,55 +272,9 @@ struct CopyConfig { bool RemoveAllRpaths = false; DebugCompressionType CompressionType = DebugCompressionType::None; - - // parseELFConfig performs ELF-specific command-line parsing. Fills `ELF` on - // success or returns an Error otherwise. - Error parseELFConfig() { - if (!ELF) { - Expected<elf::ELFCopyConfig> ELFConfig = elf::parseConfig(*this); - if (!ELFConfig) - return ELFConfig.takeError(); - ELF = *ELFConfig; - } - return Error::success(); - } -}; - -// Configuration for the overall invocation of this tool. When invoked as -// objcopy, will always contain exactly one CopyConfig. When invoked as strip, -// will contain one or more CopyConfigs. -struct DriverConfig { - SmallVector<CopyConfig, 1> CopyConfigs; - BumpPtrAllocator Alloc; }; -// ParseObjcopyOptions returns the config and sets the input arguments. If a -// help flag is set then ParseObjcopyOptions will print the help messege and -// exit. ErrorCallback is used to handle recoverable errors. An Error returned -// by the callback aborts the parsing and is then returned by this function. -Expected<DriverConfig> -parseObjcopyOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback); - -// ParseInstallNameToolOptions returns the config and sets the input arguments. -// If a help flag is set then ParseInstallNameToolOptions will print the help -// messege and exit. -Expected<DriverConfig> -parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr); - -// ParseBitcodeStripOptions returns the config and sets the input arguments. -// If a help flag is set then ParseBitcodeStripOptions will print the help -// messege and exit. -Expected<DriverConfig> parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr); - -// ParseStripOptions returns the config and sets the input arguments. If a -// help flag is set then ParseStripOptions will print the help messege and -// exit. ErrorCallback is used to handle recoverable errors. An Error returned -// by the callback aborts the parsing and is then returned by this function. -Expected<DriverConfig> -parseStripOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback); } // namespace objcopy } // namespace llvm -#endif +#endif // LLVM_TOOLS_LLVM_OBJCOPY_COMMONCONFIG_H diff --git a/llvm/tools/llvm-objcopy/CommonOpts.td b/llvm/tools/llvm-objcopy/CommonOpts.td index 6481d1d1df05..4222532a1a38 100644 --- a/llvm/tools/llvm-objcopy/CommonOpts.td +++ b/llvm/tools/llvm-objcopy/CommonOpts.td @@ -84,6 +84,9 @@ def K : JoinedOrSeparate<["-"], "K">, def keep_file_symbols : Flag<["--"], "keep-file-symbols">, HelpText<"Do not remove file symbols">; +def keep_undefined : Flag<["--"], "keep-undefined">, + HelpText<"Do not remove undefined symbols">; + def only_keep_debug : Flag<["--"], "only-keep-debug">, HelpText< @@ -99,7 +102,8 @@ def X : Flag<["-"], "X">, def discard_all : Flag<["--"], "discard-all">, - HelpText<"Remove all local symbols except file and section symbols">; + HelpText<"Remove all local symbols except file and section symbols. Also " + "remove all debug sections">; def x : Flag<["-"], "x">, Alias<discard_all>, HelpText<"Alias for --discard-all">; diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/ConfigManager.cpp index ba74759a34c2..9f7d06b99418 100644 --- a/llvm/tools/llvm-objcopy/CopyConfig.cpp +++ b/llvm/tools/llvm-objcopy/ConfigManager.cpp @@ -1,4 +1,4 @@ -//===- CopyConfig.cpp -----------------------------------------------------===// +//===- ConfigManager.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,8 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "CopyConfig.h" - +#include "ConfigManager.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -18,12 +17,13 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/StringSaver.h" #include <memory> -namespace llvm { -namespace objcopy { +using namespace llvm; +using namespace llvm::objcopy; namespace { enum ObjcopyID { @@ -60,7 +60,9 @@ static const opt::OptTable::Info ObjcopyInfoTable[] = { class ObjcopyOptTable : public opt::OptTable { public: - ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {} + ObjcopyOptTable() : OptTable(ObjcopyInfoTable) { + setGroupedShortOptions(true); + } }; enum InstallNameToolID { @@ -164,7 +166,7 @@ static const opt::OptTable::Info StripInfoTable[] = { class StripOptTable : public opt::OptTable { public: - StripOptTable() : OptTable(StripInfoTable) {} + StripOptTable() : OptTable(StripInfoTable) { setGroupedShortOptions(true); } }; } // namespace @@ -244,9 +246,10 @@ parseSetSectionAlignment(StringRef FlagValue) { "bad format for --set-section-alignment: missing section name"); uint64_t NewAlign; if (Split.second.getAsInteger(0, NewAlign)) - return createStringError(errc::invalid_argument, - "invalid alignment for --set-section-alignment: '%s'", - Split.second.str().c_str()); + return createStringError( + errc::invalid_argument, + "invalid alignment for --set-section-alignment: '%s'", + Split.second.str().c_str()); return std::make_pair(Split.first, NewAlign); } @@ -272,10 +275,12 @@ parseSetSectionFlagValue(StringRef FlagValue) { return SFU; } +namespace { struct TargetInfo { FileFormat Format; MachineInfo Machine; }; +} // namespace // FIXME: consolidate with the bfd parsing used by lld. static const StringMap<MachineInfo> TargetMap{ @@ -337,10 +342,9 @@ getOutputTargetInfoByTargetName(StringRef TargetName) { return {TargetInfo{Format, MI}}; } -static Error -addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, - StringRef Filename, MatchStyle MS, - llvm::function_ref<Error(Error)> ErrorCallback) { +static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, + StringRef Filename, MatchStyle MS, + function_ref<Error(Error)> ErrorCallback) { StringSaver Saver(Alloc); SmallVector<StringRef, 16> Lines; auto BufOrErr = MemoryBuffer::getFile(Filename); @@ -363,7 +367,7 @@ addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, Expected<NameOrPattern> NameOrPattern::create(StringRef Pattern, MatchStyle MS, - llvm::function_ref<Error(Error)> ErrorCallback) { + function_ref<Error(Error)> ErrorCallback) { switch (MS) { case MatchStyle::Literal: return NameOrPattern(Pattern); @@ -457,7 +461,7 @@ static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS, HelpText = " [options] input"; break; } - OptTable.PrintHelp(OS, (ToolName + HelpText).str().c_str(), + OptTable.printHelp(OS, (ToolName + HelpText).str().c_str(), (ToolName + " tool").str().c_str()); // TODO: Replace this with libOption call once it adds extrahelp support. // The CommandLine library has a cl::extrahelp class to support this, @@ -465,19 +469,186 @@ static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS, OS << "\nPass @FILE as argument to read options from FILE.\n"; } +static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue) { + // Parse value given with --add-symbol option and create the + // new symbol if possible. The value format for --add-symbol is: + // + // <name>=[<section>:]<value>[,<flags>] + // + // where: + // <name> - symbol name, can be empty string + // <section> - optional section name. If not given ABS symbol is created + // <value> - symbol value, can be decimal or hexadecimal number prefixed + // with 0x. + // <flags> - optional flags affecting symbol type, binding or visibility. + NewSymbolInfo SI; + StringRef Value; + std::tie(SI.SymbolName, Value) = FlagValue.split('='); + if (Value.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --add-symbol, missing '=' after '%s'", + SI.SymbolName.str().c_str()); + + if (Value.contains(':')) { + std::tie(SI.SectionName, Value) = Value.split(':'); + if (SI.SectionName.empty() || Value.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --add-symbol, missing section name or symbol value"); + } + + SmallVector<StringRef, 6> Flags; + Value.split(Flags, ','); + if (Flags[0].getAsInteger(0, SI.Value)) + return createStringError(errc::invalid_argument, "bad symbol value: '%s'", + Flags[0].str().c_str()); + + using Functor = std::function<void()>; + SmallVector<StringRef, 6> UnsupportedFlags; + for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) + static_cast<Functor>( + StringSwitch<Functor>(Flags[I]) + .CaseLower("global", + [&] { SI.Flags.push_back(SymbolFlag::Global); }) + .CaseLower("local", [&] { SI.Flags.push_back(SymbolFlag::Local); }) + .CaseLower("weak", [&] { SI.Flags.push_back(SymbolFlag::Weak); }) + .CaseLower("default", + [&] { SI.Flags.push_back(SymbolFlag::Default); }) + .CaseLower("hidden", + [&] { SI.Flags.push_back(SymbolFlag::Hidden); }) + .CaseLower("protected", + [&] { SI.Flags.push_back(SymbolFlag::Protected); }) + .CaseLower("file", [&] { SI.Flags.push_back(SymbolFlag::File); }) + .CaseLower("section", + [&] { SI.Flags.push_back(SymbolFlag::Section); }) + .CaseLower("object", + [&] { SI.Flags.push_back(SymbolFlag::Object); }) + .CaseLower("function", + [&] { SI.Flags.push_back(SymbolFlag::Function); }) + .CaseLower( + "indirect-function", + [&] { SI.Flags.push_back(SymbolFlag::IndirectFunction); }) + .CaseLower("debug", [&] { SI.Flags.push_back(SymbolFlag::Debug); }) + .CaseLower("constructor", + [&] { SI.Flags.push_back(SymbolFlag::Constructor); }) + .CaseLower("warning", + [&] { SI.Flags.push_back(SymbolFlag::Warning); }) + .CaseLower("indirect", + [&] { SI.Flags.push_back(SymbolFlag::Indirect); }) + .CaseLower("synthetic", + [&] { SI.Flags.push_back(SymbolFlag::Synthetic); }) + .CaseLower("unique-object", + [&] { SI.Flags.push_back(SymbolFlag::UniqueObject); }) + .StartsWithLower("before=", + [&] { + StringRef SymNamePart = + Flags[I].split('=').second; + + if (!SymNamePart.empty()) + SI.BeforeSyms.push_back(SymNamePart); + }) + .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))(); + if (!UnsupportedFlags.empty()) + return createStringError(errc::invalid_argument, + "unsupported flag%s for --add-symbol: '%s'", + UnsupportedFlags.size() > 1 ? "s" : "", + join(UnsupportedFlags, "', '").c_str()); + + return SI; +} + +Expected<const ELFConfig &> ConfigManager::getELFConfig() const { + if (Common.StripSwiftSymbols || Common.KeepUndefined) + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for ELF"); + + return ELF; +} + +Expected<const COFFConfig &> ConfigManager::getCOFFConfig() const { + if (Common.AllowBrokenLinks || !Common.SplitDWO.empty() || + !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() || + !Common.DumpSection.empty() || !Common.KeepSection.empty() || + ELF.NewSymbolVisibility || !Common.SymbolsToGlobalize.empty() || + !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() || + !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || + !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() || + Common.ExtractDWO || Common.LocalizeHidden || Common.PreserveDates || + Common.StripDWO || Common.StripNonAlloc || Common.StripSections || + Common.StripSwiftSymbols || Common.KeepUndefined || Common.Weaken || + Common.DecompressDebugSections || + Common.DiscardMode == DiscardType::Locals || + !Common.SymbolsToAdd.empty() || Common.EntryExpr) { + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for COFF"); + } + + return COFF; +} + +Expected<const MachOConfig &> ConfigManager::getMachOConfig() const { + if (Common.AllowBrokenLinks || !Common.SplitDWO.empty() || + !Common.SymbolsPrefix.empty() || !Common.AllocSectionsPrefix.empty() || + !Common.KeepSection.empty() || ELF.NewSymbolVisibility || + !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToKeep.empty() || + !Common.SymbolsToLocalize.empty() || !Common.SymbolsToWeaken.empty() || + !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || + !Common.UnneededSymbolsToRemove.empty() || + !Common.SetSectionAlignment.empty() || !Common.SetSectionFlags.empty() || + Common.ExtractDWO || Common.LocalizeHidden || Common.PreserveDates || + Common.StripAllGNU || Common.StripDWO || Common.StripNonAlloc || + Common.StripSections || Common.Weaken || Common.DecompressDebugSections || + Common.StripUnneeded || Common.DiscardMode == DiscardType::Locals || + !Common.SymbolsToAdd.empty() || Common.EntryExpr) { + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for MachO"); + } + + return MachO; +} + +Expected<const WasmConfig &> ConfigManager::getWasmConfig() const { + if (!Common.AddGnuDebugLink.empty() || Common.ExtractPartition || + !Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() || + !Common.AllocSectionsPrefix.empty() || + Common.DiscardMode != DiscardType::None || ELF.NewSymbolVisibility || + !Common.SymbolsToAdd.empty() || !Common.RPathToAdd.empty() || + !Common.SymbolsToGlobalize.empty() || !Common.SymbolsToLocalize.empty() || + !Common.SymbolsToKeep.empty() || !Common.SymbolsToRemove.empty() || + !Common.UnneededSymbolsToRemove.empty() || + !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || + !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() || + !Common.SetSectionFlags.empty() || !Common.SymbolsToRename.empty()) { + return createStringError( + llvm::errc::invalid_argument, + "only flags for section dumping, removal, and addition are supported"); + } + + return Wasm; +} + // ParseObjcopyOptions returns the config and sets the input arguments. If a // help flag is set then ParseObjcopyOptions will print the help messege and // exit. Expected<DriverConfig> -parseObjcopyOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback) { +objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr, + function_ref<Error(Error)> ErrorCallback) { DriverConfig DC; ObjcopyOptTable T; + + const char *const *DashDash = + std::find_if(RawArgsArr.begin(), RawArgsArr.end(), + [](StringRef Str) { return Str == "--"; }); + ArrayRef<const char *> ArgsArr = makeArrayRef(RawArgsArr.begin(), DashDash); + if (DashDash != RawArgsArr.end()) + DashDash = std::next(DashDash); + unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); - if (InputArgs.size() == 0) { + if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) { printHelp(T, errs(), ToolType::Objcopy); exit(1); } @@ -501,6 +672,7 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT)) Positional.push_back(Arg->getValue()); + std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional)); if (Positional.empty()) return createStringError(errc::invalid_argument, "no input file specified"); @@ -509,7 +681,9 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, return createStringError(errc::invalid_argument, "too many positional arguments"); - CopyConfig Config; + ConfigManager ConfigMgr; + CommonConfig &Config = ConfigMgr.Common; + ELFConfig &ELFConfig = ConfigMgr.ELF; Config.InputFilename = Positional[0]; Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1]; if (InputArgs.hasArg(OBJCOPY_target) && @@ -548,10 +722,24 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, .Case("ihex", FileFormat::IHex) .Default(FileFormat::Unspecified); - if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) - Config.NewSymbolVisibility = + if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) { + const uint8_t Invalid = 0xff; + StringRef VisibilityStr = InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility); + ELFConfig.NewSymbolVisibility = StringSwitch<uint8_t>(VisibilityStr) + .Case("default", ELF::STV_DEFAULT) + .Case("hidden", ELF::STV_HIDDEN) + .Case("internal", ELF::STV_INTERNAL) + .Case("protected", ELF::STV_PROTECTED) + .Default(Invalid); + + if (ELFConfig.NewSymbolVisibility == Invalid) + return createStringError(errc::invalid_argument, + "'%s' is not a valid symbol visibility", + VisibilityStr.str().c_str()); + } + Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat) .Case("binary", FileFormat::Binary) .Case("ihex", FileFormat::IHex) @@ -607,13 +795,6 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, Config.GnuDebugLinkCRC32 = llvm::crc32(arrayRefFromStringRef(Debug->getBuffer())); } - Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); - if (InputArgs.hasArg(OBJCOPY_build_id_link_input)) - Config.BuildIdLinkInput = - InputArgs.getLastArgValue(OBJCOPY_build_id_link_input); - if (InputArgs.hasArg(OBJCOPY_build_id_link_output)) - Config.BuildIdLinkOutput = - InputArgs.getLastArgValue(OBJCOPY_build_id_link_output); Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols); Config.AllocSectionsPrefix = @@ -706,8 +887,14 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, "bad format for --add-section: missing file name"); Config.AddSection.push_back(ArgValue); } - for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section)) - Config.DumpSection.push_back(Arg->getValue()); + for (auto *Arg : InputArgs.filtered(OBJCOPY_dump_section)) { + StringRef Value(Arg->getValue()); + if (Value.split('=').second.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --dump-section, expected section=file"); + Config.DumpSection.push_back(Value); + } Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu); Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug); @@ -727,6 +914,7 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, : DiscardType::Locals; Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); + Config.KeepUndefined = InputArgs.hasArg(OBJCOPY_keep_undefined); Config.DecompressDebugSections = InputArgs.hasArg(OBJCOPY_decompress_debug_sections); if (Config.DiscardMode == DiscardType::All) { @@ -797,8 +985,13 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(), SymbolMatchStyle, ErrorCallback)) return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) - Config.SymbolsToAdd.push_back(Arg->getValue()); + for (auto *Arg : InputArgs.filtered(OBJCOPY_add_symbol)) { + Expected<NewSymbolInfo> SymInfo = parseNewSymbolInfo(Arg->getValue()); + if (!SymInfo) + return SymInfo.takeError(); + + Config.SymbolsToAdd.push_back(*SymInfo); + } Config.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links); @@ -852,7 +1045,7 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, "cannot specify --extract-partition together with " "--extract-main-partition"); - DC.CopyConfigs.push_back(std::move(Config)); + DC.CopyConfigs.push_back(std::move(ConfigMgr)); return std::move(DC); } @@ -860,9 +1053,10 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, // If a help flag is set then ParseInstallNameToolOptions will print the help // messege and exit. Expected<DriverConfig> -parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { +objcopy::parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { DriverConfig DC; - CopyConfig Config; + ConfigManager ConfigMgr; + CommonConfig &Config = ConfigMgr.Common; InstallNameToolOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = @@ -986,14 +1180,15 @@ parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { Config.InputFilename = Positional[0]; Config.OutputFilename = Positional[0]; - DC.CopyConfigs.push_back(std::move(Config)); + DC.CopyConfigs.push_back(std::move(ConfigMgr)); return std::move(DC); } Expected<DriverConfig> -parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) { +objcopy::parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) { DriverConfig DC; - CopyConfig Config; + ConfigManager ConfigMgr; + CommonConfig &Config = ConfigMgr.Common; BitcodeStripOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; opt::InputArgList InputArgs = @@ -1030,7 +1225,7 @@ parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) { Config.InputFilename = Positional[0]; Config.OutputFilename = Positional[0]; - DC.CopyConfigs.push_back(std::move(Config)); + DC.CopyConfigs.push_back(std::move(ConfigMgr)); return std::move(DC); } @@ -1038,14 +1233,21 @@ parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr) { // help flag is set then ParseStripOptions will print the help messege and // exit. Expected<DriverConfig> -parseStripOptions(ArrayRef<const char *> ArgsArr, - llvm::function_ref<Error(Error)> ErrorCallback) { +objcopy::parseStripOptions(ArrayRef<const char *> RawArgsArr, + function_ref<Error(Error)> ErrorCallback) { + const char *const *DashDash = + std::find_if(RawArgsArr.begin(), RawArgsArr.end(), + [](StringRef Str) { return Str == "--"; }); + ArrayRef<const char *> ArgsArr = makeArrayRef(RawArgsArr.begin(), DashDash); + if (DashDash != RawArgsArr.end()) + DashDash = std::next(DashDash); + StripOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); - if (InputArgs.size() == 0) { + if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) { printHelp(T, errs(), ToolType::Strip); exit(1); } @@ -1067,6 +1269,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, Arg->getAsString(InputArgs).c_str()); for (auto Arg : InputArgs.filtered(STRIP_INPUT)) Positional.push_back(Arg->getValue()); + std::copy(DashDash, RawArgsArr.end(), std::back_inserter(Positional)); if (Positional.empty()) return createStringError(errc::invalid_argument, "no input file specified"); @@ -1076,7 +1279,8 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, errc::invalid_argument, "multiple input files cannot be used in combination with -o"); - CopyConfig Config; + ConfigManager ConfigMgr; + CommonConfig &Config = ConfigMgr.Common; if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard)) return createStringError(errc::invalid_argument, @@ -1104,6 +1308,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, Config.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols); Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug); Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); + Config.KeepUndefined = InputArgs.hasArg(STRIP_keep_undefined); for (auto Arg : InputArgs.filtered(STRIP_keep_section)) if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( @@ -1148,7 +1353,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, Config.InputFilename = Positional[0]; Config.OutputFilename = InputArgs.getLastArgValue(STRIP_output, Positional[0]); - DC.CopyConfigs.push_back(std::move(Config)); + DC.CopyConfigs.push_back(std::move(ConfigMgr)); } else { StringMap<unsigned> InputFiles; for (StringRef Filename : Positional) { @@ -1164,7 +1369,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, } Config.InputFilename = Filename; Config.OutputFilename = Filename; - DC.CopyConfigs.push_back(Config); + DC.CopyConfigs.push_back(ConfigMgr); } } @@ -1175,6 +1380,3 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, return std::move(DC); } - -} // namespace objcopy -} // namespace llvm diff --git a/llvm/tools/llvm-objcopy/ConfigManager.h b/llvm/tools/llvm-objcopy/ConfigManager.h new file mode 100644 index 000000000000..c0d0e8bbc721 --- /dev/null +++ b/llvm/tools/llvm-objcopy/ConfigManager.h @@ -0,0 +1,80 @@ +//===- ConfigManager.h ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H +#define LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H + +#include "COFF/COFFConfig.h" +#include "CommonConfig.h" +#include "ELF/ELFConfig.h" +#include "MachO/MachOConfig.h" +#include "MultiFormatConfig.h" +#include "wasm/WasmConfig.h" +#include "llvm/Support/Allocator.h" +#include <vector> + +namespace llvm { +namespace objcopy { + +// ConfigManager keeps all configurations and prepare +// format-specific options. +struct ConfigManager : public MultiFormatConfig { + virtual ~ConfigManager() {} + + const CommonConfig &getCommonConfig() const override { return Common; } + Expected<const ELFConfig &> getELFConfig() const override; + Expected<const COFFConfig &> getCOFFConfig() const override; + Expected<const MachOConfig &> getMachOConfig() const override; + Expected<const WasmConfig &> getWasmConfig() const override; + + // All configs. + CommonConfig Common; + ELFConfig ELF; + COFFConfig COFF; + MachOConfig MachO; + WasmConfig Wasm; +}; + +// Configuration for the overall invocation of this tool. When invoked as +// objcopy, will always contain exactly one CopyConfig. When invoked as strip, +// will contain one or more CopyConfigs. +struct DriverConfig { + SmallVector<ConfigManager, 1> CopyConfigs; + BumpPtrAllocator Alloc; +}; + +// ParseObjcopyOptions returns the config and sets the input arguments. If a +// help flag is set then ParseObjcopyOptions will print the help messege and +// exit. ErrorCallback is used to handle recoverable errors. An Error returned +// by the callback aborts the parsing and is then returned by this function. +Expected<DriverConfig> +parseObjcopyOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); + +// ParseInstallNameToolOptions returns the config and sets the input arguments. +// If a help flag is set then ParseInstallNameToolOptions will print the help +// messege and exit. +Expected<DriverConfig> +parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr); + +// ParseBitcodeStripOptions returns the config and sets the input arguments. +// If a help flag is set then ParseBitcodeStripOptions will print the help +// messege and exit. +Expected<DriverConfig> parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr); + +// ParseStripOptions returns the config and sets the input arguments. If a +// help flag is set then ParseStripOptions will print the help messege and +// exit. ErrorCallback is used to handle recoverable errors. An Error returned +// by the callback aborts the parsing and is then returned by this function. +Expected<DriverConfig> +parseStripOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_CONFIGMANAGER_H diff --git a/llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp b/llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp deleted file mode 100644 index 40993760add7..000000000000 --- a/llvm/tools/llvm-objcopy/ELF/ELFConfig.cpp +++ /dev/null @@ -1,133 +0,0 @@ -//===- ELFConfig.cpp ------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "CopyConfig.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Error.h" - -namespace llvm { -namespace objcopy { -namespace elf { - -static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue, - uint8_t DefaultVisibility) { - // Parse value given with --add-symbol option and create the - // new symbol if possible. The value format for --add-symbol is: - // - // <name>=[<section>:]<value>[,<flags>] - // - // where: - // <name> - symbol name, can be empty string - // <section> - optional section name. If not given ABS symbol is created - // <value> - symbol value, can be decimal or hexadecimal number prefixed - // with 0x. - // <flags> - optional flags affecting symbol type, binding or visibility: - // The following are currently supported: - // - // global, local, weak, default, hidden, file, section, object, - // indirect-function. - // - // The following flags are ignored and provided for GNU - // compatibility only: - // - // warning, debug, constructor, indirect, synthetic, - // unique-object, before=<symbol>. - NewSymbolInfo SI; - StringRef Value; - std::tie(SI.SymbolName, Value) = FlagValue.split('='); - if (Value.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --add-symbol, missing '=' after '%s'", - SI.SymbolName.str().c_str()); - - if (Value.contains(':')) { - std::tie(SI.SectionName, Value) = Value.split(':'); - if (SI.SectionName.empty() || Value.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --add-symbol, missing section name or symbol value"); - } - - SmallVector<StringRef, 6> Flags; - Value.split(Flags, ','); - if (Flags[0].getAsInteger(0, SI.Value)) - return createStringError(errc::invalid_argument, "bad symbol value: '%s'", - Flags[0].str().c_str()); - - SI.Visibility = DefaultVisibility; - - using Functor = std::function<void(void)>; - SmallVector<StringRef, 6> UnsupportedFlags; - for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) - static_cast<Functor>( - StringSwitch<Functor>(Flags[I]) - .CaseLower("global", [&SI] { SI.Bind = ELF::STB_GLOBAL; }) - .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; }) - .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; }) - .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; }) - .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; }) - .CaseLower("protected", - [&SI] { SI.Visibility = ELF::STV_PROTECTED; }) - .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; }) - .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; }) - .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; }) - .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; }) - .CaseLower("indirect-function", - [&SI] { SI.Type = ELF::STT_GNU_IFUNC; }) - .CaseLower("debug", [] {}) - .CaseLower("constructor", [] {}) - .CaseLower("warning", [] {}) - .CaseLower("indirect", [] {}) - .CaseLower("synthetic", [] {}) - .CaseLower("unique-object", [] {}) - .StartsWithLower("before", [] {}) - .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))(); - if (!UnsupportedFlags.empty()) - return createStringError(errc::invalid_argument, - "unsupported flag%s for --add-symbol: '%s'", - UnsupportedFlags.size() > 1 ? "s" : "", - join(UnsupportedFlags, "', '").c_str()); - return SI; -} - -Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config) { - ELFCopyConfig ELFConfig; - if (Config.NewSymbolVisibility) { - const uint8_t Invalid = 0xff; - ELFConfig.NewSymbolVisibility = - StringSwitch<uint8_t>(*Config.NewSymbolVisibility) - .Case("default", ELF::STV_DEFAULT) - .Case("hidden", ELF::STV_HIDDEN) - .Case("internal", ELF::STV_INTERNAL) - .Case("protected", ELF::STV_PROTECTED) - .Default(Invalid); - - if (ELFConfig.NewSymbolVisibility == Invalid) - return createStringError(errc::invalid_argument, - "'%s' is not a valid symbol visibility", - Config.NewSymbolVisibility->str().c_str()); - } - - for (StringRef Arg : Config.SymbolsToAdd) { - Expected<elf::NewSymbolInfo> NSI = parseNewSymbolInfo( - Arg, - ELFConfig.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT)); - if (!NSI) - return NSI.takeError(); - ELFConfig.SymbolsToAdd.push_back(*NSI); - } - - return ELFConfig; -} - -} // end namespace elf -} // end namespace objcopy -} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/ELF/ELFConfig.h b/llvm/tools/llvm-objcopy/ELF/ELFConfig.h index 977efbc4166f..42d407da17ff 100644 --- a/llvm/tools/llvm-objcopy/ELF/ELFConfig.h +++ b/llvm/tools/llvm-objcopy/ELF/ELFConfig.h @@ -6,39 +6,23 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_OBJCOPY_ELFCONFIG_H -#define LLVM_TOOLS_OBJCOPY_ELFCONFIG_H +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/ELFTypes.h" -#include "llvm/Support/Error.h" #include <vector> namespace llvm { namespace objcopy { -struct CopyConfig; -namespace elf { - -struct NewSymbolInfo { - StringRef SymbolName; - StringRef SectionName; - uint64_t Value = 0; - uint8_t Type = ELF::STT_NOTYPE; - uint8_t Bind = ELF::STB_GLOBAL; - uint8_t Visibility = ELF::STV_DEFAULT; -}; - -struct ELFCopyConfig { - Optional<uint8_t> NewSymbolVisibility; - std::vector<NewSymbolInfo> SymbolsToAdd; +// ELF specific configuration for copying/stripping a single file. +struct ELFConfig { + uint8_t NewSymbolVisibility = (uint8_t)ELF::STV_DEFAULT; }; -Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config); - -} // namespace elf } // namespace objcopy } // namespace llvm -#endif +#endif // LLVM_TOOLS_LLVM_OBJCOPY_ELF_ELFCONFIG_H diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp index c53a34bc46a3..986eeca6256c 100644 --- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -7,9 +7,10 @@ //===----------------------------------------------------------------------===// #include "ELFObjcopy.h" -#include "Buffer.h" -#include "CopyConfig.h" +#include "CommonConfig.h" +#include "ELFConfig.h" #include "Object.h" +#include "llvm-objcopy.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" @@ -44,12 +45,12 @@ #include <system_error> #include <utility> -namespace llvm { -namespace objcopy { -namespace elf { +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::objcopy; +using namespace llvm::objcopy::elf; +using namespace llvm::object; -using namespace object; -using namespace ELF; using SectionPred = std::function<bool(const SectionBase &Sec)>; static bool isDebugSection(const SectionBase &Sec) { @@ -70,7 +71,7 @@ static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { return !isDWOSection(Sec); } -uint64_t getNewShfFlags(SectionFlag AllFlags) { +static uint64_t getNewShfFlags(SectionFlag AllFlags) { uint64_t NewFlags = 0; if (AllFlags & SectionFlag::SecAlloc) NewFlags |= ELF::SHF_ALLOC; @@ -132,77 +133,40 @@ static ElfType getOutputElfType(const MachineInfo &MI) { return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; } -static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, +static std::unique_ptr<Writer> createELFWriter(const CommonConfig &Config, + Object &Obj, raw_ostream &Out, ElfType OutputElfType) { // Depending on the initial ELFT and OutputFormat we need a different Writer. switch (OutputElfType) { case ELFT_ELF32LE: - return std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, !Config.StripSections, + return std::make_unique<ELFWriter<ELF32LE>>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); case ELFT_ELF64LE: - return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, !Config.StripSections, + return std::make_unique<ELFWriter<ELF64LE>>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); case ELFT_ELF32BE: - return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, !Config.StripSections, + return std::make_unique<ELFWriter<ELF32BE>>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); case ELFT_ELF64BE: - return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, !Config.StripSections, + return std::make_unique<ELFWriter<ELF64BE>>(Obj, Out, !Config.StripSections, Config.OnlyKeepDebug); } llvm_unreachable("Invalid output format"); } -static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, +static std::unique_ptr<Writer> createWriter(const CommonConfig &Config, + Object &Obj, raw_ostream &Out, ElfType OutputElfType) { switch (Config.OutputFormat) { case FileFormat::Binary: - return std::make_unique<BinaryWriter>(Obj, Buf); + return std::make_unique<BinaryWriter>(Obj, Out); case FileFormat::IHex: - return std::make_unique<IHexWriter>(Obj, Buf); + return std::make_unique<IHexWriter>(Obj, Out); default: - return createELFWriter(Config, Obj, Buf, OutputElfType); + return createELFWriter(Config, Obj, Out, OutputElfType); } } -template <class ELFT> -static Expected<ArrayRef<uint8_t>> -findBuildID(const CopyConfig &Config, const object::ELFFile<ELFT> &In) { - auto PhdrsOrErr = In.program_headers(); - if (auto Err = PhdrsOrErr.takeError()) - return createFileError(Config.InputFilename, std::move(Err)); - - for (const auto &Phdr : *PhdrsOrErr) { - if (Phdr.p_type != PT_NOTE) - continue; - Error Err = Error::success(); - for (auto Note : In.notes(Phdr, Err)) - if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU) - return Note.getDesc(); - if (Err) - return createFileError(Config.InputFilename, std::move(Err)); - } - - return createFileError(Config.InputFilename, - createStringError(llvm::errc::invalid_argument, - "could not find build ID")); -} - -static Expected<ArrayRef<uint8_t>> -findBuildID(const CopyConfig &Config, const object::ELFObjectFileBase &In) { - if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(&In)) - return findBuildID(Config, O->getELFFile()); - else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(&In)) - return findBuildID(Config, O->getELFFile()); - else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(&In)) - return findBuildID(Config, O->getELFFile()); - else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(&In)) - return findBuildID(Config, O->getELFFile()); - - llvm_unreachable("Bad file format"); -} - template <class... Ts> static Error makeStringError(std::error_code EC, const Twine &Msg, Ts &&... Args) { @@ -210,83 +174,6 @@ static Error makeStringError(std::error_code EC, const Twine &Msg, return createStringError(EC, FullMsg.c_str(), std::forward<Ts>(Args)...); } -#define MODEL_8 "%%%%%%%%" -#define MODEL_16 MODEL_8 MODEL_8 -#define MODEL_32 (MODEL_16 MODEL_16) - -static Error linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink, - StringRef Suffix, - ArrayRef<uint8_t> BuildIdBytes) { - SmallString<128> Path = Config.BuildIdLinkDir; - sys::path::append(Path, llvm::toHex(BuildIdBytes[0], /*LowerCase*/ true)); - if (auto EC = sys::fs::create_directories(Path)) - return createFileError( - Path.str(), - makeStringError(EC, "cannot create build ID link directory")); - - sys::path::append(Path, - llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true)); - Path += Suffix; - SmallString<128> TmpPath; - // create_hard_link races so we need to link to a temporary path but - // we want to make sure that we choose a filename that does not exist. - // By using 32 model characters we get 128-bits of entropy. It is - // unlikely that this string has ever existed before much less exists - // on this disk or in the current working directory. - // Additionally we prepend the original Path for debugging but also - // because it ensures that we're linking within a directory on the same - // partition on the same device which is critical. It has the added - // win of yet further decreasing the odds of a conflict. - sys::fs::createUniquePath(Twine(Path) + "-" + MODEL_32 + ".tmp", TmpPath, - /*MakeAbsolute*/ false); - if (auto EC = sys::fs::create_hard_link(ToLink, TmpPath)) { - Path.push_back('\0'); - return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), - Path.data()); - } - // We then atomically rename the link into place which will just move the - // link. If rename fails something is more seriously wrong so just return - // an error. - if (auto EC = sys::fs::rename(TmpPath, Path)) { - Path.push_back('\0'); - return makeStringError(EC, "cannot link '%s' to '%s'", ToLink.data(), - Path.data()); - } - // If `Path` was already a hard-link to the same underlying file then the - // temp file will be left so we need to remove it. Remove will not cause - // an error by default if the file is already gone so just blindly remove - // it rather than checking. - if (auto EC = sys::fs::remove(TmpPath)) { - TmpPath.push_back('\0'); - return makeStringError(EC, "could not remove '%s'", TmpPath.data()); - } - return Error::success(); -} - -static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader, - StringRef File, ElfType OutputElfType) { - Expected<std::unique_ptr<Object>> DWOFile = Reader.create(false); - if (!DWOFile) - return DWOFile.takeError(); - - auto OnlyKeepDWOPred = [&DWOFile](const SectionBase &Sec) { - return onlyKeepDWOPred(**DWOFile, Sec); - }; - if (Error E = - (*DWOFile)->removeSections(Config.AllowBrokenLinks, OnlyKeepDWOPred)) - return E; - if (Config.OutputArch) { - (*DWOFile)->Machine = Config.OutputArch.getValue().EMachine; - (*DWOFile)->OSABI = Config.OutputArch.getValue().OSABI; - } - FileBuffer FB(File); - std::unique_ptr<Writer> Writer = - createWriter(Config, **DWOFile, FB, OutputElfType); - if (Error E = Writer->finalize()) - return E; - return Writer->write(); -} - static Error dumpSectionToFile(StringRef SecName, StringRef Filename, Object &Obj) { for (auto &Sec : Obj.sections()) { @@ -357,7 +244,7 @@ static bool isUnneededSymbol(const Symbol &Sym) { Sym.Type != STT_SECTION; } -static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { +static Error updateAndRemoveSymbols(const CommonConfig &Config, Object &Obj) { // TODO: update or remove symbols only if there is an option that affects // them. if (!Obj.SymbolTable) @@ -452,7 +339,7 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { return Obj.removeSymbols(RemoveSymbolsPred); } -static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { +static Error replaceAndRemoveSections(const CommonConfig &Config, Object &Obj) { SectionPred RemovePred = [](const SectionBase &) { return false; }; // Removes: @@ -462,7 +349,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { }; } - if (Config.StripDWO || !Config.SplitDWO.empty()) + if (Config.StripDWO) RemovePred = [RemovePred](const SectionBase &Sec) { return isDWOSection(Sec) || RemovePred(Sec); }; @@ -613,6 +500,60 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { return Obj.removeSections(Config.AllowBrokenLinks, RemovePred); } +// Add symbol to the Object symbol table with the specified properties. +static void addSymbol(Object &Obj, const NewSymbolInfo &SymInfo, + uint8_t DefaultVisibility) { + SectionBase *Sec = Obj.findSection(SymInfo.SectionName); + uint64_t Value = Sec ? Sec->Addr + SymInfo.Value : SymInfo.Value; + + uint8_t Bind = ELF::STB_GLOBAL; + uint8_t Type = ELF::STT_NOTYPE; + uint8_t Visibility = DefaultVisibility; + + for (SymbolFlag FlagValue : SymInfo.Flags) + switch (FlagValue) { + case SymbolFlag::Global: + Bind = ELF::STB_GLOBAL; + break; + case SymbolFlag::Local: + Bind = ELF::STB_LOCAL; + break; + case SymbolFlag::Weak: + Bind = ELF::STB_WEAK; + break; + case SymbolFlag::Default: + Visibility = ELF::STV_DEFAULT; + break; + case SymbolFlag::Hidden: + Visibility = ELF::STV_HIDDEN; + break; + case SymbolFlag::Protected: + Visibility = ELF::STV_PROTECTED; + break; + case SymbolFlag::File: + Type = ELF::STT_FILE; + break; + case SymbolFlag::Section: + Type = ELF::STT_SECTION; + break; + case SymbolFlag::Object: + Type = ELF::STT_OBJECT; + break; + case SymbolFlag::Function: + Type = ELF::STT_FUNC; + break; + case SymbolFlag::IndirectFunction: + Type = ELF::STT_GNU_IFUNC; + break; + default: /* Other flag values are ignored for ELF. */ + break; + }; + + Obj.SymbolTable->addSymbol( + SymInfo.SymbolName, Bind, Type, Sec, Value, Visibility, + Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0); +} + // This function handles the high level operations of GNU objcopy including // handling command line options. It's important to outline certain properties // we expect to hold of the command line operations. Any operation that "keeps" @@ -620,21 +561,19 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { // any previous removals. Lastly whether or not something is removed shouldn't // depend a) on the order the options occur in or b) on some opaque priority // system. The only priority is that keeps/copies overrule removes. -static Error handleArgs(const CopyConfig &Config, Object &Obj, - const Reader &Reader, ElfType OutputElfType) { - if (Config.StripSwiftSymbols) - return createStringError(llvm::errc::invalid_argument, - "option not supported by llvm-objcopy for ELF"); - if (!Config.SplitDWO.empty()) - if (Error E = - splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType)) - return E; - +static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig, + Object &Obj) { if (Config.OutputArch) { Obj.Machine = Config.OutputArch.getValue().EMachine; Obj.OSABI = Config.OutputArch.getValue().OSABI; } + if (!Config.SplitDWO.empty() && Config.ExtractDWO) { + return Obj.removeSections( + Config.AllowBrokenLinks, + [&Obj](const SectionBase &Sec) { return onlyKeepDWOPred(Obj, Sec); }); + } + // Dump sections before add/remove for compatibility with GNU objcopy. for (StringRef Flag : Config.DumpSection) { StringRef SectionName; @@ -748,17 +687,12 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj, // If the symbol table was previously removed, we need to create a new one // before adding new symbols. - if (!Obj.SymbolTable && !Config.ELF->SymbolsToAdd.empty()) + if (!Obj.SymbolTable && !Config.SymbolsToAdd.empty()) if (Error E = Obj.addNewSymbolTable()) return E; - for (const NewSymbolInfo &SI : Config.ELF->SymbolsToAdd) { - SectionBase *Sec = Obj.findSection(SI.SectionName); - uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value; - Obj.SymbolTable->addSymbol( - SI.SymbolName, SI.Bind, SI.Type, Sec, Value, SI.Visibility, - Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0); - } + for (const NewSymbolInfo &SI : Config.SymbolsToAdd) + addSymbol(Obj, SI, ELFConfig.NewSymbolVisibility); // --set-section-flags works with sections added by --add-section. if (!Config.SetSectionFlags.empty()) { @@ -776,8 +710,8 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj, return Error::success(); } -static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out, - ElfType OutputElfType) { +static Error writeOutput(const CommonConfig &Config, Object &Obj, + raw_ostream &Out, ElfType OutputElfType) { std::unique_ptr<Writer> Writer = createWriter(Config, Obj, Out, OutputElfType); if (Error E = Writer->finalize()) @@ -785,8 +719,9 @@ static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out, return Writer->write(); } -Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { +Error objcopy::elf::executeObjcopyOnIHex(const CommonConfig &Config, + const ELFConfig &ELFConfig, + MemoryBuffer &In, raw_ostream &Out) { IHexReader Reader(&In); Expected<std::unique_ptr<Object>> Obj = Reader.create(true); if (!Obj) @@ -794,16 +729,16 @@ Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, const ElfType OutputElfType = getOutputElfType(Config.OutputArch.getValueOr(MachineInfo())); - if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType)) + if (Error E = handleArgs(Config, ELFConfig, **Obj)) return E; return writeOutput(Config, **Obj, Out, OutputElfType); } -Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { - uint8_t NewSymbolVisibility = - Config.ELF->NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT); - BinaryReader Reader(&In, NewSymbolVisibility); +Error objcopy::elf::executeObjcopyOnRawBinary(const CommonConfig &Config, + const ELFConfig &ELFConfig, + MemoryBuffer &In, + raw_ostream &Out) { + BinaryReader Reader(&In, ELFConfig.NewSymbolVisibility); Expected<std::unique_ptr<Object>> Obj = Reader.create(true); if (!Obj) return Obj.takeError(); @@ -812,13 +747,15 @@ Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, // (-B<arch>). const ElfType OutputElfType = getOutputElfType(Config.OutputArch.getValueOr(MachineInfo())); - if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType)) + if (Error E = handleArgs(Config, ELFConfig, **Obj)) return E; return writeOutput(Config, **Obj, Out, OutputElfType); } -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out) { +Error objcopy::elf::executeObjcopyOnBinary(const CommonConfig &Config, + const ELFConfig &ELFConfig, + object::ELFObjectFileBase &In, + raw_ostream &Out) { ELFReader Reader(&In, Config.ExtractPartition); Expected<std::unique_ptr<Object>> Obj = Reader.create(!Config.SymbolsToAdd.empty()); @@ -828,41 +765,12 @@ Error executeObjcopyOnBinary(const CopyConfig &Config, const ElfType OutputElfType = Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue()) : getOutputElfType(In); - ArrayRef<uint8_t> BuildIdBytes; - if (!Config.BuildIdLinkDir.empty()) { - auto BuildIdBytesOrErr = findBuildID(Config, In); - if (auto E = BuildIdBytesOrErr.takeError()) - return E; - BuildIdBytes = *BuildIdBytesOrErr; - - if (BuildIdBytes.size() < 2) - return createFileError( - Config.InputFilename, - createStringError(object_error::parse_failed, - "build ID is smaller than two bytes")); - } - - if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) - if (Error E = - linkToBuildIdDir(Config, Config.InputFilename, - Config.BuildIdLinkInput.getValue(), BuildIdBytes)) - return E; - - if (Error E = handleArgs(Config, **Obj, Reader, OutputElfType)) + if (Error E = handleArgs(Config, ELFConfig, **Obj)) return createFileError(Config.InputFilename, std::move(E)); if (Error E = writeOutput(Config, **Obj, Out, OutputElfType)) return createFileError(Config.InputFilename, std::move(E)); - if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) - if (Error E = - linkToBuildIdDir(Config, Config.OutputFilename, - Config.BuildIdLinkOutput.getValue(), BuildIdBytes)) - return createFileError(Config.OutputFilename, std::move(E)); return Error::success(); } - -} // end namespace elf -} // end namespace objcopy -} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h index e13e237e29c4..852661e68f37 100644 --- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h @@ -12,22 +12,26 @@ namespace llvm { class Error; class MemoryBuffer; +class raw_ostream; namespace object { class ELFObjectFileBase; } // end namespace object namespace objcopy { -struct CopyConfig; -class Buffer; +struct CommonConfig; +struct ELFConfig; namespace elf { -Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out); -Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out); -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out); +Error executeObjcopyOnIHex(const CommonConfig &Config, + const ELFConfig &ELFConfig, MemoryBuffer &In, + raw_ostream &Out); +Error executeObjcopyOnRawBinary(const CommonConfig &Config, + const ELFConfig &ELFConfig, MemoryBuffer &In, + raw_ostream &Out); +Error executeObjcopyOnBinary(const CommonConfig &Config, + const ELFConfig &ELFConfig, + object::ELFObjectFileBase &In, raw_ostream &Out); } // end namespace elf } // end namespace objcopy diff --git a/llvm/tools/llvm-objcopy/ELF/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp index 0ff82f951b62..ba91d08e5540 100644 --- a/llvm/tools/llvm-objcopy/ELF/Object.cpp +++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp @@ -29,16 +29,14 @@ #include <utility> #include <vector> -namespace llvm { -namespace objcopy { -namespace elf { - -using namespace object; -using namespace ELF; +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::objcopy::elf; +using namespace llvm::object; template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) { - uint8_t *B = Buf.getBufferStart() + Obj.ProgramHdrSegment.Offset + - Seg.Index * sizeof(Elf_Phdr); + uint8_t *B = reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + + Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr); Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B); Phdr.p_type = Seg.Type; Phdr.p_flags = Seg.Flags; @@ -67,7 +65,8 @@ void SectionBase::replaceSectionReferences( void SectionBase::onRemove() {} template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) { - uint8_t *B = Buf.getBufferStart() + Sec.HeaderOffset; + uint8_t *B = + reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Sec.HeaderOffset; Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B); Shdr.sh_name = Sec.NameIndex; Shdr.sh_type = Sec.Type; @@ -191,7 +190,7 @@ template <class T> static T checkedGetHex(StringRef S) { // Fills exactly Len bytes of buffer with hexadecimal characters // representing value 'X' template <class T, class Iterator> -static Iterator utohexstr(T X, Iterator It, size_t Len) { +static Iterator toHexStr(T X, Iterator It, size_t Len) { // Fill range with '0' std::fill(It, It + Len, '0'); @@ -220,13 +219,13 @@ IHexLineData IHexRecord::getLine(uint8_t Type, uint16_t Addr, assert(Line.size()); auto Iter = Line.begin(); *Iter++ = ':'; - Iter = utohexstr(Data.size(), Iter, 2); - Iter = utohexstr(Addr, Iter, 4); - Iter = utohexstr(Type, Iter, 2); + Iter = toHexStr(Data.size(), Iter, 2); + Iter = toHexStr(Addr, Iter, 4); + Iter = toHexStr(Type, Iter, 2); for (uint8_t X : Data) - Iter = utohexstr(X, Iter, 2); + Iter = toHexStr(X, Iter, 2); StringRef S(Line.data() + 1, std::distance(Line.begin() + 1, Iter)); - Iter = utohexstr(getChecksum(S), Iter, 2); + Iter = toHexStr(getChecksum(S), Iter, 2); *Iter++ = '\r'; *Iter++ = '\n'; assert(Iter == Line.end()); @@ -474,7 +473,7 @@ Error ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) { return createStringError(errc::invalid_argument, "'" + Sec.Name + "': " + toString(std::move(Err))); - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset; std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); return Error::success(); @@ -519,7 +518,7 @@ Error BinarySectionWriter::visit(const CompressedSection &Sec) { template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset; if (Sec.CompressionType == DebugCompressionType::None) { std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); return Error::success(); @@ -624,7 +623,8 @@ void StringTableSection::prepareForLayout() { } Error SectionWriter::visit(const StringTableSection &Sec) { - Sec.StrTabBuilder.write(Out.getBufferStart() + Sec.Offset); + Sec.StrTabBuilder.write(reinterpret_cast<uint8_t *>(Out.getBufferStart()) + + Sec.Offset); return Error::success(); } @@ -638,7 +638,7 @@ Error StringTableSection::accept(MutableSectionVisitor &Visitor) { template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset; llvm::copy(Sec.Indexes, reinterpret_cast<Elf_Word *>(Buf)); return Error::success(); } @@ -979,7 +979,7 @@ static void writeRel(const RelRange &Relocations, T *Buf) { template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const RelocationSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + uint8_t *Buf = reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset; if (Sec.Type == SHT_REL) writeRel(Sec.Relocations, reinterpret_cast<Elf_Rel *>(Buf)); else @@ -1069,6 +1069,12 @@ Error Section::removeSectionReferences( void GroupSection::finalize() { this->Info = Sym ? Sym->Index : 0; this->Link = SymTab ? SymTab->Index : 0; + // Linker deduplication for GRP_COMDAT is based on Sym->Name. The local/global + // status is not part of the equation. If Sym is localized, the intention is + // likely to make the group fully localized. Drop GRP_COMDAT to suppress + // deduplication. See https://groups.google.com/g/generic-abi/c/2X6mR-s2zoc + if ((FlagWord & GRP_COMDAT) && Sym && Sym->Binding == STB_LOCAL) + this->FlagWord &= ~GRP_COMDAT; } Error GroupSection::removeSectionReferences( @@ -1160,7 +1166,8 @@ GnuDebugLinkSection::GnuDebugLinkSection(StringRef File, template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) { - unsigned char *Buf = Out.getBufferStart() + Sec.Offset; + unsigned char *Buf = + reinterpret_cast<uint8_t *>(Out.getBufferStart()) + Sec.Offset; Elf_Word *CRC = reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word)); *CRC = Sec.CRC32; @@ -1180,7 +1187,7 @@ template <class ELFT> Error ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) { ELF::Elf32_Word *Buf = reinterpret_cast<ELF::Elf32_Word *>(Out.getBufferStart() + Sec.Offset); - *Buf++ = Sec.FlagWord; + support::endian::write32<ELFT::TargetEndianness>(Buf++, Sec.FlagWord); for (SectionBase *S : Sec.GroupMembers) support::endian::write32<ELFT::TargetEndianness>(Buf++, S->Index); return Error::success(); @@ -1202,6 +1209,10 @@ static bool sectionWithinSegment(const SectionBase &Sec, const Segment &Seg) { // not the first. uint64_t SecSize = Sec.Size ? Sec.Size : 1; + // Ignore just added sections. + if (Sec.OriginalOffset == std::numeric_limits<uint64_t>::max()) + return false; + if (Sec.Type == SHT_NOBITS) { if (!(Sec.Flags & SHF_ALLOC)) return false; @@ -1290,8 +1301,9 @@ void BinaryELFBuilder::addData(SymbolTableSection *SymTab) { DataSection.Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE; std::string SanitizedFilename = MemBuf->getBufferIdentifier().str(); - std::replace_if(std::begin(SanitizedFilename), std::end(SanitizedFilename), - [](char C) { return !isalnum(C); }, '_'); + std::replace_if( + std::begin(SanitizedFilename), std::end(SanitizedFilename), + [](char C) { return !isAlnum(C); }, '_'); Twine Prefix = Twine("_binary_") + SanitizedFilename; SymTab->addSymbol(Prefix + "_start", STB_GLOBAL, STT_NOTYPE, &DataSection, @@ -1505,7 +1517,8 @@ Error ELFBuilder<ELFT>::initGroupSection(GroupSection *GroupSec) { reinterpret_cast<const ELF::Elf32_Word *>(GroupSec->Contents.data()); const ELF::Elf32_Word *End = Word + GroupSec->Contents.size() / sizeof(ELF::Elf32_Word); - GroupSec->setFlagWord(*Word++); + GroupSec->setFlagWord( + support::endian::read32<ELFT::TargetEndianness>(Word++)); for (; Word != End; ++Word) { uint32_t Index = support::endian::read32<ELFT::TargetEndianness>(Word); Expected<SectionBase *> Sec = SecTable.getSection( @@ -1778,7 +1791,7 @@ template <class ELFT> Error ELFBuilder<ELFT>::readSectionHeaders() { Sec->OriginalIndex = Sec->Index; Sec->OriginalData = ArrayRef<uint8_t>(ElfFile.base() + Shdr.sh_offset, - (Shdr.sh_type == SHT_NOBITS) ? 0 : Shdr.sh_size); + (Shdr.sh_type == SHT_NOBITS) ? (size_t)0 : Shdr.sh_size); } return Error::success(); @@ -1971,7 +1984,7 @@ Expected<std::unique_ptr<Object>> ELFReader::create(bool EnsureSymtab) const { } template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { - Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf.getBufferStart()); + Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf->getBufferStart()); std::fill(Ehdr.e_ident, Ehdr.e_ident + 16, 0); Ehdr.e_ident[EI_MAG0] = 0x7f; Ehdr.e_ident[EI_MAG1] = 'E'; @@ -2036,7 +2049,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeShdrs() { // This reference serves to write the dummy section header at the begining // of the file. It is not used for anything else Elf_Shdr &Shdr = - *reinterpret_cast<Elf_Shdr *>(Buf.getBufferStart() + Obj.SHOff); + *reinterpret_cast<Elf_Shdr *>(Buf->getBufferStart() + Obj.SHOff); Shdr.sh_name = 0; Shdr.sh_type = SHT_NULL; Shdr.sh_flags = 0; @@ -2076,7 +2089,7 @@ template <class ELFT> Error ELFWriter<ELFT>::writeSectionData() { template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() { for (Segment &Seg : Obj.segments()) { size_t Size = std::min<size_t>(Seg.FileSize, Seg.getContents().size()); - std::memcpy(Buf.getBufferStart() + Seg.Offset, Seg.getContents().data(), + std::memcpy(Buf->getBufferStart() + Seg.Offset, Seg.getContents().data(), Size); } @@ -2087,12 +2100,12 @@ template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() { continue; uint64_t Offset = Sec.OriginalOffset - Parent->OriginalOffset + Parent->Offset; - std::memset(Buf.getBufferStart() + Offset, 0, Sec.Size); + std::memset(Buf->getBufferStart() + Offset, 0, Sec.Size); } } template <class ELFT> -ELFWriter<ELFT>::ELFWriter(Object &Obj, Buffer &Buf, bool WSH, +ELFWriter<ELFT>::ELFWriter(Object &Obj, raw_ostream &Buf, bool WSH, bool OnlyKeepDebug) : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs), OnlyKeepDebug(OnlyKeepDebug) {} @@ -2311,18 +2324,19 @@ static uint64_t layoutSegmentsForOnlyKeepDebug(std::vector<Segment *> &Segments, uint64_t HdrEnd) { uint64_t MaxOffset = 0; for (Segment *Seg : Segments) { - // An empty segment contains no section (see sectionWithinSegment). If it - // has a parent segment, copy the parent segment's offset field. This works - // for empty PT_TLS. We don't handle empty segments without a parent for - // now. - if (Seg->ParentSegment != nullptr && Seg->MemSize == 0) - Seg->Offset = Seg->ParentSegment->Offset; - - const SectionBase *FirstSec = Seg->firstSection(); - if (Seg->Type == PT_PHDR || !FirstSec) + if (Seg->Type == PT_PHDR) continue; - uint64_t Offset = FirstSec->Offset; + // The segment offset is generally the offset of the first section. + // + // For a segment containing no section (see sectionWithinSegment), if it has + // a parent segment, copy the parent segment's offset field. This works for + // empty PT_TLS. If no parent segment, use 0: the segment is not useful for + // debugging anyway. + const SectionBase *FirstSec = Seg->firstSection(); + uint64_t Offset = + FirstSec ? FirstSec->Offset + : (Seg->ParentSegment ? Seg->ParentSegment->Offset : 0); uint64_t FileSize = 0; for (const SectionBase *Sec : Seg->Sections) { uint64_t Size = Sec->Type == SHT_NOBITS ? 0 : Sec->Size; @@ -2409,7 +2423,11 @@ template <class ELFT> Error ELFWriter<ELFT>::write() { return E; if (WriteSectionHeaders) writeShdrs(); - return Buf.commit(); + + // TODO: Implement direct writing to the output stream (without intermediate + // memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + return Error::success(); } static Error removeUnneededSections(Object &Obj) { @@ -2450,8 +2468,10 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() { bool NeedsLargeIndexes = false; if (Obj.sections().size() >= SHN_LORESERVE) { SectionTableRef Sections = Obj.sections(); + // Sections doesn't include the null section header, so account for this + // when skipping the first N sections. NeedsLargeIndexes = - any_of(drop_begin(Sections, SHN_LORESERVE), + any_of(drop_begin(Sections, SHN_LORESERVE - 1), [](const SectionBase &Sec) { return Sec.HasSymbol; }); // TODO: handle case where only one section needs the large index table but // only needs it because the large index table hasn't been removed yet. @@ -2529,9 +2549,14 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() { Sec.finalize(); } - if (Error E = Buf.allocate(totalSize())) - return E; - SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(Buf); + size_t TotalSize = totalSize(); + Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(errc::not_enough_memory, + "failed to allocate memory buffer of " + + Twine::utohexstr(TotalSize) + " bytes"); + + SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(*Buf); return Error::success(); } @@ -2540,7 +2565,10 @@ Error BinaryWriter::write() { if (Error Err = Sec.accept(*SecWriter)) return Err; - return Buf.commit(); + // TODO: Implement direct writing to the output stream (without intermediate + // memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + return Error::success(); } Error BinaryWriter::finalize() { @@ -2553,7 +2581,7 @@ Error BinaryWriter::finalize() { if (Sec.ParentSegment != nullptr) Sec.Addr = Sec.Offset - Sec.ParentSegment->Offset + Sec.ParentSegment->PAddr; - if (Sec.Size > 0) + if (Sec.Type != SHT_NOBITS && Sec.Size > 0) MinAddr = std::min(MinAddr, Sec.Addr); } @@ -2568,9 +2596,12 @@ Error BinaryWriter::finalize() { TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size); } - if (Error E = Buf.allocate(TotalSize)) - return E; - SecWriter = std::make_unique<BinarySectionWriter>(Buf); + Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(errc::not_enough_memory, + "failed to allocate memory buffer of " + + Twine::utohexstr(TotalSize) + " bytes"); + SecWriter = std::make_unique<BinarySectionWriter>(*Buf); return Error::success(); } @@ -2608,7 +2639,7 @@ uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) { } Error IHexWriter::write() { - IHexSectionWriter Writer(Buf); + IHexSectionWriter Writer(*Buf); // Write sections. for (const SectionBase *Sec : Sections) if (Error Err = Sec->accept(Writer)) @@ -2616,11 +2647,17 @@ Error IHexWriter::write() { uint64_t Offset = Writer.getBufferOffset(); // Write entry point address. - Offset += writeEntryPointRecord(Buf.getBufferStart() + Offset); + Offset += writeEntryPointRecord( + reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Offset); // Write EOF. - Offset += writeEndOfFileRecord(Buf.getBufferStart() + Offset); + Offset += writeEndOfFileRecord( + reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + Offset); assert(Offset == TotalSize); - return Buf.commit(); + + // TODO: Implement direct writing to the output stream (without intermediate + // memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + return Error::success(); } Error IHexWriter::checkSection(const SectionBase &Sec) { @@ -2634,37 +2671,27 @@ Error IHexWriter::checkSection(const SectionBase &Sec) { } Error IHexWriter::finalize() { - bool UseSegments = false; - auto ShouldWrite = [](const SectionBase &Sec) { - return (Sec.Flags & ELF::SHF_ALLOC) && (Sec.Type != ELF::SHT_NOBITS); - }; - auto IsInPtLoad = [](const SectionBase &Sec) { - return Sec.ParentSegment && Sec.ParentSegment->Type == ELF::PT_LOAD; - }; - // We can't write 64-bit addresses. if (addressOverflows32bit(Obj.Entry)) return createStringError(errc::invalid_argument, - "Entry point address 0x%llx overflows 32 bits.", + "Entry point address 0x%llx overflows 32 bits", Obj.Entry); - // If any section we're to write has segment then we - // switch to using physical addresses. Otherwise we - // use section virtual address. - for (const SectionBase &Sec : Obj.sections()) - if (ShouldWrite(Sec) && IsInPtLoad(Sec)) { - UseSegments = true; - break; - } - for (const SectionBase &Sec : Obj.sections()) - if (ShouldWrite(Sec) && (!UseSegments || IsInPtLoad(Sec))) { + if ((Sec.Flags & ELF::SHF_ALLOC) && Sec.Type != ELF::SHT_NOBITS && + Sec.Size > 0) { if (Error E = checkSection(Sec)) return E; Sections.insert(&Sec); } - IHexSectionWriterBase LengthCalc(Buf); + std::unique_ptr<WritableMemoryBuffer> EmptyBuffer = + WritableMemoryBuffer::getNewMemBuffer(0); + if (!EmptyBuffer) + return createStringError(errc::not_enough_memory, + "failed to allocate memory buffer of 0 bytes"); + + IHexSectionWriterBase LengthCalc(*EmptyBuffer); for (const SectionBase *Sec : Sections) if (Error Err = Sec->accept(LengthCalc)) return Err; @@ -2674,11 +2701,20 @@ Error IHexWriter::finalize() { TotalSize = LengthCalc.getBufferOffset() + (Obj.Entry ? IHexRecord::getLineLength(4) : 0) + IHexRecord::getLineLength(0); - if (Error E = Buf.allocate(TotalSize)) - return E; + + Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(errc::not_enough_memory, + "failed to allocate memory buffer of " + + Twine::utohexstr(TotalSize) + " bytes"); + return Error::success(); } +namespace llvm { +namespace objcopy { +namespace elf { + template class ELFBuilder<ELF64LE>; template class ELFBuilder<ELF64BE>; template class ELFBuilder<ELF32LE>; diff --git a/llvm/tools/llvm-objcopy/ELF/Object.h b/llvm/tools/llvm-objcopy/ELF/Object.h index 0205c2d4f398..6fd26afa3ca1 100644 --- a/llvm/tools/llvm-objcopy/ELF/Object.h +++ b/llvm/tools/llvm-objcopy/ELF/Object.h @@ -9,8 +9,7 @@ #ifndef LLVM_TOOLS_OBJCOPY_OBJECT_H #define LLVM_TOOLS_OBJCOPY_OBJECT_H -#include "Buffer.h" -#include "CopyConfig.h" +#include "CommonConfig.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -19,6 +18,7 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" #include <cstddef> #include <cstdint> #include <functional> @@ -106,7 +106,7 @@ public: class SectionWriter : public SectionVisitor { protected: - Buffer &Out; + WritableMemoryBuffer &Out; public: virtual ~SectionWriter() = default; @@ -123,7 +123,7 @@ public: virtual Error visit(const CompressedSection &Sec) override = 0; virtual Error visit(const DecompressedSection &Sec) override = 0; - explicit SectionWriter(Buffer &Buf) : Out(Buf) {} + explicit SectionWriter(WritableMemoryBuffer &Buf) : Out(Buf) {} }; template <class ELFT> class ELFSectionWriter : public SectionWriter { @@ -143,7 +143,7 @@ public: Error visit(const CompressedSection &Sec) override; Error visit(const DecompressedSection &Sec) override; - explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {} + explicit ELFSectionWriter(WritableMemoryBuffer &Buf) : SectionWriter(Buf) {} }; template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor { @@ -187,7 +187,8 @@ public: Error visit(const CompressedSection &Sec) override; Error visit(const DecompressedSection &Sec) override; - explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {} + explicit BinarySectionWriter(WritableMemoryBuffer &Buf) + : SectionWriter(Buf) {} }; using IHexLineData = SmallVector<char, 64>; @@ -283,7 +284,8 @@ protected: virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data); public: - explicit IHexSectionWriterBase(Buffer &Buf) : BinarySectionWriter(Buf) {} + explicit IHexSectionWriterBase(WritableMemoryBuffer &Buf) + : BinarySectionWriter(Buf) {} uint64_t getBufferOffset() const { return Offset; } Error visit(const Section &Sec) final; @@ -296,7 +298,7 @@ public: // Real IHEX section writer class IHexSectionWriter : public IHexSectionWriterBase { public: - IHexSectionWriter(Buffer &Buf) : IHexSectionWriterBase(Buf) {} + IHexSectionWriter(WritableMemoryBuffer &Buf) : IHexSectionWriterBase(Buf) {} void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data) override; Error visit(const StringTableSection &Sec) override; @@ -305,14 +307,15 @@ public: class Writer { protected: Object &Obj; - Buffer &Buf; + std::unique_ptr<WritableMemoryBuffer> Buf; + raw_ostream &Out; public: virtual ~Writer(); virtual Error finalize() = 0; virtual Error write() = 0; - Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {} + Writer(Object &O, raw_ostream &Out) : Obj(O), Out(Out) {} }; template <class ELFT> class ELFWriter : public Writer { @@ -349,7 +352,7 @@ public: Error finalize() override; Error write() override; - ELFWriter(Object &Obj, Buffer &Buf, bool WSH, bool OnlyKeepDebug); + ELFWriter(Object &Obj, raw_ostream &Out, bool WSH, bool OnlyKeepDebug); }; class BinaryWriter : public Writer { @@ -362,7 +365,7 @@ public: ~BinaryWriter() {} Error finalize() override; Error write() override; - BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} + BinaryWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {} }; class IHexWriter : public Writer { @@ -381,7 +384,7 @@ public: ~IHexWriter() {} Error finalize() override; Error write() override; - IHexWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} + IHexWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {} }; class SectionBase { diff --git a/llvm/tools/llvm-objcopy/MachO/MachOConfig.h b/llvm/tools/llvm-objcopy/MachO/MachOConfig.h new file mode 100644 index 000000000000..7c5dbfde19a0 --- /dev/null +++ b/llvm/tools/llvm-objcopy/MachO/MachOConfig.h @@ -0,0 +1,21 @@ +//===- MachOConfig.h --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H + +namespace llvm { +namespace objcopy { + +// Mach-O specific configuration for copying/stripping a single file. +struct MachOConfig {}; + +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_MACHO_MACHOCONFIG_H diff --git a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp index 8e2bf36238ec..6ed21806fe5e 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp @@ -11,9 +11,8 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" -namespace llvm { -namespace objcopy { -namespace macho { +using namespace llvm; +using namespace llvm::objcopy::macho; StringTableBuilder::Kind MachOLayoutBuilder::getStringTableBuilderKind(const Object &O, bool Is64Bit) { @@ -252,7 +251,10 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { uint64_t StartOfFunctionStarts = StartOfExportTrie + O.Exports.Trie.size(); uint64_t StartOfDataInCode = StartOfFunctionStarts + O.FunctionStarts.Data.size(); - uint64_t StartOfSymbols = StartOfDataInCode + O.DataInCode.Data.size(); + uint64_t StartOfLinkerOptimizationHint = + StartOfDataInCode + O.DataInCode.Data.size(); + uint64_t StartOfSymbols = + StartOfLinkerOptimizationHint + O.LinkerOptimizationHint.Data.size(); uint64_t StartOfIndirectSymbols = StartOfSymbols + NListSize * O.SymTable.Symbols.size(); uint64_t StartOfSymbolStrings = @@ -321,6 +323,11 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { MLC.linkedit_data_command_data.dataoff = StartOfDataInCode; MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size(); break; + case MachO::LC_LINKER_OPTIMIZATION_HINT: + MLC.linkedit_data_command_data.dataoff = StartOfLinkerOptimizationHint; + MLC.linkedit_data_command_data.datasize = + O.LinkerOptimizationHint.Data.size(); + break; case MachO::LC_FUNCTION_STARTS: MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts; MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size(); @@ -371,6 +378,8 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { case MachO::LC_LOAD_WEAK_DYLIB: case MachO::LC_UUID: case MachO::LC_SOURCE_VERSION: + case MachO::LC_THREAD: + case MachO::LC_UNIXTHREAD: // Nothing to update. break; default: @@ -392,7 +401,3 @@ Error MachOLayoutBuilder::layout() { Offset = layoutRelocations(Offset); return layoutTail(Offset); } - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp index fef4a0ae5594..823306916bbe 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -7,22 +7,25 @@ //===----------------------------------------------------------------------===// #include "MachOObjcopy.h" -#include "../CopyConfig.h" #include "../llvm-objcopy.h" +#include "CommonConfig.h" #include "MachOReader.h" #include "MachOWriter.h" +#include "MultiFormatConfig.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/MachOUniversalWriter.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/SmallVectorMemoryBuffer.h" -namespace llvm { -namespace objcopy { -namespace macho { +using namespace llvm; +using namespace llvm::objcopy; +using namespace llvm::objcopy::macho; +using namespace llvm::object; -using namespace object; using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>; using LoadCommandPred = std::function<bool(const LoadCommand &LC)>; @@ -46,7 +49,7 @@ static StringRef getPayloadString(const LoadCommand &LC) { .rtrim('\0'); } -static Error removeSections(const CopyConfig &Config, Object &Obj) { +static Error removeSections(const CommonConfig &Config, Object &Obj) { SectionPred RemovePred = [](const std::unique_ptr<Section> &) { return false; }; @@ -77,14 +80,14 @@ static Error removeSections(const CopyConfig &Config, Object &Obj) { return Obj.removeSections(RemovePred); } -static void markSymbols(const CopyConfig &Config, Object &Obj) { +static void markSymbols(const CommonConfig &, Object &Obj) { // Symbols referenced from the indirect symbol table must not be removed. for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols) if (ISE.Symbol) (*ISE.Symbol)->Referenced = true; } -static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { +static void updateAndRemoveSymbols(const CommonConfig &Config, Object &Obj) { for (SymbolEntry &Sym : Obj.SymTable) { auto I = Config.SymbolsToRename.find(Sym.Name); if (I != Config.SymbolsToRename.end()) @@ -94,6 +97,10 @@ static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { auto RemovePred = [Config, &Obj](const std::unique_ptr<SymbolEntry> &N) { if (N->Referenced) return false; + if (Config.KeepUndefined && N->isUndefinedSymbol()) + return false; + if (N->n_desc & MachO::REFERENCED_DYNAMICALLY) + return false; if (Config.StripAll) return true; if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT)) @@ -132,7 +139,7 @@ static LoadCommand buildRPathLoadCommand(StringRef Path) { return LC; } -static Error processLoadCommands(const CopyConfig &Config, Object &Obj) { +static Error processLoadCommands(const CommonConfig &Config, Object &Obj) { // Remove RPaths. DenseSet<StringRef> RPathsToRemove(Config.RPathsToRemove.begin(), Config.RPathsToRemove.end()); @@ -326,26 +333,7 @@ static Error isValidMachOCannonicalName(StringRef Name) { return Error::success(); } -static Error handleArgs(const CopyConfig &Config, Object &Obj) { - if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || - Config.BuildIdLinkInput || Config.BuildIdLinkOutput || - !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || - !Config.AllocSectionsPrefix.empty() || !Config.KeepSection.empty() || - Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() || - !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || - !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || - !Config.SectionsToRename.empty() || - !Config.UnneededSymbolsToRemove.empty() || - !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || - Config.ExtractDWO || Config.LocalizeHidden || Config.PreserveDates || - Config.StripAllGNU || Config.StripDWO || Config.StripNonAlloc || - Config.StripSections || Config.Weaken || Config.DecompressDebugSections || - Config.StripUnneeded || Config.DiscardMode == DiscardType::Locals || - !Config.SymbolsToAdd.empty() || Config.EntryExpr) { - return createStringError(llvm::errc::invalid_argument, - "option not supported by llvm-objcopy for MachO"); - } - +static Error handleArgs(const CommonConfig &Config, Object &Obj) { // Dump sections before add/remove for compatibility with GNU objcopy. for (StringRef Flag : Config.DumpSection) { StringRef SectionName; @@ -385,8 +373,10 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { return Error::success(); } -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::MachOObjectFile &In, Buffer &Out) { +Error objcopy::macho::executeObjcopyOnBinary(const CommonConfig &Config, + const MachOConfig &, + object::MachOObjectFile &In, + raw_ostream &Out) { MachOReader Reader(In); Expected<std::unique_ptr<Object>> O = Reader.create(); if (!O) @@ -414,9 +404,9 @@ Error executeObjcopyOnBinary(const CopyConfig &Config, return Writer.write(); } -Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config, - const MachOUniversalBinary &In, - Buffer &Out) { +Error objcopy::macho::executeObjcopyOnMachOUniversalBinary( + const MultiFormatConfig &Config, const MachOUniversalBinary &In, + raw_ostream &Out) { SmallVector<OwningBinary<Binary>, 2> Binaries; SmallVector<Slice, 2> Slices; for (const auto &O : In.objects()) { @@ -429,7 +419,7 @@ Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config, Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr = writeArchiveToBuffer(*NewArchiveMembersOrErr, (*ArOrErr)->hasSymbolTable(), (*ArOrErr)->kind(), - Config.DeterministicArchives, + Config.getCommonConfig().DeterministicArchives, (*ArOrErr)->isThin()); if (!OutputBufferOrErr) return OutputBufferOrErr.takeError(); @@ -453,36 +443,39 @@ Error executeObjcopyOnMachOUniversalBinary(CopyConfig &Config, Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile(); if (!ObjOrErr) { consumeError(ObjOrErr.takeError()); - return createStringError(std::errc::invalid_argument, - "slice for '%s' of the universal Mach-O binary " - "'%s' is not a Mach-O object or an archive", - O.getArchFlagName().c_str(), - Config.InputFilename.str().c_str()); + return createStringError( + std::errc::invalid_argument, + "slice for '%s' of the universal Mach-O binary " + "'%s' is not a Mach-O object or an archive", + O.getArchFlagName().c_str(), + Config.getCommonConfig().InputFilename.str().c_str()); } std::string ArchFlagName = O.getArchFlagName(); - MemBuffer MB(ArchFlagName); - if (Error E = executeObjcopyOnBinary(Config, **ObjOrErr, MB)) + + SmallVector<char, 0> Buffer; + raw_svector_ostream MemStream(Buffer); + + Expected<const MachOConfig &> MachO = Config.getMachOConfig(); + if (!MachO) + return MachO.takeError(); + + if (Error E = executeObjcopyOnBinary(Config.getCommonConfig(), *MachO, + **ObjOrErr, MemStream)) return E; - std::unique_ptr<WritableMemoryBuffer> OutputBuffer = - MB.releaseMemoryBuffer(); - Expected<std::unique_ptr<Binary>> BinaryOrErr = - object::createBinary(*OutputBuffer); + + std::unique_ptr<MemoryBuffer> MB = + std::make_unique<SmallVectorMemoryBuffer>(std::move(Buffer), + ArchFlagName); + Expected<std::unique_ptr<Binary>> BinaryOrErr = object::createBinary(*MB); if (!BinaryOrErr) return BinaryOrErr.takeError(); - Binaries.emplace_back(std::move(*BinaryOrErr), std::move(OutputBuffer)); + Binaries.emplace_back(std::move(*BinaryOrErr), std::move(MB)); Slices.emplace_back(*cast<MachOObjectFile>(Binaries.back().getBinary()), O.getAlign()); } - Expected<std::unique_ptr<MemoryBuffer>> B = - writeUniversalBinaryToBuffer(Slices); - if (!B) - return B.takeError(); - if (Error E = Out.allocate((*B)->getBufferSize())) - return E; - memcpy(Out.getBufferStart(), (*B)->getBufferStart(), (*B)->getBufferSize()); - return Out.commit(); -} -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm + if (Error Err = writeUniversalBinaryToStream(Slices, Out)) + return Err; + + return Error::success(); +} diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h index c3f5391f79b6..e30940a8d6eb 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h +++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h @@ -11,6 +11,7 @@ namespace llvm { class Error; +class raw_ostream; namespace object { class MachOObjectFile; @@ -18,15 +19,17 @@ class MachOUniversalBinary; } // end namespace object namespace objcopy { -struct CopyConfig; -class Buffer; +struct CommonConfig; +struct MachOConfig; +class MultiFormatConfig; namespace macho { -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::MachOObjectFile &In, Buffer &Out); +Error executeObjcopyOnBinary(const CommonConfig &Config, const MachOConfig &, + object::MachOObjectFile &In, raw_ostream &Out); Error executeObjcopyOnMachOUniversalBinary( - CopyConfig &Config, const object::MachOUniversalBinary &In, Buffer &Out); + const MultiFormatConfig &Config, const object::MachOUniversalBinary &In, + raw_ostream &Out); } // end namespace macho } // end namespace objcopy diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp index 548a85bd497e..7d1c29b42c2e 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp @@ -13,9 +13,9 @@ #include "llvm/Support/Errc.h" #include <memory> -namespace llvm { -namespace objcopy { -namespace macho { +using namespace llvm; +using namespace llvm::objcopy; +using namespace llvm::objcopy::macho; void MachOReader::readHeader(Object &O) const { O.Header.Magic = MachOObj.getHeader().magic; @@ -28,7 +28,7 @@ void MachOReader::readHeader(Object &O) const { } template <typename SectionType> -Section constructSectionCommon(SectionType Sec, uint32_t Index) { +static Section constructSectionCommon(const SectionType &Sec, uint32_t Index) { StringRef SegName(Sec.segname, strnlen(Sec.segname, sizeof(Sec.segname))); StringRef SectName(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname))); Section S(SegName, SectName); @@ -46,39 +46,34 @@ Section constructSectionCommon(SectionType Sec, uint32_t Index) { return S; } -template <typename SectionType> -Section constructSection(SectionType Sec, uint32_t Index); - -template <> Section constructSection(MachO::section Sec, uint32_t Index) { +Section constructSection(const MachO::section &Sec, uint32_t Index) { return constructSectionCommon(Sec, Index); } -template <> Section constructSection(MachO::section_64 Sec, uint32_t Index) { +Section constructSection(const MachO::section_64 &Sec, uint32_t Index) { Section S = constructSectionCommon(Sec, Index); S.Reserved3 = Sec.reserved3; return S; } template <typename SectionType, typename SegmentType> -Expected<std::vector<std::unique_ptr<Section>>> -extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, - const object::MachOObjectFile &MachOObj, - uint32_t &NextSectionIndex) { - auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize; - const SectionType *Curr = - reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType)); +Expected<std::vector<std::unique_ptr<Section>>> static extractSections( + const object::MachOObjectFile::LoadCommandInfo &LoadCmd, + const object::MachOObjectFile &MachOObj, uint32_t &NextSectionIndex) { std::vector<std::unique_ptr<Section>> Sections; - for (; reinterpret_cast<const void *>(Curr) < End; Curr++) { - if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { - SectionType Sec; - memcpy((void *)&Sec, Curr, sizeof(SectionType)); + for (auto Curr = reinterpret_cast<const SectionType *>(LoadCmd.Ptr + + sizeof(SegmentType)), + End = reinterpret_cast<const SectionType *>(LoadCmd.Ptr + + LoadCmd.C.cmdsize); + Curr < End; ++Curr) { + SectionType Sec; + memcpy((void *)&Sec, Curr, sizeof(SectionType)); + + if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) MachO::swapStruct(Sec); - Sections.push_back( - std::make_unique<Section>(constructSection(Sec, NextSectionIndex))); - } else { - Sections.push_back( - std::make_unique<Section>(constructSection(*Curr, NextSectionIndex))); - } + + Sections.push_back( + std::make_unique<Section>(constructSection(Sec, NextSectionIndex))); Section &S = *Sections.back(); @@ -95,6 +90,7 @@ extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, S.Content = StringRef(reinterpret_cast<const char *>(Data->data()), Data->size()); + const uint32_t CPUType = MachOObj.getHeader().cputype; S.Relocations.reserve(S.NReloc); for (auto RI = MachOObj.section_rel_begin(SecRef->getRawDataRefImpl()), RE = MachOObj.section_rel_end(SecRef->getRawDataRefImpl()); @@ -103,6 +99,10 @@ extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, R.Symbol = nullptr; // We'll fill this field later. R.Info = MachOObj.getRelocation(RI->getRawDataRefImpl()); R.Scattered = MachOObj.isRelocationScattered(R.Info); + unsigned Type = MachOObj.getAnyRelocationType(R.Info); + // TODO Support CPU_TYPE_ARM. + R.IsAddend = !R.Scattered && (CPUType == MachO::CPU_TYPE_ARM64 && + Type == MachO::ARM64_RELOC_ADDEND); R.Extern = !R.Scattered && MachOObj.getPlainRelocationExternal(R.Info); S.Relocations.push_back(R); } @@ -151,6 +151,9 @@ Error MachOReader::readLoadCommands(Object &O) const { case MachO::LC_DATA_IN_CODE: O.DataInCodeCommandIndex = O.LoadCommands.size(); break; + case MachO::LC_LINKER_OPTIMIZATION_HINT: + O.LinkerOptimizationHintCommandIndex = O.LoadCommands.size(); + break; case MachO::LC_FUNCTION_STARTS: O.FunctionStartsCommandIndex = O.LoadCommands.size(); break; @@ -223,7 +226,7 @@ void MachOReader::setSymbolInRelocationInfo(Object &O) const { for (LoadCommand &LC : O.LoadCommands) for (std::unique_ptr<Section> &Sec : LC.Sections) for (auto &Reloc : Sec->Relocations) - if (!Reloc.Scattered) { + if (!Reloc.Scattered && !Reloc.IsAddend) { const uint32_t SymbolNum = Reloc.getPlainRelocationSymbolNum(MachOObj.isLittleEndian()); if (Reloc.Extern) { @@ -276,6 +279,11 @@ void MachOReader::readDataInCodeData(Object &O) const { return readLinkData(O, O.DataInCodeCommandIndex, O.DataInCode); } +void MachOReader::readLinkerOptimizationHint(Object &O) const { + return readLinkData(O, O.LinkerOptimizationHintCommandIndex, + O.LinkerOptimizationHint); +} + void MachOReader::readFunctionStartsData(Object &O) const { return readLinkData(O, O.FunctionStartsCommandIndex, O.FunctionStarts); } @@ -330,12 +338,9 @@ Expected<std::unique_ptr<Object>> MachOReader::create() const { readExportInfo(*Obj); readCodeSignature(*Obj); readDataInCodeData(*Obj); + readLinkerOptimizationHint(*Obj); readFunctionStartsData(*Obj); readIndirectSymbolTable(*Obj); readSwiftVersion(*Obj); return std::move(Obj); } - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.h b/llvm/tools/llvm-objcopy/MachO/MachOReader.h index b446e02865e5..ca3a0214cb6d 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOReader.h +++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.h @@ -39,6 +39,7 @@ class MachOReader : public Reader { void readLinkData(Object &O, Optional<size_t> LCIndex, LinkData &LD) const; void readCodeSignature(Object &O) const; void readDataInCodeData(Object &O) const; + void readLinkerOptimizationHint(Object &O) const; void readFunctionStartsData(Object &O) const; void readIndirectSymbolTable(Object &O) const; void readSwiftVersion(Object &O) const; diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp index 56dd08df3b09..295098ed4118 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp @@ -16,9 +16,8 @@ #include "llvm/Support/ErrorHandling.h" #include <memory> -namespace llvm { -namespace objcopy { -namespace macho { +using namespace llvm; +using namespace llvm::objcopy::macho; size_t MachOWriter::headerSize() const { return Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); @@ -108,6 +107,16 @@ size_t MachOWriter::totalSize() const { LinkEditDataCommand.datasize); } + if (O.LinkerOptimizationHintCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.LinkerOptimizationHintCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Ends.push_back(LinkEditDataCommand.dataoff + + LinkEditDataCommand.datasize); + } + if (O.FunctionStartsCommandIndex) { const MachO::linkedit_data_command &LinkEditDataCommand = O.LoadCommands[*O.FunctionStartsCommandIndex] @@ -159,11 +168,12 @@ void MachOWriter::writeHeader() { auto HeaderSize = Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); - memcpy(B.getBufferStart(), &Header, HeaderSize); + memcpy(Buf->getBufferStart(), &Header, HeaderSize); } void MachOWriter::writeLoadCommands() { - uint8_t *Begin = B.getBufferStart() + headerSize(); + uint8_t *Begin = + reinterpret_cast<uint8_t *>(Buf->getBufferStart()) + headerSize(); for (const LoadCommand &LC : O.LoadCommands) { // Construct a load command. MachO::macho_load_command MLC = LC.MachOLoadCommand; @@ -257,11 +267,11 @@ void MachOWriter::writeSections() { assert(Sec->Offset && "Section offset can not be zero"); assert((Sec->Size == Sec->Content.size()) && "Incorrect section size"); - memcpy(B.getBufferStart() + Sec->Offset, Sec->Content.data(), + memcpy(Buf->getBufferStart() + Sec->Offset, Sec->Content.data(), Sec->Content.size()); for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) { RelocationInfo RelocInfo = Sec->Relocations[Index]; - if (!RelocInfo.Scattered) { + if (!RelocInfo.Scattered && !RelocInfo.IsAddend) { const uint32_t SymbolNum = RelocInfo.Extern ? (*RelocInfo.Symbol)->Index : (*RelocInfo.Sec)->Index; @@ -270,7 +280,7 @@ void MachOWriter::writeSections() { if (IsLittleEndian != sys::IsLittleEndianHost) MachO::swapStruct( reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info)); - memcpy(B.getBufferStart() + Sec->RelOff + + memcpy(Buf->getBufferStart() + Sec->RelOff + Index * sizeof(MachO::any_relocation_info), &RelocInfo.Info, sizeof(RelocInfo.Info)); } @@ -300,7 +310,7 @@ void MachOWriter::writeStringTable() { O.LoadCommands[*O.SymTabCommandIndex] .MachOLoadCommand.symtab_command_data; - uint8_t *StrTable = (uint8_t *)B.getBufferStart() + SymTabCommand.stroff; + uint8_t *StrTable = (uint8_t *)Buf->getBufferStart() + SymTabCommand.stroff; LayoutBuilder.getStringTableBuilder().write(StrTable); } @@ -311,7 +321,7 @@ void MachOWriter::writeSymbolTable() { O.LoadCommands[*O.SymTabCommandIndex] .MachOLoadCommand.symtab_command_data; - char *SymTable = (char *)B.getBufferStart() + SymTabCommand.symoff; + char *SymTable = (char *)Buf->getBufferStart() + SymTabCommand.symoff; for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end(); Iter != End; Iter++) { SymbolEntry *Sym = Iter->get(); @@ -330,7 +340,7 @@ void MachOWriter::writeRebaseInfo() { const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.rebase_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.rebase_off; assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) && "Incorrect rebase opcodes size"); memcpy(Out, O.Rebases.Opcodes.data(), O.Rebases.Opcodes.size()); @@ -342,7 +352,7 @@ void MachOWriter::writeBindInfo() { const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.bind_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.bind_off; assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) && "Incorrect bind opcodes size"); memcpy(Out, O.Binds.Opcodes.data(), O.Binds.Opcodes.size()); @@ -354,7 +364,7 @@ void MachOWriter::writeWeakBindInfo() { const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.weak_bind_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.weak_bind_off; assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) && "Incorrect weak bind opcodes size"); memcpy(Out, O.WeakBinds.Opcodes.data(), O.WeakBinds.Opcodes.size()); @@ -366,7 +376,7 @@ void MachOWriter::writeLazyBindInfo() { const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.lazy_bind_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.lazy_bind_off; assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) && "Incorrect lazy bind opcodes size"); memcpy(Out, O.LazyBinds.Opcodes.data(), O.LazyBinds.Opcodes.size()); @@ -378,7 +388,7 @@ void MachOWriter::writeExportInfo() { const MachO::dyld_info_command &DyLdInfoCommand = O.LoadCommands[*O.DyLdInfoCommandIndex] .MachOLoadCommand.dyld_info_command_data; - char *Out = (char *)B.getBufferStart() + DyLdInfoCommand.export_off; + char *Out = (char *)Buf->getBufferStart() + DyLdInfoCommand.export_off; assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) && "Incorrect export trie size"); memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size()); @@ -393,7 +403,7 @@ void MachOWriter::writeIndirectSymbolTable() { .MachOLoadCommand.dysymtab_command_data; uint32_t *Out = - (uint32_t *)(B.getBufferStart() + DySymTabCommand.indirectsymoff); + (uint32_t *)(Buf->getBufferStart() + DySymTabCommand.indirectsymoff); for (const IndirectSymbolEntry &Sym : O.IndirectSymTable.Symbols) { uint32_t Entry = (Sym.Symbol) ? (*Sym.Symbol)->Index : Sym.OriginalIndex; if (IsLittleEndian != sys::IsLittleEndianHost) @@ -407,7 +417,7 @@ void MachOWriter::writeLinkData(Optional<size_t> LCIndex, const LinkData &LD) { return; const MachO::linkedit_data_command &LinkEditDataCommand = O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data; - char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff; + char *Out = (char *)Buf->getBufferStart() + LinkEditDataCommand.dataoff; assert((LinkEditDataCommand.datasize == LD.Data.size()) && "Incorrect data size"); memcpy(Out, LD.Data.data(), LD.Data.size()); @@ -421,6 +431,11 @@ void MachOWriter::writeDataInCodeData() { return writeLinkData(O.DataInCodeCommandIndex, O.DataInCode); } +void MachOWriter::writeLinkerOptimizationHint() { + return writeLinkData(O.LinkerOptimizationHintCommandIndex, + O.LinkerOptimizationHint); +} + void MachOWriter::writeFunctionStartsData() { return writeLinkData(O.FunctionStartsCommandIndex, O.FunctionStarts); } @@ -490,6 +505,16 @@ void MachOWriter::writeTail() { &MachOWriter::writeDataInCodeData); } + if (O.LinkerOptimizationHintCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.LinkerOptimizationHintCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Queue.emplace_back(LinkEditDataCommand.dataoff, + &MachOWriter::writeLinkerOptimizationHint); + } + if (O.FunctionStartsCommandIndex) { const MachO::linkedit_data_command &LinkEditDataCommand = O.LoadCommands[*O.FunctionStartsCommandIndex] @@ -511,16 +536,20 @@ void MachOWriter::writeTail() { Error MachOWriter::finalize() { return LayoutBuilder.layout(); } Error MachOWriter::write() { - if (Error E = B.allocate(totalSize())) - return E; - memset(B.getBufferStart(), 0, totalSize()); + size_t TotalSize = totalSize(); + Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); + if (!Buf) + return createStringError(errc::not_enough_memory, + "failed to allocate memory buffer of " + + Twine::utohexstr(TotalSize) + " bytes"); + memset(Buf->getBufferStart(), 0, totalSize()); writeHeader(); writeLoadCommands(); writeSections(); writeTail(); - return B.commit(); -} -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm + // TODO: Implement direct writing to the output stream (without intermediate + // memory buffer Buf). + Out.write(Buf->getBufferStart(), Buf->getBufferSize()); + return Error::success(); +} diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h index c2c6f5a55e9a..c8c06d644e9f 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h +++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// -#include "../Buffer.h" #include "MachOLayoutBuilder.h" #include "MachOObjcopy.h" #include "Object.h" @@ -24,7 +23,8 @@ class MachOWriter { bool Is64Bit; bool IsLittleEndian; uint64_t PageSize; - Buffer &B; + std::unique_ptr<WritableMemoryBuffer> Buf; + raw_ostream &Out; MachOLayoutBuilder LayoutBuilder; size_t headerSize() const; @@ -48,14 +48,15 @@ class MachOWriter { void writeLinkData(Optional<size_t> LCIndex, const LinkData &LD); void writeCodeSignatureData(); void writeDataInCodeData(); + void writeLinkerOptimizationHint(); void writeFunctionStartsData(); void writeTail(); public: MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, uint64_t PageSize, - Buffer &B) + raw_ostream &Out) : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), - PageSize(PageSize), B(B), LayoutBuilder(O, Is64Bit, PageSize) {} + PageSize(PageSize), Out(Out), LayoutBuilder(O, Is64Bit, PageSize) {} size_t totalSize() const; Error finalize(); diff --git a/llvm/tools/llvm-objcopy/MachO/Object.cpp b/llvm/tools/llvm-objcopy/MachO/Object.cpp index cdb97531fb66..b4f98fa84cb5 100644 --- a/llvm/tools/llvm-objcopy/MachO/Object.cpp +++ b/llvm/tools/llvm-objcopy/MachO/Object.cpp @@ -10,9 +10,8 @@ #include "llvm/ADT/SmallPtrSet.h" #include <unordered_set> -namespace llvm { -namespace objcopy { -namespace macho { +using namespace llvm; +using namespace llvm::objcopy::macho; const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const { assert(Index < Symbols.size() && "invalid symbol index"); @@ -47,6 +46,9 @@ void Object::updateLoadCommandIndexes() { case MachO::LC_DATA_IN_CODE: DataInCodeCommandIndex = Index; break; + case MachO::LC_LINKER_OPTIMIZATION_HINT: + LinkerOptimizationHintCommandIndex = Index; + break; case MachO::LC_FUNCTION_STARTS: FunctionStartsCommandIndex = Index; break; @@ -190,7 +192,3 @@ Optional<uint64_t> LoadCommand::getSegmentVMAddr() const { return None; } } - -} // end namespace macho -} // end namespace objcopy -} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/MachO/Object.h b/llvm/tools/llvm-objcopy/MachO/Object.h index 0bb4b344b2eb..207502e2241b 100644 --- a/llvm/tools/llvm-objcopy/MachO/Object.h +++ b/llvm/tools/llvm-objcopy/MachO/Object.h @@ -180,6 +180,9 @@ struct RelocationInfo { Optional<const Section *> Sec; // True if Info is a scattered_relocation_info. bool Scattered; + // True if the type is an ADDEND. r_symbolnum holds the addend instead of a + // symbol index. + bool IsAddend; // True if the r_symbolnum points to a section number (i.e. r_extern=0). bool Extern; MachO::any_relocation_info Info; @@ -310,6 +313,7 @@ struct Object { ExportInfo Exports; IndirectSymbolTable IndirectSymTable; LinkData DataInCode; + LinkData LinkerOptimizationHint; LinkData FunctionStarts; LinkData CodeSignature; @@ -325,6 +329,8 @@ struct Object { Optional<size_t> DySymTabCommandIndex; /// The index LC_DATA_IN_CODE load comamnd if present. Optional<size_t> DataInCodeCommandIndex; + /// The index of LC_LINKER_OPTIMIZATIN_HINT load comamnd if present. + Optional<size_t> LinkerOptimizationHintCommandIndex; /// The index LC_FUNCTION_STARTS load comamnd if present. Optional<size_t> FunctionStartsCommandIndex; diff --git a/llvm/tools/llvm-objcopy/MultiFormatConfig.h b/llvm/tools/llvm-objcopy/MultiFormatConfig.h new file mode 100644 index 000000000000..31d9883d6d3a --- /dev/null +++ b/llvm/tools/llvm-objcopy/MultiFormatConfig.h @@ -0,0 +1,37 @@ +//===- MultiFormatConfig.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { + +struct CommonConfig; +struct ELFConfig; +struct COFFConfig; +struct MachOConfig; +struct WasmConfig; + +class MultiFormatConfig { +public: + virtual ~MultiFormatConfig() {} + + virtual const CommonConfig &getCommonConfig() const = 0; + virtual Expected<const ELFConfig &> getELFConfig() const = 0; + virtual Expected<const COFFConfig &> getCOFFConfig() const = 0; + virtual Expected<const MachOConfig &> getMachOConfig() const = 0; + virtual Expected<const WasmConfig &> getWasmConfig() const = 0; +}; + +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_MULTIFORMATCONFIG_H diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td index 9e6b6f0005cd..63abbe4c2020 100644 --- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -196,19 +196,6 @@ defm prefix_alloc_sections : Eq<"prefix-alloc-sections", "Add <prefix> to the start of every allocated section name">, MetaVarName<"prefix">; -defm build_id_link_dir - : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and " - "--build-id-link-output to <dir>">, - MetaVarName<"dir">; -defm build_id_link_input - : Eq<"build-id-link-input", "Hard-link the input to <dir>/xx/xxx<suffix> " - "name derived from hex build ID">, - MetaVarName<"suffix">; -defm build_id_link_output - : Eq<"build-id-link-output", "Hard-link the output to <dir>/xx/xxx<suffix> " - "name derived from hex build ID">, - MetaVarName<"suffix">; - defm set_start : Eq<"set-start", "Set the start address to <addr>. Overrides " "any previous --change-start or --adjust-start values.">, MetaVarName<"addr">; diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp index 7fd2acd11e99..ad166487eb78 100644 --- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -6,17 +6,23 @@ // //===----------------------------------------------------------------------===// -#include "Buffer.h" +#include "llvm-objcopy.h" +#include "COFF/COFFConfig.h" #include "COFF/COFFObjcopy.h" -#include "CopyConfig.h" +#include "CommonConfig.h" +#include "ConfigManager.h" +#include "ELF/ELFConfig.h" #include "ELF/ELFObjcopy.h" +#include "MachO/MachOConfig.h" #include "MachO/MachOObjcopy.h" +#include "wasm/WasmConfig.h" #include "wasm/WasmObjcopy.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/Binary.h" @@ -32,6 +38,7 @@ #include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" @@ -40,6 +47,7 @@ #include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/SmallVectorMemoryBuffer.h" #include "llvm/Support/StringSaver.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" @@ -51,13 +59,14 @@ #include <system_error> #include <utility> -namespace llvm { -namespace objcopy { +using namespace llvm; +using namespace llvm::objcopy; +using namespace llvm::object; // The name this program was invoked as. -StringRef ToolName; +static StringRef ToolName; -ErrorSuccess reportWarning(Error E) { +static ErrorSuccess reportWarning(Error E) { assert(E); WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n'; return Error::success(); @@ -72,7 +81,7 @@ static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) { // strip-10.exe -> strip // powerpc64-unknown-freebsd13-objcopy -> objcopy // llvm-install-name-tool -> install-name-tool - auto I = Stem.rfind_lower(Tool); + auto I = Stem.rfind_insensitive(Tool); return I != StringRef::npos && (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); }; @@ -87,13 +96,6 @@ static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) { return parseObjcopyOptions(Args, reportWarning); } -} // end namespace objcopy -} // end namespace llvm - -using namespace llvm; -using namespace llvm::object; -using namespace llvm::objcopy; - // For regular archives this function simply calls llvm::writeArchive, // For thin archives it writes the archive file itself as well as its members. static Error deepWriteArchive(StringRef ArcName, @@ -108,20 +110,21 @@ static Error deepWriteArchive(StringRef ArcName, return Error::success(); for (const NewArchiveMember &Member : NewMembers) { - // Internally, FileBuffer will use the buffer created by - // FileOutputBuffer::create, for regular files (that is the case for - // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer. + // For regular files (as is the case for deepWriteArchive), + // FileOutputBuffer::create will return OnDiskBuffer. // OnDiskBuffer uses a temporary file and then renames it. So in reality // there is no inefficiency / duplicated in-memory buffers in this case. For // now in-memory buffers can not be completely avoided since // NewArchiveMember still requires them even though writeArchive does not // write them on disk. - FileBuffer FB(Member.MemberName); - if (Error E = FB.allocate(Member.Buf->getBufferSize())) - return E; + Expected<std::unique_ptr<FileOutputBuffer>> FB = + FileOutputBuffer::create(Member.MemberName, Member.Buf->getBufferSize(), + FileOutputBuffer::F_executable); + if (!FB) + return FB.takeError(); std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), - FB.getBufferStart()); - if (Error E = FB.commit()) + (*FB)->getBufferStart()); + if (Error E = (*FB)->commit()) return E; } return Error::success(); @@ -129,18 +132,22 @@ static Error deepWriteArchive(StringRef ArcName, /// The function executeObjcopyOnIHex does the dispatch based on the format /// of the output specified by the command line options. -static Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { +static Error executeObjcopyOnIHex(ConfigManager &ConfigMgr, MemoryBuffer &In, + raw_ostream &Out) { // TODO: support output formats other than ELF. - if (Error E = Config.parseELFConfig()) - return E; - return elf::executeObjcopyOnIHex(Config, In, Out); + Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig(); + if (!ELFConfig) + return ELFConfig.takeError(); + + return elf::executeObjcopyOnIHex(ConfigMgr.getCommonConfig(), *ELFConfig, In, + Out); } /// The function executeObjcopyOnRawBinary does the dispatch based on the format /// of the output specified by the command line options. -static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { +static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr, + MemoryBuffer &In, raw_ostream &Out) { + const CommonConfig &Config = ConfigMgr.getCommonConfig(); switch (Config.OutputFormat) { case FileFormat::ELF: // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the @@ -149,9 +156,11 @@ static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, case FileFormat::Binary: case FileFormat::IHex: case FileFormat::Unspecified: - if (Error E = Config.parseELFConfig()) - return E; - return elf::executeObjcopyOnRawBinary(Config, In, Out); + Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig(); + if (!ELFConfig) + return ELFConfig.takeError(); + + return elf::executeObjcopyOnRawBinary(Config, *ELFConfig, In, Out); } llvm_unreachable("unsupported output format"); @@ -159,23 +168,41 @@ static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, /// The function executeObjcopyOnBinary does the dispatch based on the format /// of the input binary (ELF, MachO or COFF). -static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, - Buffer &Out) { +static Error executeObjcopyOnBinary(const MultiFormatConfig &Config, + object::Binary &In, raw_ostream &Out) { if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) { - if (Error E = Config.parseELFConfig()) - return E; - return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); - } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) - return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); - else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) - return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); - else if (auto *MachOUniversalBinary = - dyn_cast<object::MachOUniversalBinary>(&In)) + Expected<const ELFConfig &> ELFConfig = Config.getELFConfig(); + if (!ELFConfig) + return ELFConfig.takeError(); + + return elf::executeObjcopyOnBinary(Config.getCommonConfig(), *ELFConfig, + *ELFBinary, Out); + } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) { + Expected<const COFFConfig &> COFFConfig = Config.getCOFFConfig(); + if (!COFFConfig) + return COFFConfig.takeError(); + + return coff::executeObjcopyOnBinary(Config.getCommonConfig(), *COFFConfig, + *COFFBinary, Out); + } else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) { + Expected<const MachOConfig &> MachOConfig = Config.getMachOConfig(); + if (!MachOConfig) + return MachOConfig.takeError(); + + return macho::executeObjcopyOnBinary(Config.getCommonConfig(), *MachOConfig, + *MachOBinary, Out); + } else if (auto *MachOUniversalBinary = + dyn_cast<object::MachOUniversalBinary>(&In)) { return macho::executeObjcopyOnMachOUniversalBinary( Config, *MachOUniversalBinary, Out); - else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In)) - return objcopy::wasm::executeObjcopyOnBinary(Config, *WasmBinary, Out); - else + } else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In)) { + Expected<const WasmConfig &> WasmConfig = Config.getWasmConfig(); + if (!WasmConfig) + return WasmConfig.takeError(); + + return objcopy::wasm::executeObjcopyOnBinary(Config.getCommonConfig(), + *WasmConfig, *WasmBinary, Out); + } else return createStringError(object_error::invalid_file_type, "unsupported object file format"); } @@ -184,7 +211,7 @@ namespace llvm { namespace objcopy { Expected<std::vector<NewArchiveMember>> -createNewArchiveMembers(CopyConfig &Config, const Archive &Ar) { +createNewArchiveMembers(const MultiFormatConfig &Config, const Archive &Ar) { std::vector<NewArchiveMember> NewArchiveMembers; Error Err = Error::success(); for (const Archive::Child &Child : Ar.children(Err)) { @@ -197,32 +224,38 @@ createNewArchiveMembers(CopyConfig &Config, const Archive &Ar) { return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", ChildOrErr.takeError()); - MemBuffer MB(ChildNameOrErr.get()); - if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) + SmallVector<char, 0> Buffer; + raw_svector_ostream MemStream(Buffer); + + if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MemStream)) return std::move(E); - Expected<NewArchiveMember> Member = - NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); + Expected<NewArchiveMember> Member = NewArchiveMember::getOldMember( + Child, Config.getCommonConfig().DeterministicArchives); if (!Member) return createFileError(Ar.getFileName(), Member.takeError()); - Member->Buf = MB.releaseMemoryBuffer(); + + Member->Buf = std::make_unique<SmallVectorMemoryBuffer>( + std::move(Buffer), ChildNameOrErr.get()); Member->MemberName = Member->Buf->getBufferIdentifier(); NewArchiveMembers.push_back(std::move(*Member)); } if (Err) - return createFileError(Config.InputFilename, std::move(Err)); + return createFileError(Config.getCommonConfig().InputFilename, + std::move(Err)); return std::move(NewArchiveMembers); } } // end namespace objcopy } // end namespace llvm -static Error executeObjcopyOnArchive(CopyConfig &Config, +static Error executeObjcopyOnArchive(const ConfigManager &ConfigMgr, const object::Archive &Ar) { Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr = - createNewArchiveMembers(Config, Ar); + createNewArchiveMembers(ConfigMgr, Ar); if (!NewArchiveMembersOrErr) return NewArchiveMembersOrErr.takeError(); + const CommonConfig &Config = ConfigMgr.getCommonConfig(); return deepWriteArchive(Config.OutputFilename, *NewArchiveMembersOrErr, Ar.hasSymbolTable(), Ar.kind(), Config.DeterministicArchives, Ar.isThin()); @@ -230,8 +263,9 @@ static Error executeObjcopyOnArchive(CopyConfig &Config, static Error restoreStatOnFile(StringRef Filename, const sys::fs::file_status &Stat, - bool PreserveDates) { + const ConfigManager &ConfigMgr) { int FD; + const CommonConfig &Config = ConfigMgr.getCommonConfig(); // Writing to stdout should not be treated as an error here, just // do not set access/modification times or permissions. @@ -242,7 +276,7 @@ static Error restoreStatOnFile(StringRef Filename, sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) return createFileError(Filename, EC); - if (PreserveDates) + if (Config.PreserveDates) if (auto EC = sys::fs::setLastAccessAndModificationTime( FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) return createFileError(Filename, EC); @@ -250,17 +284,23 @@ static Error restoreStatOnFile(StringRef Filename, sys::fs::file_status OStat; if (std::error_code EC = sys::fs::status(FD, OStat)) return createFileError(Filename, EC); - if (OStat.type() == sys::fs::file_type::regular_file) + if (OStat.type() == sys::fs::file_type::regular_file) { +#ifndef _WIN32 + // Keep ownership if llvm-objcopy is called under root. + if (Config.InputFilename == Config.OutputFilename && OStat.getUser() == 0) + sys::fs::changeFileOwnership(FD, Stat.getUser(), Stat.getGroup()); +#endif + + sys::fs::perms Perm = Stat.permissions(); + if (Config.InputFilename != Config.OutputFilename) + Perm = static_cast<sys::fs::perms>(Perm & ~sys::fs::getUmask() & ~06000); #ifdef _WIN32 - if (auto EC = sys::fs::setPermissions( - Filename, static_cast<sys::fs::perms>(Stat.permissions() & - ~sys::fs::getUmask()))) + if (auto EC = sys::fs::setPermissions(Filename, Perm)) #else - if (auto EC = sys::fs::setPermissions( - FD, static_cast<sys::fs::perms>(Stat.permissions() & - ~sys::fs::getUmask()))) + if (auto EC = sys::fs::setPermissions(FD, Perm)) #endif return createFileError(Filename, EC); + } if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) return createFileError(Filename, EC); @@ -271,7 +311,9 @@ static Error restoreStatOnFile(StringRef Filename, /// The function executeObjcopy does the higher level dispatch based on the type /// of input (raw binary, archive or single object file) and takes care of the /// format-agnostic modifications, i.e. preserving dates. -static Error executeObjcopy(CopyConfig &Config) { +static Error executeObjcopy(ConfigManager &ConfigMgr) { + CommonConfig &Config = ConfigMgr.Common; + sys::fs::file_status Stat; if (Config.InputFilename != "-") { if (auto EC = sys::fs::status(Config.InputFilename, Stat)) @@ -280,61 +322,85 @@ static Error executeObjcopy(CopyConfig &Config) { Stat.permissions(static_cast<sys::fs::perms>(0777)); } - using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &); - ProcessRawFn ProcessRaw; - switch (Config.InputFormat) { - case FileFormat::Binary: - ProcessRaw = executeObjcopyOnRawBinary; - break; - case FileFormat::IHex: - ProcessRaw = executeObjcopyOnIHex; - break; - default: - ProcessRaw = nullptr; - } + std::function<Error(raw_ostream & OutFile)> ObjcopyFunc; + + OwningBinary<llvm::object::Binary> BinaryHolder; + std::unique_ptr<MemoryBuffer> MemoryBufferHolder; - if (ProcessRaw) { - auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); + if (Config.InputFormat == FileFormat::Binary || + Config.InputFormat == FileFormat::IHex) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFileOrSTDIN(Config.InputFilename); if (!BufOrErr) return createFileError(Config.InputFilename, BufOrErr.getError()); - FileBuffer FB(Config.OutputFilename); - if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) - return E; + MemoryBufferHolder = std::move(*BufOrErr); + + if (Config.InputFormat == FileFormat::Binary) + ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { + // Handle FileFormat::Binary. + return executeObjcopyOnRawBinary(ConfigMgr, *MemoryBufferHolder, + OutFile); + }; + else + ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { + // Handle FileFormat::IHex. + return executeObjcopyOnIHex(ConfigMgr, *MemoryBufferHolder, OutFile); + }; } else { Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = createBinary(Config.InputFilename); if (!BinaryOrErr) return createFileError(Config.InputFilename, BinaryOrErr.takeError()); + BinaryHolder = std::move(*BinaryOrErr); - if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { - if (Error E = executeObjcopyOnArchive(Config, *Ar)) + if (Archive *Ar = dyn_cast<Archive>(BinaryHolder.getBinary())) { + // Handle Archive. + if (Error E = executeObjcopyOnArchive(ConfigMgr, *Ar)) return E; } else { - FileBuffer FB(Config.OutputFilename); - if (Error E = executeObjcopyOnBinary(Config, - *BinaryOrErr.get().getBinary(), FB)) + // Handle llvm::object::Binary. + ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { + return executeObjcopyOnBinary(ConfigMgr, *BinaryHolder.getBinary(), + OutFile); + }; + } + } + + if (ObjcopyFunc) { + if (Config.SplitDWO.empty()) { + // Apply transformations described by Config and store result into + // Config.OutputFilename using specified ObjcopyFunc function. + if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc)) + return E; + } else { + Config.ExtractDWO = true; + Config.StripDWO = false; + // Copy .dwo tables from the Config.InputFilename into Config.SplitDWO + // file using specified ObjcopyFunc function. + if (Error E = writeToOutput(Config.SplitDWO, ObjcopyFunc)) + return E; + Config.ExtractDWO = false; + Config.StripDWO = true; + // Apply transformations described by Config, remove .dwo tables and + // store result into Config.OutputFilename using specified ObjcopyFunc + // function. + if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc)) return E; } } - if (Error E = - restoreStatOnFile(Config.OutputFilename, Stat, Config.PreserveDates)) + if (Error E = restoreStatOnFile(Config.OutputFilename, Stat, ConfigMgr)) return E; if (!Config.SplitDWO.empty()) { Stat.permissions(static_cast<sys::fs::perms>(0666)); - if (Error E = - restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates)) + if (Error E = restoreStatOnFile(Config.SplitDWO, Stat, ConfigMgr)) return E; } return Error::success(); } -namespace { - -} // anonymous namespace - int main(int argc, char **argv) { InitLLVM X(argc, argv); ToolName = argv[0]; @@ -360,8 +426,8 @@ int main(int argc, char **argv) { WithColor::error(errs(), ToolName)); return 1; } - for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { - if (Error E = executeObjcopy(CopyConfig)) { + for (ConfigManager &ConfigMgr : DriverConfig->CopyConfigs) { + if (Error E = executeObjcopy(ConfigMgr)) { logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); return 1; } diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.h b/llvm/tools/llvm-objcopy/llvm-objcopy.h index 97a166769f95..182c95dc64c8 100644 --- a/llvm/tools/llvm-objcopy/llvm-objcopy.h +++ b/llvm/tools/llvm-objcopy/llvm-objcopy.h @@ -10,6 +10,7 @@ #define LLVM_TOOLS_OBJCOPY_OBJCOPY_H #include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" namespace llvm { @@ -22,9 +23,10 @@ class Archive; } // end namespace object namespace objcopy { -struct CopyConfig; +class MultiFormatConfig; Expected<std::vector<NewArchiveMember>> -createNewArchiveMembers(CopyConfig &Config, const object::Archive &Ar); +createNewArchiveMembers(const MultiFormatConfig &Config, + const object::Archive &Ar); } // end namespace objcopy } // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/wasm/WasmConfig.h b/llvm/tools/llvm-objcopy/wasm/WasmConfig.h new file mode 100644 index 000000000000..4e40926ae453 --- /dev/null +++ b/llvm/tools/llvm-objcopy/wasm/WasmConfig.h @@ -0,0 +1,21 @@ +//===- WasmConfig.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H + +namespace llvm { +namespace objcopy { + +// Wasm specific configuration for copying/stripping a single file. +struct WasmConfig {}; + +} // namespace objcopy +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMCONFIG_H diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp index eb0e5635cef9..397d09757e54 100644 --- a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp @@ -7,18 +7,35 @@ //===----------------------------------------------------------------------===// #include "WasmObjcopy.h" -#include "Buffer.h" -#include "CopyConfig.h" +#include "CommonConfig.h" #include "Object.h" #include "Reader.h" #include "Writer.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/FileOutputBuffer.h" namespace llvm { namespace objcopy { namespace wasm { using namespace object; +using SectionPred = std::function<bool(const Section &Sec)>; + +static bool isDebugSection(const Section &Sec) { + return Sec.Name.startswith(".debug"); +} + +static bool isLinkerSection(const Section &Sec) { + return Sec.Name.startswith("reloc.") || Sec.Name == "linking"; +} + +static bool isNameSection(const Section &Sec) { return Sec.Name == "name"; } + +// Sections which are known to be "comments" or informational and do not affect +// program semantics. +static bool isCommentSection(const Section &Sec) { + return Sec.Name == "producers"; +} static Error dumpSectionToFile(StringRef SecName, StringRef Filename, Object &Obj) { @@ -39,7 +56,60 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename, return createStringError(errc::invalid_argument, "section '%s' not found", SecName.str().c_str()); } -static Error handleArgs(const CopyConfig &Config, Object &Obj) { + +static void removeSections(const CommonConfig &Config, Object &Obj) { + SectionPred RemovePred = [](const Section &) { return false; }; + + // Explicitly-requested sections. + if (!Config.ToRemove.empty()) { + RemovePred = [&Config](const Section &Sec) { + return Config.ToRemove.matches(Sec.Name); + }; + } + + if (Config.StripDebug) { + RemovePred = [RemovePred](const Section &Sec) { + return RemovePred(Sec) || isDebugSection(Sec); + }; + } + + if (Config.StripAll) { + RemovePred = [RemovePred](const Section &Sec) { + return RemovePred(Sec) || isDebugSection(Sec) || isLinkerSection(Sec) || + isNameSection(Sec) || isCommentSection(Sec); + }; + } + + if (Config.OnlyKeepDebug) { + RemovePred = [&Config](const Section &Sec) { + // Keep debug sections, unless explicitly requested to remove. + // Remove everything else, including known sections. + return Config.ToRemove.matches(Sec.Name) || !isDebugSection(Sec); + }; + } + + if (!Config.OnlySection.empty()) { + RemovePred = [&Config](const Section &Sec) { + // Explicitly keep these sections regardless of previous removes. + // Remove everything else, inluding known sections. + return !Config.OnlySection.matches(Sec.Name); + }; + } + + if (!Config.KeepSection.empty()) { + RemovePred = [&Config, RemovePred](const Section &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (Config.KeepSection.matches(Sec.Name)) + return false; + // Otherwise defer to RemovePred. + return RemovePred(Sec); + }; + } + + Obj.removeSections(RemovePred); +} + +static Error handleArgs(const CommonConfig &Config, Object &Obj) { // Only support AddSection, DumpSection, RemoveSection for now. for (StringRef Flag : Config.DumpSection) { StringRef SecName; @@ -49,11 +119,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { return createFileError(FileName, std::move(E)); } - Obj.removeSections([&Config](const Section &Sec) { - if (Config.ToRemove.matches(Sec.Name)) - return true; - return false; - }); + removeSections(Config, Obj); for (StringRef Flag : Config.AddSection) { StringRef SecName, FileName; @@ -72,28 +138,11 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { Obj.addSectionWithOwnedContents(Sec, std::move(Buf)); } - if (!Config.AddGnuDebugLink.empty() || !Config.BuildIdLinkDir.empty() || - Config.BuildIdLinkInput || Config.BuildIdLinkOutput || - Config.ExtractPartition || !Config.SplitDWO.empty() || - !Config.SymbolsPrefix.empty() || !Config.AllocSectionsPrefix.empty() || - Config.DiscardMode != DiscardType::None || Config.NewSymbolVisibility || - !Config.SymbolsToAdd.empty() || !Config.RPathToAdd.empty() || - !Config.OnlySection.empty() || !Config.SymbolsToGlobalize.empty() || - !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || - !Config.SymbolsToRemove.empty() || - !Config.UnneededSymbolsToRemove.empty() || - !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || - !Config.SectionsToRename.empty() || !Config.SetSectionAlignment.empty() || - !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty()) { - return createStringError( - llvm::errc::invalid_argument, - "only add-section, dump-section, and remove-section are supported"); - } return Error::success(); } -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::WasmObjectFile &In, Buffer &Out) { +Error executeObjcopyOnBinary(const CommonConfig &Config, const WasmConfig &, + object::WasmObjectFile &In, raw_ostream &Out) { Reader TheReader(In); Expected<std::unique_ptr<Object>> ObjOrErr = TheReader.create(); if (!ObjOrErr) diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h index 3557d5c0a50d..28268e38c584 100644 --- a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h +++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h @@ -11,18 +11,19 @@ namespace llvm { class Error; +class raw_ostream; namespace object { class WasmObjectFile; } // end namespace object namespace objcopy { -struct CopyConfig; -class Buffer; +struct CommonConfig; +struct WasmConfig; namespace wasm { -Error executeObjcopyOnBinary(const CopyConfig &Config, - object::WasmObjectFile &In, Buffer &Out); +Error executeObjcopyOnBinary(const CommonConfig &Config, const WasmConfig &, + object::WasmObjectFile &In, raw_ostream &Out); } // end namespace wasm } // end namespace objcopy diff --git a/llvm/tools/llvm-objcopy/wasm/Writer.cpp b/llvm/tools/llvm-objcopy/wasm/Writer.cpp index 50d26507b498..2fad9e60c50f 100644 --- a/llvm/tools/llvm-objcopy/wasm/Writer.cpp +++ b/llvm/tools/llvm-objcopy/wasm/Writer.cpp @@ -9,6 +9,7 @@ #include "Writer.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" @@ -54,23 +55,23 @@ size_t Writer::finalize() { } Error Writer::write() { - size_t FileSize = finalize(); - if (Error E = Buf.allocate(FileSize)) - return E; + size_t TotalSize = finalize(); + Out.reserveExtraSpace(TotalSize); // Write the header. - uint8_t *Ptr = Buf.getBufferStart(); - Ptr = std::copy(Obj.Header.Magic.begin(), Obj.Header.Magic.end(), Ptr); - support::endian::write32le(Ptr, Obj.Header.Version); - Ptr += sizeof(Obj.Header.Version); + Out.write(Obj.Header.Magic.data(), Obj.Header.Magic.size()); + uint32_t Version; + support::endian::write32le(&Version, Obj.Header.Version); + Out.write(reinterpret_cast<const char *>(&Version), sizeof(Version)); // Write each section. for (size_t I = 0, S = SectionHeaders.size(); I < S; ++I) { - Ptr = std::copy(SectionHeaders[I].begin(), SectionHeaders[I].end(), Ptr); - ArrayRef<uint8_t> Contents = Obj.Sections[I].Contents; - Ptr = std::copy(Contents.begin(), Contents.end(), Ptr); + Out.write(SectionHeaders[I].data(), SectionHeaders[I].size()); + Out.write(reinterpret_cast<const char *>(Obj.Sections[I].Contents.data()), + Obj.Sections[I].Contents.size()); } - return Buf.commit(); + + return Error::success(); } } // end namespace wasm diff --git a/llvm/tools/llvm-objcopy/wasm/Writer.h b/llvm/tools/llvm-objcopy/wasm/Writer.h index da48ee730c3b..4404cd8caf84 100644 --- a/llvm/tools/llvm-objcopy/wasm/Writer.h +++ b/llvm/tools/llvm-objcopy/wasm/Writer.h @@ -9,7 +9,6 @@ #ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H #define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H -#include "Buffer.h" #include "Object.h" #include <cstdint> #include <vector> @@ -20,13 +19,13 @@ namespace wasm { class Writer { public: - Writer(Object &Obj, Buffer &Buf) : Obj(Obj), Buf(Buf) {} + Writer(Object &Obj, raw_ostream &Out) : Obj(Obj), Out(Out) {} Error write(); private: using SectionHeader = SmallVector<char, 8>; Object &Obj; - Buffer &Buf; + raw_ostream &Out; std::vector<SectionHeader> SectionHeaders; /// Generate a wasm section section header for S. diff --git a/llvm/tools/llvm-objdump/COFFDump.cpp b/llvm/tools/llvm-objdump/COFFDump.cpp index b9d69d62e4e7..09a900182d24 100644 --- a/llvm/tools/llvm-objdump/COFFDump.cpp +++ b/llvm/tools/llvm-objdump/COFFDump.cpp @@ -454,8 +454,7 @@ static bool getPDataSection(const COFFObjectFile *Obj, continue; const coff_section *Pdata = Obj->getCOFFSection(Section); - for (const RelocationRef &Reloc : Section.relocations()) - Rels.push_back(Reloc); + append_range(Rels, Section.relocations()); // Sort relocations by address. llvm::sort(Rels, isRelocAddressLess); diff --git a/llvm/tools/llvm-objdump/ELFDump.cpp b/llvm/tools/llvm-objdump/ELFDump.cpp index 1c4d59179cc7..da7415834c63 100644 --- a/llvm/tools/llvm-objdump/ELFDump.cpp +++ b/llvm/tools/llvm-objdump/ELFDump.cpp @@ -74,7 +74,10 @@ static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj, const typename ELFT::Rela *ERela = Obj->getRela(Rel); Addend = ERela->r_addend; Undef = ERela->getSymbol(false) == 0; - } else if ((*SecOrErr)->sh_type != ELF::SHT_REL) { + } else if ((*SecOrErr)->sh_type == ELF::SHT_REL) { + const typename ELFT::Rel *ERel = Obj->getRel(Rel); + Undef = ERel->getSymbol(false) == 0; + } else { return make_error<BinaryError>(); } @@ -177,7 +180,7 @@ static void printDynamicSection(const ELFFile<ELFT> &Elf, StringRef Filename) { MaxLen = std::max(MaxLen, Elf.getDynamicTagAsString(Dyn.d_tag).size()); std::string TagFmt = " %-" + std::to_string(MaxLen) + "s "; - outs() << "Dynamic Section:\n"; + outs() << "\nDynamic Section:\n"; for (const typename ELFT::Dyn &Dyn : DynamicEntries) { if (Dyn.d_tag == ELF::DT_NULL) continue; @@ -205,7 +208,7 @@ static void printDynamicSection(const ELFFile<ELFT> &Elf, StringRef Filename) { template <class ELFT> static void printProgramHeaders(const ELFFile<ELFT> &Obj, StringRef FileName) { - outs() << "Program Header:\n"; + outs() << "\nProgram Header:\n"; auto ProgramHeaderOrError = Obj.program_headers(); if (!ProgramHeaderOrError) { reportWarning("unable to read program headers: " + @@ -272,13 +275,12 @@ static void printProgramHeaders(const ELFFile<ELFT> &Obj, StringRef FileName) { << ((Phdr.p_flags & ELF::PF_W) ? "w" : "-") << ((Phdr.p_flags & ELF::PF_X) ? "x" : "-") << "\n"; } - outs() << "\n"; } template <class ELFT> static void printSymbolVersionDependency(ArrayRef<uint8_t> Contents, StringRef StrTab) { - outs() << "Version References:\n"; + outs() << "\nVersion References:\n"; const uint8_t *Buf = Contents.data(); while (Buf) { @@ -304,7 +306,7 @@ template <class ELFT> static void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr, ArrayRef<uint8_t> Contents, StringRef StrTab) { - outs() << "Version definitions:\n"; + outs() << "\nVersion definitions:\n"; const uint8_t *Buf = Contents.data(); uint32_t VerdefIndex = 1; diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp index 51212d52c18b..7c1fdf03542f 100644 --- a/llvm/tools/llvm-objdump/MachODump.cpp +++ b/llvm/tools/llvm-objdump/MachODump.cpp @@ -12,6 +12,7 @@ #include "MachODump.h" +#include "ObjdumpOptID.h" #include "llvm-objdump.h" #include "llvm-c/Disassembler.h" #include "llvm/ADT/STLExtras.h" @@ -34,8 +35,8 @@ #include "llvm/MC/MCTargetOptions.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" +#include "llvm/Option/ArgList.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" @@ -52,7 +53,7 @@ #include <cstring> #include <system_error> -#ifdef HAVE_LIBXAR +#ifdef LLVM_HAVE_LIBXAR extern "C" { #include <xar/xar.h> } @@ -62,125 +63,63 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::objdump; -cl::OptionCategory objdump::MachOCat("llvm-objdump MachO Specific Options"); - -cl::opt<bool> objdump::FirstPrivateHeader( - "private-header", - cl::desc("Display only the first format specific file header"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::ExportsTrie("exports-trie", - cl::desc("Display mach-o exported symbols"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::Rebase("rebase", - cl::desc("Display mach-o rebasing info"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::Bind("bind", cl::desc("Display mach-o binding info"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::LazyBind("lazy-bind", - cl::desc("Display mach-o lazy binding info"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::WeakBind("weak-bind", - cl::desc("Display mach-o weak binding info"), - cl::cat(MachOCat)); - -static cl::opt<bool> - UseDbg("g", cl::Grouping, - cl::desc("Print line information from debug info if available"), - cl::cat(MachOCat)); - -static cl::opt<std::string> DSYMFile("dsym", - cl::desc("Use .dSYM file for debug info"), - cl::cat(MachOCat)); - -static cl::opt<bool> FullLeadingAddr("full-leading-addr", - cl::desc("Print full leading address"), - cl::cat(MachOCat)); - -static cl::opt<bool> NoLeadingHeaders("no-leading-headers", - cl::desc("Print no leading headers"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::UniversalHeaders( - "universal-headers", - cl::desc("Print Mach-O universal headers (requires --macho)"), - cl::cat(MachOCat)); - -static cl::opt<bool> ArchiveMemberOffsets( - "archive-member-offsets", - cl::desc("Print the offset to each archive member for Mach-O archives " - "(requires --macho and --archive-headers)"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::IndirectSymbols( - "indirect-symbols", - cl::desc( - "Print indirect symbol table for Mach-O objects (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::DataInCode( - "data-in-code", - cl::desc( - "Print the data in code table for Mach-O objects (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> - objdump::LinkOptHints("link-opt-hints", - cl::desc("Print the linker optimization hints for " - "Mach-O objects (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> - objdump::InfoPlist("info-plist", - cl::desc("Print the info plist section as strings for " - "Mach-O objects (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> - objdump::DylibsUsed("dylibs-used", - cl::desc("Print the shared libraries used for linked " - "Mach-O files (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> objdump::DylibId("dylib-id", - cl::desc("Print the shared library's id for the " - "dylib Mach-O file (requires --macho)"), - cl::cat(MachOCat)); - -static cl::opt<bool> - NonVerbose("non-verbose", - cl::desc("Print the info for Mach-O objects in non-verbose or " - "numeric form (requires --macho)"), - cl::cat(MachOCat)); - -cl::opt<bool> - objdump::ObjcMetaData("objc-meta-data", - cl::desc("Print the Objective-C runtime meta data " - "for Mach-O files (requires --macho)"), - cl::cat(MachOCat)); - -static cl::opt<std::string> DisSymName( - "dis-symname", - cl::desc("disassemble just this symbol's instructions (requires --macho)"), - cl::cat(MachOCat)); - -static cl::opt<bool> NoSymbolicOperands( - "no-symbolic-operands", - cl::desc("do not symbolic operands when disassembling (requires --macho)"), - cl::cat(MachOCat)); - -static cl::list<std::string> - ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), - cl::ZeroOrMore, cl::cat(MachOCat)); +bool objdump::FirstPrivateHeader; +bool objdump::ExportsTrie; +bool objdump::Rebase; +bool objdump::Rpaths; +bool objdump::Bind; +bool objdump::LazyBind; +bool objdump::WeakBind; +static bool UseDbg; +static std::string DSYMFile; +bool objdump::FullLeadingAddr; +bool objdump::LeadingHeaders; +bool objdump::UniversalHeaders; +static bool ArchiveMemberOffsets; +bool objdump::IndirectSymbols; +bool objdump::DataInCode; +bool objdump::FunctionStarts; +bool objdump::LinkOptHints; +bool objdump::InfoPlist; +bool objdump::DylibsUsed; +bool objdump::DylibId; +bool objdump::Verbose; +bool objdump::ObjcMetaData; +std::string objdump::DisSymName; +bool objdump::SymbolicOperands; +static std::vector<std::string> ArchFlags; static bool ArchAll = false; - static std::string ThumbTripleName; +void objdump::parseMachOOptions(const llvm::opt::InputArgList &InputArgs) { + FirstPrivateHeader = InputArgs.hasArg(OBJDUMP_private_header); + ExportsTrie = InputArgs.hasArg(OBJDUMP_exports_trie); + Rebase = InputArgs.hasArg(OBJDUMP_rebase); + Rpaths = InputArgs.hasArg(OBJDUMP_rpaths); + Bind = InputArgs.hasArg(OBJDUMP_bind); + LazyBind = InputArgs.hasArg(OBJDUMP_lazy_bind); + WeakBind = InputArgs.hasArg(OBJDUMP_weak_bind); + UseDbg = InputArgs.hasArg(OBJDUMP_g); + DSYMFile = InputArgs.getLastArgValue(OBJDUMP_dsym_EQ).str(); + FullLeadingAddr = InputArgs.hasArg(OBJDUMP_full_leading_addr); + LeadingHeaders = !InputArgs.hasArg(OBJDUMP_no_leading_headers); + UniversalHeaders = InputArgs.hasArg(OBJDUMP_universal_headers); + ArchiveMemberOffsets = InputArgs.hasArg(OBJDUMP_archive_member_offsets); + IndirectSymbols = InputArgs.hasArg(OBJDUMP_indirect_symbols); + DataInCode = InputArgs.hasArg(OBJDUMP_data_in_code); + FunctionStarts = InputArgs.hasArg(OBJDUMP_function_starts); + LinkOptHints = InputArgs.hasArg(OBJDUMP_link_opt_hints); + InfoPlist = InputArgs.hasArg(OBJDUMP_info_plist); + DylibsUsed = InputArgs.hasArg(OBJDUMP_dylibs_used); + DylibId = InputArgs.hasArg(OBJDUMP_dylib_id); + Verbose = !InputArgs.hasArg(OBJDUMP_non_verbose); + ObjcMetaData = InputArgs.hasArg(OBJDUMP_objc_meta_data); + DisSymName = InputArgs.getLastArgValue(OBJDUMP_dis_symname).str(); + SymbolicOperands = !InputArgs.hasArg(OBJDUMP_no_symbolic_operands); + ArchFlags = InputArgs.getAllArgValues(OBJDUMP_arch_EQ); +} + static const Target *GetTarget(const MachOObjectFile *MachOObj, const char **McpuDefault, const Target **ThumbTarget) { @@ -245,7 +184,7 @@ typedef std::pair<uint64_t, DiceRef> DiceTableEntry; typedef std::vector<DiceTableEntry> DiceTable; typedef DiceTable::iterator dice_table_iterator; -#ifdef HAVE_LIBXAR +#ifdef LLVM_HAVE_LIBXAR namespace { struct ScopedXarFile { xar_t xar; @@ -272,7 +211,7 @@ struct ScopedXarIter { operator xar_iter_t() { return iter; } }; } // namespace -#endif // defined(HAVE_LIBXAR) +#endif // defined(LLVM_HAVE_LIBXAR) // This is used to search for a data in code table entry for the PC being // disassembled. The j parameter has the PC in j.first. A single data in code @@ -296,19 +235,19 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, default: case MachO::DICE_KIND_DATA: if (Length >= 4) { - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 4), outs()); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; Size = 4; } else if (Length >= 2) { - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << Value; Size = 2; } else { - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[0]; outs() << "\t.byte " << Value; @@ -320,14 +259,14 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, outs() << "\t@ data in code kind = " << Kind << "\n"; break; case MachO::DICE_KIND_JUMP_TABLE8: - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 1), outs()); Value = bytes[0]; outs() << "\t.byte " << format("%3u", Value) << "\t@ KIND_JUMP_TABLE8\n"; Size = 1; break; case MachO::DICE_KIND_JUMP_TABLE16: - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << format("%5u", Value & 0xffff) @@ -336,7 +275,7 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, break; case MachO::DICE_KIND_JUMP_TABLE32: case MachO::DICE_KIND_ABS_JUMP_TABLE32: - if (!NoShowRawInsn) + if (ShowRawInsn) dumpBytes(makeArrayRef(bytes, 4), outs()); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; @@ -362,8 +301,7 @@ static void getSectionsAndSymbols(MachOObjectFile *MachOObj, Symbols.push_back(Symbol); } - for (const SectionRef &Section : MachOObj->sections()) - Sections.push_back(Section); + append_range(Sections, MachOObj->sections()); bool BaseSegmentAddressSet = false; for (const auto &Command : MachOObj->load_commands()) { @@ -461,7 +399,7 @@ static void printRelocationTargetName(const MachOObjectFile *O, if (isExtern) { symbol_iterator SI = O->symbol_begin(); - advance(SI, Val); + std::advance(SI, Val); S = unwrapOrError(SI->getName(), FileName); } else { section_iterator SI = O->section_begin(); @@ -473,7 +411,7 @@ static void printRelocationTargetName(const MachOObjectFile *O, uint32_t I = Val - 1; while (I != 0 && SI != O->section_end()) { --I; - advance(SI, 1); + std::advance(SI, 1); } if (SI == O->section_end()) { Fmt << Val << " (?,?)"; @@ -1104,6 +1042,43 @@ static void PrintRelocations(const MachOObjectFile *O, const bool verbose) { } } +static void PrintFunctionStarts(MachOObjectFile *O) { + uint64_t BaseSegmentAddress = 0; + for (const MachOObjectFile::LoadCommandInfo &Command : O->load_commands()) { + if (Command.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command SLC = O->getSegmentLoadCommand(Command); + if (StringRef(SLC.segname) == "__TEXT") { + BaseSegmentAddress = SLC.vmaddr; + break; + } + } else if (Command.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 SLC = O->getSegment64LoadCommand(Command); + if (StringRef(SLC.segname) == "__TEXT") { + BaseSegmentAddress = SLC.vmaddr; + break; + } + } + } + + SmallVector<uint64_t, 8> FunctionStarts; + for (const MachOObjectFile::LoadCommandInfo &LC : O->load_commands()) { + if (LC.C.cmd == MachO::LC_FUNCTION_STARTS) { + MachO::linkedit_data_command FunctionStartsLC = + O->getLinkeditDataLoadCommand(LC); + O->ReadULEB128s(FunctionStartsLC.dataoff, FunctionStarts); + break; + } + } + + for (uint64_t S : FunctionStarts) { + uint64_t Addr = BaseSegmentAddress + S; + if (O->is64Bit()) + outs() << format("%016" PRIx64, Addr) << "\n"; + else + outs() << format("%08" PRIx32, static_cast<uint32_t>(Addr)) << "\n"; + } +} + static void PrintDataInCodeTable(MachOObjectFile *O, bool verbose) { MachO::linkedit_data_command DIC = O->getDataInCodeLoadCommand(); uint32_t nentries = DIC.datasize / sizeof(struct MachO::data_in_code_entry); @@ -1260,6 +1235,16 @@ static void PrintDylibs(MachOObjectFile *O, bool JustId) { } } +static void printRpaths(MachOObjectFile *O) { + for (const auto &Command : O->load_commands()) { + if (Command.C.cmd == MachO::LC_RPATH) { + auto Rpath = O->getRpathCommand(Command); + const char *P = (const char *)(Command.Ptr) + Rpath.path; + outs() << P << "\n"; + } + } +} + typedef DenseMap<uint64_t, StringRef> SymbolAddressMap; static void CreateSymbolAddressMap(MachOObjectFile *O, @@ -1712,12 +1697,12 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, StringRef DisSegName, StringRef DisSectName); static void DumpProtocolSection(MachOObjectFile *O, const char *sect, uint32_t size, uint32_t addr); -#ifdef HAVE_LIBXAR +#ifdef LLVM_HAVE_LIBXAR static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, uint32_t size, bool verbose, bool PrintXarHeader, bool PrintXarFileHeaders, std::string XarMemberName); -#endif // defined(HAVE_LIBXAR) +#endif // defined(LLVM_HAVE_LIBXAR) static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, bool verbose) { @@ -1770,7 +1755,7 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, uint32_t sect_size = BytesStr.size(); uint64_t sect_addr = Section.getAddress(); - if (!NoLeadingHeaders) + if (LeadingHeaders) outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; @@ -1788,13 +1773,13 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, DumpProtocolSection(O, sect, sect_size, sect_addr); continue; } -#ifdef HAVE_LIBXAR +#ifdef LLVM_HAVE_LIBXAR if (SegName == "__LLVM" && SectName == "__bundle") { - DumpBitcodeSection(O, sect, sect_size, verbose, !NoSymbolicOperands, + DumpBitcodeSection(O, sect, sect_size, verbose, SymbolicOperands, ArchiveHeaders, ""); continue; } -#endif // defined(HAVE_LIBXAR) +#endif // defined(LLVM_HAVE_LIBXAR) switch (section_type) { case MachO::S_REGULAR: DumpRawSectionContents(O, sect, sect_size, sect_addr); @@ -1803,20 +1788,20 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, outs() << "zerofill section and has no contents in the file\n"; break; case MachO::S_CSTRING_LITERALS: - DumpCstringSection(O, sect, sect_size, sect_addr, !NoLeadingAddr); + DumpCstringSection(O, sect, sect_size, sect_addr, LeadingAddr); break; case MachO::S_4BYTE_LITERALS: - DumpLiteral4Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); + DumpLiteral4Section(O, sect, sect_size, sect_addr, LeadingAddr); break; case MachO::S_8BYTE_LITERALS: - DumpLiteral8Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); + DumpLiteral8Section(O, sect, sect_size, sect_addr, LeadingAddr); break; case MachO::S_16BYTE_LITERALS: - DumpLiteral16Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); + DumpLiteral16Section(O, sect, sect_size, sect_addr, LeadingAddr); break; case MachO::S_LITERAL_POINTERS: DumpLiteralPointerSection(O, Section, sect, sect_size, sect_addr, - !NoLeadingAddr); + LeadingAddr); break; case MachO::S_MOD_INIT_FUNC_POINTERS: case MachO::S_MOD_TERM_FUNC_POINTERS: @@ -1853,7 +1838,7 @@ static void DumpInfoPlistSectionContents(StringRef Filename, DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); if (SegName == "__TEXT" && SectName == "__info_plist") { - if (!NoLeadingHeaders) + if (LeadingHeaders) outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; StringRef BytesStr = unwrapOrError(Section.getContents(), O->getFileName()); @@ -1911,9 +1896,9 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, // UniversalHeaders or ArchiveHeaders. if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase || Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols || - DataInCode || LinkOptHints || DylibsUsed || DylibId || ObjcMetaData || - (!FilterSections.empty())) { - if (!NoLeadingHeaders) { + DataInCode || FunctionStarts || LinkOptHints || DylibsUsed || DylibId || + Rpaths || ObjcMetaData || (!FilterSections.empty())) { + if (LeadingHeaders) { outs() << Name; if (!ArchiveMemberName.empty()) outs() << '(' << ArchiveMemberName << ')'; @@ -1964,19 +1949,21 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, DisassembleMachO(FileName, MachOOF, "__TEXT", "__text"); } if (IndirectSymbols) - PrintIndirectSymbols(MachOOF, !NonVerbose); + PrintIndirectSymbols(MachOOF, Verbose); if (DataInCode) - PrintDataInCodeTable(MachOOF, !NonVerbose); + PrintDataInCodeTable(MachOOF, Verbose); + if (FunctionStarts) + PrintFunctionStarts(MachOOF); if (LinkOptHints) PrintLinkOptHints(MachOOF); if (Relocations) - PrintRelocations(MachOOF, !NonVerbose); + PrintRelocations(MachOOF, Verbose); if (SectionHeaders) printSectionHeaders(MachOOF); if (SectionContents) printSectionContents(MachOOF); if (!FilterSections.empty()) - DumpSectionContents(FileName, MachOOF, !NonVerbose); + DumpSectionContents(FileName, MachOOF, Verbose); if (InfoPlist) DumpInfoPlistSectionContents(FileName, MachOOF); if (DylibsUsed) @@ -1994,11 +1981,13 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, if (FirstPrivateHeader) printMachOFileHeader(MachOOF); if (ObjcMetaData) - printObjcMetaData(MachOOF, !NonVerbose); + printObjcMetaData(MachOOF, Verbose); if (ExportsTrie) printExportsTrie(MachOOF); if (Rebase) printRebaseTable(MachOOF); + if (Rpaths) + printRpaths(MachOOF); if (Bind) printBindTable(MachOOF); if (LazyBind) @@ -2333,7 +2322,7 @@ void objdump::parseInputMachO(StringRef Filename) { if (Archive *A = dyn_cast<Archive>(&Bin)) { outs() << "Archive : " << Filename << "\n"; if (ArchiveHeaders) - printArchiveHeaders(Filename, A, !NonVerbose, ArchiveMemberOffsets); + printArchiveHeaders(Filename, A, Verbose, ArchiveMemberOffsets); Error Err = Error::success(); unsigned I = -1; @@ -2380,7 +2369,7 @@ void objdump::parseInputMachO(MachOUniversalBinary *UB) { auto Filename = UB->getFileName(); if (UniversalHeaders) - printMachOUniversalHeaders(UB, !NonVerbose); + printMachOUniversalHeaders(UB, Verbose); // If we have a list of architecture flags specified dump only those. if (!ArchAll && !ArchFlags.empty()) { @@ -2414,7 +2403,7 @@ void objdump::parseInputMachO(MachOUniversalBinary *UB) { outs() << " (architecture " << ArchitectureName << ")"; outs() << "\n"; if (ArchiveHeaders) - printArchiveHeaders(Filename, A.get(), !NonVerbose, + printArchiveHeaders(Filename, A.get(), Verbose, ArchiveMemberOffsets, ArchitectureName); Error Err = Error::success(); unsigned I = -1; @@ -2475,7 +2464,7 @@ void objdump::parseInputMachO(MachOUniversalBinary *UB) { std::unique_ptr<Archive> &A = *AOrErr; outs() << "Archive : " << Filename << "\n"; if (ArchiveHeaders) - printArchiveHeaders(Filename, A.get(), !NonVerbose, + printArchiveHeaders(Filename, A.get(), Verbose, ArchiveMemberOffsets); Error Err = Error::success(); unsigned I = -1; @@ -2528,8 +2517,8 @@ void objdump::parseInputMachO(MachOUniversalBinary *UB) { outs() << " (architecture " << ArchitectureName << ")"; outs() << "\n"; if (ArchiveHeaders) - printArchiveHeaders(Filename, A.get(), !NonVerbose, - ArchiveMemberOffsets, ArchitectureName); + printArchiveHeaders(Filename, A.get(), Verbose, ArchiveMemberOffsets, + ArchitectureName); Error Err = Error::success(); unsigned I = -1; for (auto &C : A->children(Err)) { @@ -6115,8 +6104,7 @@ static void printObjc2_64bit_MetaData(MachOObjectFile *O, bool verbose) { CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) - Sections.push_back(Section); + append_range(Sections, O->sections()); struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); @@ -6197,8 +6185,7 @@ static void printObjc2_32bit_MetaData(MachOObjectFile *O, bool verbose) { CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) - Sections.push_back(Section); + append_range(Sections, O->sections()); struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); @@ -6292,8 +6279,7 @@ static bool printObjc1_32bit_MetaData(MachOObjectFile *O, bool verbose) { CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) - Sections.push_back(Section); + append_range(Sections, O->sections()); struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); @@ -6450,8 +6436,7 @@ static void DumpProtocolSection(MachOObjectFile *O, const char *sect, CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) - Sections.push_back(Section); + append_range(Sections, O->sections()); struct DisassembleInfo info(O, &AddrMap, &Sections, true); @@ -6475,7 +6460,7 @@ static void DumpProtocolSection(MachOObjectFile *O, const char *sect, } } -#ifdef HAVE_LIBXAR +#ifdef LLVM_HAVE_LIBXAR static inline void swapStruct(struct xar_header &xar) { sys::swapByteOrder(xar.magic); sys::swapByteOrder(xar.size); @@ -6838,7 +6823,7 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, } } } -#endif // defined(HAVE_LIBXAR) +#endif // defined(LLVM_HAVE_LIBXAR) static void printObjcMetaData(MachOObjectFile *O, bool verbose) { if (O->is64Bit()) @@ -7165,17 +7150,15 @@ static void emitComments(raw_svector_ostream &CommentStream, // Get the default information for printing a comment. StringRef CommentBegin = MAI.getCommentString(); unsigned CommentColumn = MAI.getCommentColumn(); - bool IsFirst = true; + ListSeparator LS("\n"); while (!Comments.empty()) { - if (!IsFirst) - FormattedOS << '\n'; + FormattedOS << LS; // Emit a line of comments. FormattedOS.PadToColumn(CommentColumn); size_t Position = Comments.find('\n'); FormattedOS << CommentBegin << ' ' << Comments.substr(0, Position); // Move after the newline character. Comments = Comments.substr(Position + 1); - IsFirst = false; } FormattedOS.flush(); @@ -7245,7 +7228,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, std::unique_ptr<const MCSubtargetInfo> STI( TheTarget->createMCSubtargetInfo(TripleName, MachOMCPU, FeaturesStr)); CHECK_TARGET_INFO_CREATION(STI); - MCContext Ctx(AsmInfo.get(), MRI.get(), nullptr); + MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get()); std::unique_ptr<MCDisassembler> DisAsm( TheTarget->createMCDisassembler(*STI, Ctx)); CHECK_TARGET_INFO_CREATION(DisAsm); @@ -7295,7 +7278,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, ThumbTarget->createMCSubtargetInfo(ThumbTripleName, MachOMCPU, FeaturesStr)); CHECK_THUMB_TARGET_INFO_CREATION(ThumbSTI); - ThumbCtx.reset(new MCContext(ThumbAsmInfo.get(), ThumbMRI.get(), nullptr)); + ThumbCtx.reset(new MCContext(Triple(ThumbTripleName), ThumbAsmInfo.get(), + ThumbMRI.get(), ThumbSTI.get())); ThumbDisAsm.reset(ThumbTarget->createMCDisassembler(*ThumbSTI, *ThumbCtx)); CHECK_THUMB_TARGET_INFO_CREATION(ThumbDisAsm); MCContext *PtrThumbCtx = ThumbCtx.get(); @@ -7334,7 +7318,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, BaseSegmentAddress); // Sort the symbols by address, just in case they didn't come in that way. - llvm::sort(Symbols, SymbolSorter()); + llvm::stable_sort(Symbols, SymbolSorter()); // Build a data in code table that is sorted on by the address of each entry. uint64_t BaseAddress = 0; @@ -7489,13 +7473,13 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, return; } // Set up the block of info used by the Symbolizer call backs. - SymbolizerInfo.verbose = !NoSymbolicOperands; + SymbolizerInfo.verbose = SymbolicOperands; SymbolizerInfo.O = MachOOF; SymbolizerInfo.S = Sections[SectIdx]; SymbolizerInfo.AddrMap = &AddrMap; SymbolizerInfo.Sections = &Sections; // Same for the ThumbSymbolizer - ThumbSymbolizerInfo.verbose = !NoSymbolicOperands; + ThumbSymbolizerInfo.verbose = SymbolicOperands; ThumbSymbolizerInfo.O = MachOOF; ThumbSymbolizerInfo.S = Sections[SectIdx]; ThumbSymbolizerInfo.AddrMap = &AddrMap; @@ -7620,7 +7604,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, outs() << SymName << ":\n"; uint64_t PC = SectAddress + Index; - if (!NoLeadingAddr) { + if (LeadingAddr) { if (FullLeadingAddr) { if (MachOOF->is64Bit()) outs() << format("%016" PRIx64, PC); @@ -7630,7 +7614,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, outs() << format("%8" PRIx64 ":", PC); } } - if (!NoShowRawInsn || Arch == Triple::arm) + if (ShowRawInsn || Arch == Triple::arm) outs() << "\t"; if (DumpAndSkipDataInCode(PC, Bytes.data() + Index, Dices, Size)) @@ -7647,7 +7631,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, gotInst = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), PC, Annotations); if (gotInst) { - if (!NoShowRawInsn || Arch == Triple::arm) { + if (ShowRawInsn || Arch == Triple::arm) { dumpBytes(makeArrayRef(Bytes.data() + Index, Size), outs()); } formatted_raw_ostream FormattedOS(outs()); @@ -7717,7 +7701,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, raw_svector_ostream Annotations(AnnotationsBytes); if (DisAsm->getInstruction(Inst, InstSize, Bytes.slice(Index), PC, Annotations)) { - if (!NoLeadingAddr) { + if (LeadingAddr) { if (FullLeadingAddr) { if (MachOOF->is64Bit()) outs() << format("%016" PRIx64, PC); @@ -7727,7 +7711,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, outs() << format("%8" PRIx64 ":", PC); } } - if (!NoShowRawInsn || Arch == Triple::arm) { + if (ShowRawInsn || Arch == Triple::arm) { outs() << "\t"; dumpBytes(makeArrayRef(Bytes.data() + Index, InstSize), outs()); } @@ -8012,12 +7996,23 @@ static void printCompressedSecondLevelUnwindPage( (void)Kind; assert(Kind == 3 && "kind for a compressed 2nd level index should be 3"); + uint32_t NumCommonEncodings = CommonEncodings.size(); uint16_t EntriesStart = readNext<uint16_t>(PageData, Pos); uint16_t NumEntries = readNext<uint16_t>(PageData, Pos); - uint16_t EncodingsStart = readNext<uint16_t>(PageData, Pos); - readNext<uint16_t>(PageData, Pos); - StringRef PageEncodings = PageData.substr(EncodingsStart, StringRef::npos); + uint16_t PageEncodingsStart = readNext<uint16_t>(PageData, Pos); + uint16_t NumPageEncodings = readNext<uint16_t>(PageData, Pos); + SmallVector<uint32_t, 64> PageEncodings; + if (NumPageEncodings) { + outs() << " Page encodings: (count = " << NumPageEncodings << ")\n"; + Pos = PageEncodingsStart; + for (unsigned i = 0; i < NumPageEncodings; ++i) { + uint32_t Encoding = readNext<uint32_t>(PageData, Pos); + PageEncodings.push_back(Encoding); + outs() << " encoding[" << (i + NumCommonEncodings) + << "]: " << format("0x%08" PRIx32, Encoding) << '\n'; + } + } Pos = EntriesStart; for (unsigned i = 0; i < NumEntries; ++i) { @@ -8026,12 +8021,10 @@ static void printCompressedSecondLevelUnwindPage( uint32_t EncodingIdx = Entry >> 24; uint32_t Encoding; - if (EncodingIdx < CommonEncodings.size()) + if (EncodingIdx < NumCommonEncodings) Encoding = CommonEncodings[EncodingIdx]; else - Encoding = read<uint32_t>(PageEncodings, - sizeof(uint32_t) * - (EncodingIdx - CommonEncodings.size())); + Encoding = PageEncodings[EncodingIdx - NumCommonEncodings]; outs() << " [" << i << "]: " << "function offset=" << format("0x%08" PRIx32, FunctionOffset) @@ -10233,7 +10226,7 @@ static void PrintMachHeader(const MachOObjectFile *Obj, bool verbose) { void objdump::printMachOFileHeader(const object::ObjectFile *Obj) { const MachOObjectFile *file = dyn_cast<const MachOObjectFile>(Obj); - PrintMachHeader(file, !NonVerbose); + PrintMachHeader(file, Verbose); } void objdump::printMachOLoadCommands(const object::ObjectFile *Obj) { @@ -10251,7 +10244,7 @@ void objdump::printMachOLoadCommands(const object::ObjectFile *Obj) { filetype = H.filetype; cputype = H.cputype; } - PrintLoadCommands(file, filetype, cputype, !NonVerbose); + PrintLoadCommands(file, filetype, cputype, Verbose); } //===----------------------------------------------------------------------===// @@ -10292,30 +10285,16 @@ static void printMachOExportsTrie(const object::MachOObjectFile *Obj) { Entry.address() + BaseSegmentAddress); outs() << Entry.name(); if (WeakDef || ThreadLocal || Resolver || Abs) { - bool NeedsComma = false; + ListSeparator LS; outs() << " ["; - if (WeakDef) { - outs() << "weak_def"; - NeedsComma = true; - } - if (ThreadLocal) { - if (NeedsComma) - outs() << ", "; - outs() << "per-thread"; - NeedsComma = true; - } - if (Abs) { - if (NeedsComma) - outs() << ", "; - outs() << "absolute"; - NeedsComma = true; - } - if (Resolver) { - if (NeedsComma) - outs() << ", "; - outs() << format("resolver=0x%08llX", Entry.other()); - NeedsComma = true; - } + if (WeakDef) + outs() << LS << "weak_def"; + if (ThreadLocal) + outs() << LS << "per-thread"; + if (Abs) + outs() << LS << "absolute"; + if (Resolver) + outs() << LS << format("resolver=0x%08llX", Entry.other()); outs() << "]"; } if (ReExport) { @@ -10486,7 +10465,7 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, } void objdump::printLazyBindTable(ObjectFile *o) { - outs() << "Lazy bind table:\n"; + outs() << "\nLazy bind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOLazyBindTable(MachO); else @@ -10496,7 +10475,7 @@ void objdump::printLazyBindTable(ObjectFile *o) { } void objdump::printWeakBindTable(ObjectFile *o) { - outs() << "Weak bind table:\n"; + outs() << "\nWeak bind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOWeakBindTable(MachO); else @@ -10506,7 +10485,7 @@ void objdump::printWeakBindTable(ObjectFile *o) { } void objdump::printExportsTrie(const ObjectFile *o) { - outs() << "Exports trie:\n"; + outs() << "\nExports trie:\n"; if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOExportsTrie(MachO); else @@ -10516,7 +10495,7 @@ void objdump::printExportsTrie(const ObjectFile *o) { } void objdump::printRebaseTable(ObjectFile *o) { - outs() << "Rebase table:\n"; + outs() << "\nRebase table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachORebaseTable(MachO); else @@ -10526,7 +10505,7 @@ void objdump::printRebaseTable(ObjectFile *o) { } void objdump::printBindTable(ObjectFile *o) { - outs() << "Bind table:\n"; + outs() << "\nBind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOBindTable(MachO); else diff --git a/llvm/tools/llvm-objdump/MachODump.h b/llvm/tools/llvm-objdump/MachODump.h index adf6c3404f79..7568062bd6b0 100644 --- a/llvm/tools/llvm-objdump/MachODump.h +++ b/llvm/tools/llvm-objdump/MachODump.h @@ -24,24 +24,36 @@ class ObjectFile; class RelocationRef; } // namespace object +namespace opt { +class InputArgList; +} // namespace opt + namespace objdump { +void parseMachOOptions(const llvm::opt::InputArgList &InputArgs); + // MachO specific options -extern cl::OptionCategory MachOCat; -extern cl::opt<bool> Bind; -extern cl::opt<bool> DataInCode; -extern cl::opt<bool> DylibsUsed; -extern cl::opt<bool> DylibId; -extern cl::opt<bool> ExportsTrie; -extern cl::opt<bool> FirstPrivateHeader; -extern cl::opt<bool> IndirectSymbols; -extern cl::opt<bool> InfoPlist; -extern cl::opt<bool> LazyBind; -extern cl::opt<bool> LinkOptHints; -extern cl::opt<bool> ObjcMetaData; -extern cl::opt<bool> Rebase; -extern cl::opt<bool> UniversalHeaders; -extern cl::opt<bool> WeakBind; +extern bool Bind; +extern bool DataInCode; +extern std::string DisSymName; +extern bool DylibId; +extern bool DylibsUsed; +extern bool ExportsTrie; +extern bool FirstPrivateHeader; +extern bool FullLeadingAddr; +extern bool FunctionStarts; +extern bool IndirectSymbols; +extern bool InfoPlist; +extern bool LazyBind; +extern bool LeadingHeaders; +extern bool LinkOptHints; +extern bool ObjcMetaData; +extern bool Rebase; +extern bool Rpaths; +extern bool SymbolicOperands; +extern bool UniversalHeaders; +extern bool Verbose; +extern bool WeakBind; Error getMachORelocationValueString(const object::MachOObjectFile *Obj, const object::RelocationRef &RelRef, diff --git a/llvm/tools/llvm-objdump/ObjdumpOptID.h b/llvm/tools/llvm-objdump/ObjdumpOptID.h new file mode 100644 index 000000000000..65f6c60ad884 --- /dev/null +++ b/llvm/tools/llvm-objdump/ObjdumpOptID.h @@ -0,0 +1,13 @@ +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H +#define LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H + +enum ObjdumpOptID { + OBJDUMP_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OBJDUMP_##ID, +#include "ObjdumpOpts.inc" +#undef OPTION +}; + +#endif // LLVM_TOOLS_LLVM_OBJDUMP_OBJDUMP_OPT_ID_H diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td new file mode 100644 index 000000000000..1b19733c65d0 --- /dev/null +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -0,0 +1,323 @@ +include "llvm/Option/OptParser.td" + +def help : Flag<["--"], "help">, + HelpText<"Display available options (--help-hidden for more)">; + +def help_hidden : Flag<["--"], "help-hidden">, + Flags<[HelpHidden]>, + HelpText<"Display all available options">; + +def version : Flag<["--"], "version">, + HelpText<"Display the version of this program">; +def : Flag<["-"], "v">, Alias<version>, HelpText<"Alias for --version">; + +def adjust_vma_EQ : Joined<["--"], "adjust-vma=">, + MetaVarName<"offset">, + HelpText<"Increase the displayed address by the specified offset">; + +def all_headers : Flag<["--"], "all-headers">, + HelpText<"Display all available header information">; +def : Flag<["-"], "x">, Alias<all_headers>, HelpText<"Alias for --all-headers">; + +def arch_name_EQ : Joined<["--"], "arch-name=">, + HelpText<"Target arch to disassemble for, " + "see --version for available targets">; +def archive_headers : Flag<["--"], "archive-headers">, + HelpText<"Display archive header information">; + +def : Flag<["-"], "a">, Alias<archive_headers>, + HelpText<"Alias for --archive-headers">; + +def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">; +def : Flag<["-"], "C">, Alias<demangle>, HelpText<"Alias for --demangle">; + +def disassemble : Flag<["--"], "disassemble">, + HelpText<"Display assembler mnemonics for the machine instructions">; +def : Flag<["-"], "d">, Alias<disassemble>, HelpText<"Alias for --disassemble">; + +def disassemble_all : Flag<["--"], "disassemble-all">, + HelpText<"Display assembler mnemonics for the machine instructions">; +def : Flag<["-"], "D">, Alias<disassemble_all>, + HelpText<"Alias for --disassemble-all">; + +def symbol_description : Flag<["--"], "symbol-description">, + HelpText<"Add symbol description for disassembly. This " + "option is for XCOFF files only.">; + +def disassemble_symbols_EQ : Joined<["--"], "disassemble-symbols=">, + HelpText<"List of symbols to disassemble. " + "Accept demangled names when --demangle is " + "specified, otherwise accept mangled names">; + +def disassemble_zeroes : Flag<["--"], "disassemble-zeroes">, + HelpText<"Do not skip blocks of zeroes when disassembling">; +def : Flag<["-"], "z">, Alias<disassemble_zeroes>, + HelpText<"Alias for --disassemble-zeroes">; + +def disassembler_options_EQ : Joined<["--"], "disassembler-options=">, + MetaVarName<"options">, + HelpText<"Pass target specific disassembler options">; +def : JoinedOrSeparate<["-"], "M">, Alias<disassembler_options_EQ>, + HelpText<"Alias for --disassembler-options=">; + +def dynamic_reloc : Flag<["--"], "dynamic-reloc">, + HelpText<"Display the dynamic relocation entries in the file">; +def : Flag<["-"], "R">, Alias<dynamic_reloc>, + HelpText<"Alias for --dynamic-reloc">; + +def dwarf_EQ : Joined<["--"], "dwarf=">, + HelpText<"Dump of dwarf debug sections">, Values<"frames">; + +def fault_map_section : Flag<["--"], "fault-map-section">, + HelpText<"Display contents of faultmap section">; + +def file_headers : Flag<["--"], "file-headers">, + HelpText<"Display the contents of the overall file header">; +def : Flag<["-"], "f">, Alias<file_headers>, + HelpText<"Alias for --file-headers">; + +def full_contents : Flag<["--"], "full-contents">, + HelpText<"Display the content of each section">; +def : Flag<["-"], "s">, Alias<full_contents>, + HelpText<"Alias for --full-contents">; + +def line_numbers : Flag<["--"], "line-numbers">, + HelpText<"Display source line numbers with " + "disassembly. Implies disassemble object">; +def : Flag<["-"], "l">, Alias<line_numbers>, + HelpText<"Alias for --line-numbers">; + +def macho : Flag<["--"], "macho">, + HelpText<"Use MachO specific object file parser">; +def : Flag<["-"], "m">, Alias<macho>, HelpText<"Alias for --macho">; + +def mcpu_EQ : Joined<["--"], "mcpu=">, + MetaVarName<"cpu-name">, + HelpText<"Target a specific cpu type (--mcpu=help for details)">; + +def mattr_EQ : Joined<["--"], "mattr=">, + MetaVarName<"a1,+a2,-a3,...">, + HelpText<"Target specific attributes (--mattr=help for details)">; + +def no_show_raw_insn : Flag<["--"], "no-show-raw-insn">, + HelpText<"When disassembling instructions, " + "do not print the instruction bytes.">; + +def no_leading_addr : Flag<["--"], "no-leading-addr">, + HelpText<"Print no leading address">; + +def raw_clang_ast : Flag<["--"], "raw-clang-ast">, + HelpText<"Dump the raw binary contents of the clang AST section">; + +def reloc : Flag<["--"], "reloc">, + HelpText<"Display the relocation entries in the file">; +def : Flag<["-"], "r">, Alias<reloc>, HelpText<"Alias for --reloc">; + +def print_imm_hex : Flag<["--"], "print-imm-hex">, + HelpText<"Use hex format for immediate values">; + +def no_print_imm_hex : Flag<["--"], "no-print-imm-hex">, + HelpText<"Do not use hex format for immediate values (default)">; +def : Flag<["--"], "print-imm-hex=false">, Alias<no_print_imm_hex>; + +def private_headers : Flag<["--"], "private-headers">, + HelpText<"Display format specific file headers">; +def : Flag<["-"], "p">, Alias<private_headers>, + HelpText<"Alias for --private-headers">; + +def section_EQ : Joined<["--"], "section=">, + HelpText<"Operate on the specified sections only. " + "With --macho dump segment,section">; +def : Separate<["--"], "section">, Alias<section_EQ>; +def : JoinedOrSeparate<["-"], "j">, Alias<section_EQ>, + HelpText<"Alias for --section">; + +def section_headers : Flag<["--"], "section-headers">, + HelpText<"Display summaries of the headers for each section.">; +def : Flag<["--"], "headers">, Alias<section_headers>, + HelpText<"Alias for --section-headers">; +def : Flag<["-"], "h">, Alias<section_headers>, + HelpText<"Alias for --section-headers">; + +def show_lma : Flag<["--"], "show-lma">, + HelpText<"Display LMA column when dumping ELF section headers">; + +def source : Flag<["--"], "source">, + HelpText<"Display source inlined with disassembly. Implies disassemble object">; +def : Flag<["-"], "S">, Alias<source>, HelpText<"Alias for --source">; + +def start_address_EQ : Joined<["--"], "start-address=">, + MetaVarName<"address">, + HelpText<"Disassemble beginning at address">; +def stop_address_EQ : Joined<["--"], "stop-address=">, + MetaVarName<"address">, + HelpText<"Stop disassembly at address">; + +def syms : Flag<["--"], "syms">, + HelpText<"Display the symbol table">; +def : Flag<["-"], "t">, Alias<syms>, HelpText<"Alias for --syms">; + +def symbolize_operands : Flag<["--"], "symbolize-operands">, + HelpText<"Symbolize instruction operands when disassembling">; + +def dynamic_syms : Flag<["--"], "dynamic-syms">, + HelpText<"Display the contents of the dynamic symbol table">; +def : Flag<["-"], "T">, Alias<dynamic_syms>, + HelpText<"Alias for --dynamic-syms">; + +def triple_EQ : Joined<["--"], "triple=">, + HelpText<"Target triple to disassemble for, " + "see --version for available targets">; +def : Separate<["--"], "triple">, + Alias<triple_EQ>; + +def unwind_info : Flag<["--"], "unwind-info">, + HelpText<"Display unwind information">; +def : Flag<["-"], "u">, Alias<unwind_info>, + HelpText<"Alias for --unwind-info">; + +def wide : Flag<["--"], "wide">, + HelpText<"Ignored for compatibility with GNU objdump">; +def : Flag<["-"], "w">, Alias<wide>; + +def prefix : Separate<["--"], "prefix">, + HelpText<"Add prefix to absolute paths">; + +def prefix_strip : Separate<["--"], "prefix-strip">, + HelpText<"Strip out initial directories from absolute " + "paths. No effect without --prefix">; + +def debug_vars_EQ : Joined<["--"], "debug-vars=">, + Values<"unicode,ascii">; +def : Flag<["--"], "debug-vars">, + HelpText<"Print the locations (in registers or memory) of " + "source-level variables alongside disassembly">, + Alias<debug_vars_EQ>, AliasArgs<["unicode"]>; + +def debug_vars_indent_EQ : Joined<["--"], "debug-vars-indent=">, + HelpText<"Distance to indent the source-level variable display, " + "relative to the start of the disassembly">; + +def x86_asm_syntax_att : Flag<["--"], "x86-asm-syntax=att">, + HelpText<"Emit AT&T-style disassembly">; + +def x86_asm_syntax_intel : Flag<["--"], "x86-asm-syntax=intel">, + HelpText<"Emit Intel-style disassembly">; + + +def grp_mach_o : OptionGroup<"kind">, HelpText<"llvm-objdump MachO Specific Options">; + +def private_header : Flag<["--"], "private-header">, + HelpText<"Display only the first format specific file header">, + Group<grp_mach_o>; + +def exports_trie : Flag<["--"], "exports-trie">, + HelpText<"Display mach-o exported symbols">, + Group<grp_mach_o>; + +def rebase : Flag<["--"], "rebase">, + HelpText<"Display mach-o rebasing info">, + Group<grp_mach_o>; + +def bind : Flag<["--"], "bind">, + HelpText<"Display mach-o binding info">, + Group<grp_mach_o>; + +def lazy_bind : Flag<["--"], "lazy-bind">, + HelpText<"Display mach-o lazy binding info">, + Group<grp_mach_o>; + +def weak_bind : Flag<["--"], "weak-bind">, + HelpText<"Display mach-o weak binding info">, + Group<grp_mach_o>; + +def g : Flag<["-"], "g">, + HelpText<"Print line information from debug info if available">, + Group<grp_mach_o>; + +def dsym_EQ : Joined<["--"], "dsym=">, + HelpText<"Use .dSYM file for debug info">, + Group<grp_mach_o>; +def : Separate<["--"], "dsym">, + Alias<dsym_EQ>, + Group<grp_mach_o>; + +def full_leading_addr : Flag<["--"], "full-leading-addr">, + HelpText<"Print full leading address">, + Group<grp_mach_o>; + +def no_leading_headers : Flag<["--"], "no-leading-headers">, + HelpText<"Print no leading headers">, + Group<grp_mach_o>; + +def universal_headers : Flag<["--"], "universal-headers">, + HelpText<"Print Mach-O universal headers (requires --macho)">, + Group<grp_mach_o>; + +def archive_member_offsets : Flag<["--"], "archive-member-offsets">, + HelpText<"Print the offset to each archive member for Mach-O archives " + "(requires --macho and --archive-headers)">, + Group<grp_mach_o>; + +def indirect_symbols : Flag<["--"], "indirect-symbols">, + HelpText<"Print indirect symbol table for Mach-O objects (requires --macho)">, + Group<grp_mach_o>; + +def data_in_code : Flag<["--"], "data-in-code">, + HelpText<"Print the data in code table for Mach-O objects (requires --macho)">, + Group<grp_mach_o>; + +def function_starts : Flag<["--"], "function-starts">, + HelpText<"Print the function starts table for " + "Mach-O objects (requires --macho)">, + Group<grp_mach_o>; + +def link_opt_hints : Flag<["--"], "link-opt-hints">, + HelpText<"Print the linker optimization hints for " + "Mach-O objects (requires --macho)">, + Group<grp_mach_o>; + +def info_plist : Flag<["--"], "info-plist">, + HelpText<"Print the info plist section as strings for " + "Mach-O objects (requires --macho)">, + Group<grp_mach_o>; + +def dylibs_used : Flag<["--"], "dylibs-used">, + HelpText<"Print the shared libraries used for linked " + "Mach-O files (requires --macho)">, + Group<grp_mach_o>; + +def dylib_id : Flag<["--"], "dylib-id">, + HelpText<"Print the shared library's id for the " + "dylib Mach-O file (requires --macho)">, + Group<grp_mach_o>; + +def rpaths : Flag<["--"], "rpaths">, + HelpText<"Print the runtime search paths for the " + "Mach-O file (requires --macho)">, + Group<grp_mach_o>; + +def non_verbose : Flag<["--"], "non-verbose">, + HelpText<"Print the info for Mach-O objects in non-verbose or " + "numeric form (requires --macho)">, + Group<grp_mach_o>; + +def objc_meta_data : Flag<["--"], "objc-meta-data">, + HelpText<"Print the Objective-C runtime meta data " + "for Mach-O files (requires --macho)">, + Group<grp_mach_o>; + +def dis_symname : Separate<["--"], "dis-symname">, + HelpText<"disassemble just this symbol's instructions (requires --macho)">, + Group<grp_mach_o>; + +def no_symbolic_operands : Flag<["--"], "no-symbolic-operands">, + HelpText<"do not symbolic operands when disassembling (requires --macho)">, + Group<grp_mach_o>; + +def arch_EQ : Joined<["--"], "arch=">, + HelpText<"architecture(s) from a Mach-O file to dump">, + Group<grp_mach_o>; +def : Separate<["--"], "arch">, + Alias<arch_EQ>, + Group<grp_mach_o>; diff --git a/llvm/tools/llvm-objdump/OtoolOpts.td b/llvm/tools/llvm-objdump/OtoolOpts.td new file mode 100644 index 000000000000..61ea701ed75d --- /dev/null +++ b/llvm/tools/llvm-objdump/OtoolOpts.td @@ -0,0 +1,68 @@ +include "llvm/Option/OptParser.td" + +def help : Flag<["--"], "help">, HelpText<"print help">; +def help_hidden : Flag<["--"], "help-hidden">, + HelpText<"print help for hidden flags">; + +def arch : Separate<["-"], "arch">, + HelpText<"select slice of universal Mach-O file">; +def C : Flag<["-"], "C">, HelpText<"print linker optimization hints">; +def d : Flag<["-"], "d">, HelpText<"print data section">; +def D : Flag<["-"], "D">, HelpText<"print shared library id">; +def f : Flag<["-"], "f">, HelpText<"print universal headers">; +def G : Flag<["-"], "G">, HelpText<"print data-in-code table">; +def h : Flag<["-"], "h">, HelpText<"print mach header">; +def I : Flag<["-"], "I">, HelpText<"print indirect symbol table">; +def j : Flag<["-"], "j">, HelpText<"print opcode bytes">; +def l : Flag<["-"], "l">, HelpText<"print load commnads">; +def L : Flag<["-"], "L">, HelpText<"print used shared libraries">; +def mcpu_EQ : Joined<["-"], "mcpu=">, HelpText<"select cpu for disassembly">; +def o : Flag<["-"], "o">, HelpText<"print Objective-C segment">; +def p : Separate<["-"], "p">, + MetaVarName<"<function name>">, + HelpText<"start disassembly at <function name>">; +def P : Flag<["-"], "P">, HelpText<"print __TEXT,__info_plist section as strings">; +def : Flag<["-"], "q">, Flags<[HelpHidden]>, + HelpText<"use LLVM's disassembler (default)">; +def r : Flag<["-"], "r">, HelpText<"print relocation entries">; +def s : MultiArg<["-"], "s", 2>, + MetaVarName<"<segname> <sectname>">, + HelpText<"print contents of section">; +def t : Flag<["-"], "t">, HelpText<"print text section">; +def version : Flag<["--"], "version">, HelpText<"print version">; +def v : Flag<["-"], "v">, + HelpText<"verbose output / disassemble when printing text sections">; +def V : Flag<["-"], "V">, + HelpText<"symbolize disassembled operands (implies -v)">; +def x : Flag<["-"], "x">, HelpText<"print all text sections">; +def X : Flag<["-"], "X">, HelpText<"omit leading addresses or headers">; + +// Not (yet?) implemented: +// def a : Flag<["-"], "a">, HelpText<"print archive header">; +// -c print argument strings of a core file +// -m don't use archive(member) syntax +// -dyld_info +// -dyld_opcodes +// -chained_fixups +// -addr_slide=arg +// -function_offsets + + +// Obsolete and unsupported: +def grp_obsolete : OptionGroup<"kind">, + HelpText<"Obsolete and unsupported flags">; + +def : Flag<["-"], "B">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"force Thum disassembly (ARM 32-bit objects only)">; +def : Flag<["-"], "H">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"print two-level hints table">; +def : Flag<["-"], "M">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"print module table of shared library">; +def : Flag<["-"], "R">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"print reference table of shared library">; +def : Flag<["-"], "S">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"print table of contents of library">; +def : Flag<["-"], "T">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"print table of contents of shared library">; +def : Flag<["-"], "Q">, Flags<[HelpHidden]>, Group<grp_obsolete>, + HelpText<"llvm-otool cannot use otool-classic's disassembler">; diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp new file mode 100644 index 000000000000..8befac546204 --- /dev/null +++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp @@ -0,0 +1,483 @@ +//===-- SourcePrinter.cpp - source interleaving utilities ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the LiveVariablePrinter and SourcePrinter classes to +// keep track of DWARF info as the current address is updated, and print out the +// source file line and variable liveness as needed. +// +//===----------------------------------------------------------------------===// + +#include "SourcePrinter.h" +#include "llvm-objdump.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/FormatVariadic.h" + +#define DEBUG_TYPE "objdump" + +namespace llvm { +namespace objdump { + +unsigned getInstStartColumn(const MCSubtargetInfo &STI) { + return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; +} + +bool LiveVariable::liveAtAddress(object::SectionedAddress Addr) { + if (LocExpr.Range == None) + return false; + return LocExpr.Range->SectionIndex == Addr.SectionIndex && + LocExpr.Range->LowPC <= Addr.Address && + LocExpr.Range->HighPC > Addr.Address; +} + +void LiveVariable::print(raw_ostream &OS, const MCRegisterInfo &MRI) const { + DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()}, + Unit->getContext().isLittleEndian(), 0); + DWARFExpression Expression(Data, Unit->getAddressByteSize()); + Expression.printCompact(OS, MRI); +} + +void LiveVariablePrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) { + uint64_t FuncLowPC, FuncHighPC, SectionIndex; + FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex); + const char *VarName = VarDie.getName(DINameKind::ShortName); + DWARFUnit *U = VarDie.getDwarfUnit(); + + Expected<DWARFLocationExpressionsVector> Locs = + VarDie.getLocations(dwarf::DW_AT_location); + if (!Locs) { + // If the variable doesn't have any locations, just ignore it. We don't + // report an error or warning here as that could be noisy on optimised + // code. + consumeError(Locs.takeError()); + return; + } + + for (const DWARFLocationExpression &LocExpr : *Locs) { + if (LocExpr.Range) { + LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie); + } else { + // If the LocExpr does not have an associated range, it is valid for + // the whole of the function. + // TODO: technically it is not valid for any range covered by another + // LocExpr, does that happen in reality? + DWARFLocationExpression WholeFuncExpr{ + DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), LocExpr.Expr}; + LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie); + } + } +} + +void LiveVariablePrinter::addFunction(DWARFDie D) { + for (const DWARFDie &Child : D.children()) { + if (Child.getTag() == dwarf::DW_TAG_variable || + Child.getTag() == dwarf::DW_TAG_formal_parameter) + addVariable(D, Child); + else + addFunction(Child); + } +} + +// Get the column number (in characters) at which the first live variable +// line should be printed. +unsigned LiveVariablePrinter::getIndentLevel() const { + return DbgIndent + getInstStartColumn(STI); +} + +// Indent to the first live-range column to the right of the currently +// printed line, and return the index of that column. +// TODO: formatted_raw_ostream uses "column" to mean a number of characters +// since the last \n, and we use it to mean the number of slots in which we +// put live variable lines. Pick a less overloaded word. +unsigned LiveVariablePrinter::moveToFirstVarColumn(formatted_raw_ostream &OS) { + // Logical column number: column zero is the first column we print in, each + // logical column is 2 physical columns wide. + unsigned FirstUnprintedLogicalColumn = + std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0); + // Physical column number: the actual column number in characters, with + // zero being the left-most side of the screen. + unsigned FirstUnprintedPhysicalColumn = + getIndentLevel() + FirstUnprintedLogicalColumn * 2; + + if (FirstUnprintedPhysicalColumn > OS.getColumn()) + OS.PadToColumn(FirstUnprintedPhysicalColumn); + + return FirstUnprintedLogicalColumn; +} + +unsigned LiveVariablePrinter::findFreeColumn() { + for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx) + if (!ActiveCols[ColIdx].isActive()) + return ColIdx; + + size_t OldSize = ActiveCols.size(); + ActiveCols.grow(std::max<size_t>(OldSize * 2, 1)); + return OldSize; +} + +void LiveVariablePrinter::dump() const { + for (const LiveVariable &LV : LiveVariables) { + dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": "; + LV.print(dbgs(), MRI); + dbgs() << "\n"; + } +} + +void LiveVariablePrinter::addCompileUnit(DWARFDie D) { + if (D.getTag() == dwarf::DW_TAG_subprogram) + addFunction(D); + else + for (const DWARFDie &Child : D.children()) + addFunction(Child); +} + +/// Update to match the state of the instruction between ThisAddr and +/// NextAddr. In the common case, any live range active at ThisAddr is +/// live-in to the instruction, and any live range active at NextAddr is +/// live-out of the instruction. If IncludeDefinedVars is false, then live +/// ranges starting at NextAddr will be ignored. +void LiveVariablePrinter::update(object::SectionedAddress ThisAddr, + object::SectionedAddress NextAddr, + bool IncludeDefinedVars) { + // First, check variables which have already been assigned a column, so + // that we don't change their order. + SmallSet<unsigned, 8> CheckedVarIdxs; + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { + if (!ActiveCols[ColIdx].isActive()) + continue; + CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx); + LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx]; + ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr); + ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr); + LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-" + << NextAddr.Address << ", " << LV.VarName << ", Col " + << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn + << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n"); + + if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) + ActiveCols[ColIdx].VarIdx = Column::NullVarIdx; + } + + // Next, look for variables which don't already have a column, but which + // are now live. + if (IncludeDefinedVars) { + for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End; + ++VarIdx) { + if (CheckedVarIdxs.count(VarIdx)) + continue; + LiveVariable &LV = LiveVariables[VarIdx]; + bool LiveIn = LV.liveAtAddress(ThisAddr); + bool LiveOut = LV.liveAtAddress(NextAddr); + if (!LiveIn && !LiveOut) + continue; + + unsigned ColIdx = findFreeColumn(); + LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-" + << NextAddr.Address << ", " << LV.VarName << ", Col " + << ColIdx << ": LiveIn=" << LiveIn + << ", LiveOut=" << LiveOut << "\n"); + ActiveCols[ColIdx].VarIdx = VarIdx; + ActiveCols[ColIdx].LiveIn = LiveIn; + ActiveCols[ColIdx].LiveOut = LiveOut; + ActiveCols[ColIdx].MustDrawLabel = true; + } + } +} + +enum class LineChar { + RangeStart, + RangeMid, + RangeEnd, + LabelVert, + LabelCornerNew, + LabelCornerActive, + LabelHoriz, +}; +const char *LiveVariablePrinter::getLineChar(LineChar C) const { + bool IsASCII = DbgVariables == DVASCII; + switch (C) { + case LineChar::RangeStart: + return IsASCII ? "^" : (const char *)u8"\u2548"; + case LineChar::RangeMid: + return IsASCII ? "|" : (const char *)u8"\u2503"; + case LineChar::RangeEnd: + return IsASCII ? "v" : (const char *)u8"\u253b"; + case LineChar::LabelVert: + return IsASCII ? "|" : (const char *)u8"\u2502"; + case LineChar::LabelCornerNew: + return IsASCII ? "/" : (const char *)u8"\u250c"; + case LineChar::LabelCornerActive: + return IsASCII ? "|" : (const char *)u8"\u2520"; + case LineChar::LabelHoriz: + return IsASCII ? "-" : (const char *)u8"\u2500"; + } + llvm_unreachable("Unhandled LineChar enum"); +} + +/// Print live ranges to the right of an existing line. This assumes the +/// line is not an instruction, so doesn't start or end any live ranges, so +/// we only need to print active ranges or empty columns. If AfterInst is +/// true, this is being printed after the last instruction fed to update(), +/// otherwise this is being printed before it. +void LiveVariablePrinter::printAfterOtherLine(formatted_raw_ostream &OS, + bool AfterInst) { + if (ActiveCols.size()) { + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx < End; ++ColIdx) { + if (ActiveCols[ColIdx].isActive()) { + if ((AfterInst && ActiveCols[ColIdx].LiveOut) || + (!AfterInst && ActiveCols[ColIdx].LiveIn)) + OS << getLineChar(LineChar::RangeMid); + else if (!AfterInst && ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::LabelVert); + else + OS << " "; + } + OS << " "; + } + } + OS << "\n"; +} + +/// Print any live variable range info needed to the right of a +/// non-instruction line of disassembly. This is where we print the variable +/// names and expressions, with thin line-drawing characters connecting them +/// to the live range which starts at the next instruction. If MustPrint is +/// true, we have to print at least one line (with the continuation of any +/// already-active live ranges) because something has already been printed +/// earlier on this line. +void LiveVariablePrinter::printBetweenInsts(formatted_raw_ostream &OS, + bool MustPrint) { + bool PrintedSomething = false; + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { + if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) { + // First we need to print the live range markers for any active + // columns to the left of this one. + OS.PadToColumn(getIndentLevel()); + for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) { + if (ActiveCols[ColIdx2].isActive()) { + if (ActiveCols[ColIdx2].MustDrawLabel && !ActiveCols[ColIdx2].LiveIn) + OS << getLineChar(LineChar::LabelVert) << " "; + else + OS << getLineChar(LineChar::RangeMid) << " "; + } else + OS << " "; + } + + // Then print the variable name and location of the new live range, + // with box drawing characters joining it to the live range line. + OS << getLineChar(ActiveCols[ColIdx].LiveIn ? LineChar::LabelCornerActive + : LineChar::LabelCornerNew) + << getLineChar(LineChar::LabelHoriz) << " "; + WithColor(OS, raw_ostream::GREEN) + << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName; + OS << " = "; + { + WithColor ExprColor(OS, raw_ostream::CYAN); + LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI); + } + + // If there are any columns to the right of the expression we just + // printed, then continue their live range lines. + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx2 < End; ++ColIdx2) { + if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn) + OS << getLineChar(LineChar::RangeMid) << " "; + else + OS << " "; + } + + OS << "\n"; + PrintedSomething = true; + } + } + + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) + if (ActiveCols[ColIdx].isActive()) + ActiveCols[ColIdx].MustDrawLabel = false; + + // If we must print something (because we printed a line/column number), + // but don't have any new variables to print, then print a line which + // just continues any existing live ranges. + if (MustPrint && !PrintedSomething) + printAfterOtherLine(OS, false); +} + +/// Print the live variable ranges to the right of a disassembled instruction. +void LiveVariablePrinter::printAfterInst(formatted_raw_ostream &OS) { + if (!ActiveCols.size()) + return; + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx < End; ++ColIdx) { + if (!ActiveCols[ColIdx].isActive()) + OS << " "; + else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::RangeMid) << " "; + else if (ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::RangeStart) << " "; + else if (ActiveCols[ColIdx].LiveIn) + OS << getLineChar(LineChar::RangeEnd) << " "; + else + llvm_unreachable("var must be live in or out!"); + } +} + +bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { + std::unique_ptr<MemoryBuffer> Buffer; + if (LineInfo.Source) { + Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source); + } else { + auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName); + if (!BufferOrError) { + if (MissingSources.insert(LineInfo.FileName).second) + reportWarning("failed to find source " + LineInfo.FileName, + Obj->getFileName()); + return false; + } + Buffer = std::move(*BufferOrError); + } + // Chomp the file to get lines + const char *BufferStart = Buffer->getBufferStart(), + *BufferEnd = Buffer->getBufferEnd(); + std::vector<StringRef> &Lines = LineCache[LineInfo.FileName]; + const char *Start = BufferStart; + for (const char *I = BufferStart; I != BufferEnd; ++I) + if (*I == '\n') { + Lines.emplace_back(Start, I - Start - (BufferStart < I && I[-1] == '\r')); + Start = I + 1; + } + if (Start < BufferEnd) + Lines.emplace_back(Start, BufferEnd - Start); + SourceCache[LineInfo.FileName] = std::move(Buffer); + return true; +} + +void SourcePrinter::printSourceLine(formatted_raw_ostream &OS, + object::SectionedAddress Address, + StringRef ObjectFilename, + LiveVariablePrinter &LVP, + StringRef Delimiter) { + if (!Symbolizer) + return; + + DILineInfo LineInfo = DILineInfo(); + Expected<DILineInfo> ExpectedLineInfo = + Symbolizer->symbolizeCode(*Obj, Address); + std::string ErrorMessage; + if (ExpectedLineInfo) { + LineInfo = *ExpectedLineInfo; + } else if (!WarnedInvalidDebugInfo) { + WarnedInvalidDebugInfo = true; + // TODO Untested. + reportWarning("failed to parse debug information: " + + toString(ExpectedLineInfo.takeError()), + ObjectFilename); + } + + if (!objdump::Prefix.empty() && + sys::path::is_absolute_gnu(LineInfo.FileName)) { + // FileName has at least one character since is_absolute_gnu is false for + // an empty string. + assert(!LineInfo.FileName.empty()); + if (PrefixStrip > 0) { + uint32_t Level = 0; + auto StrippedNameStart = LineInfo.FileName.begin(); + + // Path.h iterator skips extra separators. Therefore it cannot be used + // here to keep compatibility with GNU Objdump. + for (auto Pos = StrippedNameStart + 1, End = LineInfo.FileName.end(); + Pos != End && Level < PrefixStrip; ++Pos) { + if (sys::path::is_separator(*Pos)) { + StrippedNameStart = Pos; + ++Level; + } + } + + LineInfo.FileName = + std::string(StrippedNameStart, LineInfo.FileName.end()); + } + + SmallString<128> FilePath; + sys::path::append(FilePath, Prefix, LineInfo.FileName); + + LineInfo.FileName = std::string(FilePath); + } + + if (PrintLines) + printLines(OS, LineInfo, Delimiter, LVP); + if (PrintSource) + printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP); + OldLineInfo = LineInfo; +} + +void SourcePrinter::printLines(formatted_raw_ostream &OS, + const DILineInfo &LineInfo, StringRef Delimiter, + LiveVariablePrinter &LVP) { + bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString && + LineInfo.FunctionName != OldLineInfo.FunctionName; + if (PrintFunctionName) { + OS << Delimiter << LineInfo.FunctionName; + // If demangling is successful, FunctionName will end with "()". Print it + // only if demangling did not run or was unsuccessful. + if (!StringRef(LineInfo.FunctionName).endswith("()")) + OS << "()"; + OS << ":\n"; + } + if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 && + (OldLineInfo.Line != LineInfo.Line || + OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) { + OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line; + LVP.printBetweenInsts(OS, true); + } +} + +void SourcePrinter::printSources(formatted_raw_ostream &OS, + const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter, + LiveVariablePrinter &LVP) { + if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 || + (OldLineInfo.Line == LineInfo.Line && + OldLineInfo.FileName == LineInfo.FileName)) + return; + + if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) + if (!cacheSource(LineInfo)) + return; + auto LineBuffer = LineCache.find(LineInfo.FileName); + if (LineBuffer != LineCache.end()) { + if (LineInfo.Line > LineBuffer->second.size()) { + reportWarning( + formatv( + "debug info line number {0} exceeds the number of lines in {1}", + LineInfo.Line, LineInfo.FileName), + ObjectFilename); + return; + } + // Vector begins at 0, line numbers are non-zero + OS << Delimiter << LineBuffer->second[LineInfo.Line - 1]; + LVP.printBetweenInsts(OS, true); + } +} + +SourcePrinter::SourcePrinter(const object::ObjectFile *Obj, + StringRef DefaultArch) + : Obj(Obj) { + symbolize::LLVMSymbolizer::Options SymbolizerOpts; + SymbolizerOpts.PrintFunctions = + DILineInfoSpecifier::FunctionNameKind::LinkageName; + SymbolizerOpts.Demangle = Demangle; + SymbolizerOpts.DefaultArch = std::string(DefaultArch); + Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts)); +} + +} // namespace objdump +} // namespace llvm diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h new file mode 100644 index 000000000000..21d5bdcf8a49 --- /dev/null +++ b/llvm/tools/llvm-objdump/SourcePrinter.h @@ -0,0 +1,166 @@ +//===-- SourcePrinter.h - source interleaving utilities --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H +#define LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H + +#include "llvm/ADT/IndexedMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Support/FormattedStream.h" +#include <unordered_map> +#include <vector> + +namespace llvm { +namespace objdump { + +/// Stores a single expression representing the location of a source-level +/// variable, along with the PC range for which that expression is valid. +struct LiveVariable { + DWARFLocationExpression LocExpr; + const char *VarName; + DWARFUnit *Unit; + const DWARFDie FuncDie; + + LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName, + DWARFUnit *Unit, const DWARFDie FuncDie) + : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {} + + bool liveAtAddress(object::SectionedAddress Addr); + + void print(raw_ostream &OS, const MCRegisterInfo &MRI) const; +}; + +/// Helper class for printing source variable locations alongside disassembly. +class LiveVariablePrinter { + // Information we want to track about one column in which we are printing a + // variable live range. + struct Column { + unsigned VarIdx = NullVarIdx; + bool LiveIn = false; + bool LiveOut = false; + bool MustDrawLabel = false; + + bool isActive() const { return VarIdx != NullVarIdx; } + + static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max(); + }; + + // All live variables we know about in the object/image file. + std::vector<LiveVariable> LiveVariables; + + // The columns we are currently drawing. + IndexedMap<Column> ActiveCols; + + const MCRegisterInfo &MRI; + const MCSubtargetInfo &STI; + + void addVariable(DWARFDie FuncDie, DWARFDie VarDie); + + void addFunction(DWARFDie D); + + // Get the column number (in characters) at which the first live variable + // line should be printed. + unsigned getIndentLevel() const; + + // Indent to the first live-range column to the right of the currently + // printed line, and return the index of that column. + // TODO: formatted_raw_ostream uses "column" to mean a number of characters + // since the last \n, and we use it to mean the number of slots in which we + // put live variable lines. Pick a less overloaded word. + unsigned moveToFirstVarColumn(formatted_raw_ostream &OS); + + unsigned findFreeColumn(); + +public: + LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) + : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {} + + void dump() const; + + void addCompileUnit(DWARFDie D); + + /// Update to match the state of the instruction between ThisAddr and + /// NextAddr. In the common case, any live range active at ThisAddr is + /// live-in to the instruction, and any live range active at NextAddr is + /// live-out of the instruction. If IncludeDefinedVars is false, then live + /// ranges starting at NextAddr will be ignored. + void update(object::SectionedAddress ThisAddr, + object::SectionedAddress NextAddr, bool IncludeDefinedVars); + + enum class LineChar { + RangeStart, + RangeMid, + RangeEnd, + LabelVert, + LabelCornerNew, + LabelCornerActive, + LabelHoriz, + }; + const char *getLineChar(LineChar C) const; + + /// Print live ranges to the right of an existing line. This assumes the + /// line is not an instruction, so doesn't start or end any live ranges, so + /// we only need to print active ranges or empty columns. If AfterInst is + /// true, this is being printed after the last instruction fed to update(), + /// otherwise this is being printed before it. + void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst); + + /// Print any live variable range info needed to the right of a + /// non-instruction line of disassembly. This is where we print the variable + /// names and expressions, with thin line-drawing characters connecting them + /// to the live range which starts at the next instruction. If MustPrint is + /// true, we have to print at least one line (with the continuation of any + /// already-active live ranges) because something has already been printed + /// earlier on this line. + void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint); + + /// Print the live variable ranges to the right of a disassembled instruction. + void printAfterInst(formatted_raw_ostream &OS); +}; + +class SourcePrinter { +protected: + DILineInfo OldLineInfo; + const object::ObjectFile *Obj = nullptr; + std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer; + // File name to file contents of source. + std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache; + // Mark the line endings of the cached source. + std::unordered_map<std::string, std::vector<StringRef>> LineCache; + // Keep track of missing sources. + StringSet<> MissingSources; + // Only emit 'invalid debug info' warning once. + bool WarnedInvalidDebugInfo = false; + +private: + bool cacheSource(const DILineInfo &LineInfoFile); + + void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo, + StringRef Delimiter, LiveVariablePrinter &LVP); + + void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter, + LiveVariablePrinter &LVP); + +public: + SourcePrinter() = default; + SourcePrinter(const object::ObjectFile *Obj, StringRef DefaultArch); + virtual ~SourcePrinter() = default; + virtual void printSourceLine(formatted_raw_ostream &OS, + object::SectionedAddress Address, + StringRef ObjectFilename, + LiveVariablePrinter &LVP, + StringRef Delimiter = "; "); +}; + +} // namespace objdump +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-objdump/XCOFFDump.cpp b/llvm/tools/llvm-objdump/XCOFFDump.cpp index df37abbd3881..c4cc5fe7e21c 100644 --- a/llvm/tools/llvm-objdump/XCOFFDump.cpp +++ b/llvm/tools/llvm-objdump/XCOFFDump.cpp @@ -46,22 +46,30 @@ Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile *Obj, Optional<XCOFF::StorageMappingClass> objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile *Obj, const SymbolRef &Sym) { - XCOFFSymbolRef SymRef(Sym.getRawDataRefImpl(), Obj); + const XCOFFSymbolRef SymRef = Obj->toSymbolRef(Sym.getRawDataRefImpl()); - if (SymRef.hasCsectAuxEnt()) - return SymRef.getXCOFFCsectAuxEnt32()->StorageMappingClass; + if (!SymRef.isCsectSymbol()) + return None; - return None; + auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); + if (!CsectAuxEntOrErr) + return None; + + return CsectAuxEntOrErr.get().getStorageMappingClass(); } bool objdump::isLabel(const XCOFFObjectFile *Obj, const SymbolRef &Sym) { - XCOFFSymbolRef SymRef(Sym.getRawDataRefImpl(), Obj); + const XCOFFSymbolRef SymRef = Obj->toSymbolRef(Sym.getRawDataRefImpl()); + + if (!SymRef.isCsectSymbol()) + return false; - if (SymRef.hasCsectAuxEnt()) - return SymRef.getXCOFFCsectAuxEnt32()->isLabel(); + auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); + if (!CsectAuxEntOrErr) + return false; - return false; + return CsectAuxEntOrErr.get().isLabel(); } std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo, diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index 3134f989603a..48ae92f734c7 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -19,18 +19,19 @@ #include "COFFDump.h" #include "ELFDump.h" #include "MachODump.h" +#include "ObjdumpOptID.h" +#include "SourcePrinter.h" #include "WasmDump.h" #include "XCOFFDump.h" #include "llvm/ADT/IndexedMap.h" #include "llvm/ADT/Optional.h" -#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" -#include "llvm/CodeGen/FaultMaps.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/Demangle/Demangle.h" @@ -50,12 +51,15 @@ #include "llvm/Object/COFF.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/FaultMapParser.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/Wasm.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" @@ -81,307 +85,143 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::objdump; +using namespace llvm::opt; -#define DEBUG_TYPE "objdump" +namespace { + +class CommonOptTable : public opt::OptTable { +public: + CommonOptTable(ArrayRef<Info> OptionInfos, const char *Usage, + const char *Description) + : OptTable(OptionInfos), Usage(Usage), Description(Description) { + setGroupedShortOptions(true); + } + + void printHelp(StringRef Argv0, bool ShowHidden = false) const { + Argv0 = sys::path::filename(Argv0); + opt::OptTable::printHelp(outs(), (Argv0 + Usage).str().c_str(), Description, + ShowHidden, ShowHidden); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + } + +private: + const char *Usage; + const char *Description; +}; + +// ObjdumpOptID is in ObjdumpOptID.h + +#define PREFIX(NAME, VALUE) const char *const OBJDUMP_##NAME[] = VALUE; +#include "ObjdumpOpts.inc" +#undef PREFIX + +static constexpr opt::OptTable::Info ObjdumpInfoTable[] = { +#define OBJDUMP_nullptr nullptr +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {OBJDUMP_##PREFIX, NAME, HELPTEXT, \ + METAVAR, OBJDUMP_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OBJDUMP_##GROUP, \ + OBJDUMP_##ALIAS, ALIASARGS, VALUES}, +#include "ObjdumpOpts.inc" +#undef OPTION +#undef OBJDUMP_nullptr +}; -static cl::OptionCategory ObjdumpCat("llvm-objdump Options"); - -static cl::opt<uint64_t> AdjustVMA( - "adjust-vma", - cl::desc("Increase the displayed address by the specified offset"), - cl::value_desc("offset"), cl::init(0), cl::cat(ObjdumpCat)); - -static cl::opt<bool> - AllHeaders("all-headers", - cl::desc("Display all available header information"), - cl::cat(ObjdumpCat)); -static cl::alias AllHeadersShort("x", cl::desc("Alias for --all-headers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(AllHeaders)); - -static cl::opt<std::string> - ArchName("arch-name", - cl::desc("Target arch to disassemble for, " - "see --version for available targets"), - cl::cat(ObjdumpCat)); - -cl::opt<bool> - objdump::ArchiveHeaders("archive-headers", - cl::desc("Display archive header information"), - cl::cat(ObjdumpCat)); -static cl::alias ArchiveHeadersShort("a", - cl::desc("Alias for --archive-headers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(ArchiveHeaders)); - -cl::opt<bool> objdump::Demangle("demangle", cl::desc("Demangle symbols names"), - cl::init(false), cl::cat(ObjdumpCat)); -static cl::alias DemangleShort("C", cl::desc("Alias for --demangle"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(Demangle)); - -cl::opt<bool> objdump::Disassemble( - "disassemble", - cl::desc("Display assembler mnemonics for the machine instructions"), - cl::cat(ObjdumpCat)); -static cl::alias DisassembleShort("d", cl::desc("Alias for --disassemble"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(Disassemble)); - -cl::opt<bool> objdump::DisassembleAll( - "disassemble-all", - cl::desc("Display assembler mnemonics for the machine instructions"), - cl::cat(ObjdumpCat)); -static cl::alias DisassembleAllShort("D", - cl::desc("Alias for --disassemble-all"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(DisassembleAll)); - -cl::opt<bool> objdump::SymbolDescription( - "symbol-description", - cl::desc("Add symbol description for disassembly. This " - "option is for XCOFF files only"), - cl::init(false), cl::cat(ObjdumpCat)); - -static cl::list<std::string> - DisassembleSymbols("disassemble-symbols", cl::CommaSeparated, - cl::desc("List of symbols to disassemble. " - "Accept demangled names when --demangle is " - "specified, otherwise accept mangled names"), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> DisassembleZeroes( - "disassemble-zeroes", - cl::desc("Do not skip blocks of zeroes when disassembling"), - cl::cat(ObjdumpCat)); -static cl::alias - DisassembleZeroesShort("z", cl::desc("Alias for --disassemble-zeroes"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(DisassembleZeroes)); - -static cl::list<std::string> - DisassemblerOptions("disassembler-options", - cl::desc("Pass target specific disassembler options"), - cl::value_desc("options"), cl::CommaSeparated, - cl::cat(ObjdumpCat)); -static cl::alias - DisassemblerOptionsShort("M", cl::desc("Alias for --disassembler-options"), - cl::NotHidden, cl::Grouping, cl::Prefix, - cl::CommaSeparated, - cl::aliasopt(DisassemblerOptions)); - -cl::opt<DIDumpType> objdump::DwarfDumpType( - "dwarf", cl::init(DIDT_Null), cl::desc("Dump of dwarf debug sections:"), - cl::values(clEnumValN(DIDT_DebugFrame, "frames", ".debug_frame")), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> DynamicRelocations( - "dynamic-reloc", - cl::desc("Display the dynamic relocation entries in the file"), - cl::cat(ObjdumpCat)); -static cl::alias DynamicRelocationShort("R", - cl::desc("Alias for --dynamic-reloc"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(DynamicRelocations)); - -static cl::opt<bool> - FaultMapSection("fault-map-section", - cl::desc("Display contents of faultmap section"), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> - FileHeaders("file-headers", - cl::desc("Display the contents of the overall file header"), - cl::cat(ObjdumpCat)); -static cl::alias FileHeadersShort("f", cl::desc("Alias for --file-headers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(FileHeaders)); - -cl::opt<bool> - objdump::SectionContents("full-contents", - cl::desc("Display the content of each section"), - cl::cat(ObjdumpCat)); -static cl::alias SectionContentsShort("s", - cl::desc("Alias for --full-contents"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(SectionContents)); - -static cl::list<std::string> InputFilenames(cl::Positional, - cl::desc("<input object files>"), - cl::ZeroOrMore, - cl::cat(ObjdumpCat)); - -static cl::opt<bool> - PrintLines("line-numbers", - cl::desc("Display source line numbers with " - "disassembly. Implies disassemble object"), - cl::cat(ObjdumpCat)); -static cl::alias PrintLinesShort("l", cl::desc("Alias for --line-numbers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(PrintLines)); - -static cl::opt<bool> MachOOpt("macho", - cl::desc("Use MachO specific object file parser"), - cl::cat(ObjdumpCat)); -static cl::alias MachOm("m", cl::desc("Alias for --macho"), cl::NotHidden, - cl::Grouping, cl::aliasopt(MachOOpt)); - -cl::opt<std::string> objdump::MCPU( - "mcpu", cl::desc("Target a specific cpu type (--mcpu=help for details)"), - cl::value_desc("cpu-name"), cl::init(""), cl::cat(ObjdumpCat)); - -cl::list<std::string> objdump::MAttrs( - "mattr", cl::CommaSeparated, - cl::desc("Target specific attributes (--mattr=help for details)"), - cl::value_desc("a1,+a2,-a3,..."), cl::cat(ObjdumpCat)); - -cl::opt<bool> objdump::NoShowRawInsn( - "no-show-raw-insn", - cl::desc( - "When disassembling instructions, do not print the instruction bytes."), - cl::cat(ObjdumpCat)); - -cl::opt<bool> objdump::NoLeadingAddr("no-leading-addr", - cl::desc("Print no leading address"), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> RawClangAST( - "raw-clang-ast", - cl::desc("Dump the raw binary contents of the clang AST section"), - cl::cat(ObjdumpCat)); - -cl::opt<bool> - objdump::Relocations("reloc", - cl::desc("Display the relocation entries in the file"), - cl::cat(ObjdumpCat)); -static cl::alias RelocationsShort("r", cl::desc("Alias for --reloc"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(Relocations)); - -cl::opt<bool> - objdump::PrintImmHex("print-imm-hex", - cl::desc("Use hex format for immediate values"), - cl::cat(ObjdumpCat)); - -cl::opt<bool> - objdump::PrivateHeaders("private-headers", - cl::desc("Display format specific file headers"), - cl::cat(ObjdumpCat)); -static cl::alias PrivateHeadersShort("p", - cl::desc("Alias for --private-headers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(PrivateHeaders)); - -cl::list<std::string> - objdump::FilterSections("section", - cl::desc("Operate on the specified sections only. " - "With --macho dump segment,section"), - cl::cat(ObjdumpCat)); -static cl::alias FilterSectionsj("j", cl::desc("Alias for --section"), - cl::NotHidden, cl::Grouping, cl::Prefix, - cl::aliasopt(FilterSections)); - -cl::opt<bool> objdump::SectionHeaders( - "section-headers", - cl::desc("Display summaries of the headers for each section."), - cl::cat(ObjdumpCat)); -static cl::alias SectionHeadersShort("headers", - cl::desc("Alias for --section-headers"), - cl::NotHidden, - cl::aliasopt(SectionHeaders)); -static cl::alias SectionHeadersShorter("h", - cl::desc("Alias for --section-headers"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(SectionHeaders)); - -static cl::opt<bool> - ShowLMA("show-lma", - cl::desc("Display LMA column when dumping ELF section headers"), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> PrintSource( - "source", - cl::desc( - "Display source inlined with disassembly. Implies disassemble object"), - cl::cat(ObjdumpCat)); -static cl::alias PrintSourceShort("S", cl::desc("Alias for --source"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(PrintSource)); - -static cl::opt<uint64_t> - StartAddress("start-address", cl::desc("Disassemble beginning at address"), - cl::value_desc("address"), cl::init(0), cl::cat(ObjdumpCat)); -static cl::opt<uint64_t> StopAddress("stop-address", - cl::desc("Stop disassembly at address"), - cl::value_desc("address"), - cl::init(UINT64_MAX), cl::cat(ObjdumpCat)); - -cl::opt<bool> objdump::SymbolTable("syms", cl::desc("Display the symbol table"), - cl::cat(ObjdumpCat)); -static cl::alias SymbolTableShort("t", cl::desc("Alias for --syms"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(SymbolTable)); - -static cl::opt<bool> SymbolizeOperands( - "symbolize-operands", - cl::desc("Symbolize instruction operands when disassembling"), - cl::cat(ObjdumpCat)); - -static cl::opt<bool> DynamicSymbolTable( - "dynamic-syms", - cl::desc("Display the contents of the dynamic symbol table"), - cl::cat(ObjdumpCat)); -static cl::alias DynamicSymbolTableShort("T", - cl::desc("Alias for --dynamic-syms"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(DynamicSymbolTable)); - -cl::opt<std::string> - objdump::TripleName("triple", - cl::desc("Target triple to disassemble for, see " - "--version for available targets"), - cl::cat(ObjdumpCat)); - -cl::opt<bool> objdump::UnwindInfo("unwind-info", - cl::desc("Display unwind information"), - cl::cat(ObjdumpCat)); -static cl::alias UnwindInfoShort("u", cl::desc("Alias for --unwind-info"), - cl::NotHidden, cl::Grouping, - cl::aliasopt(UnwindInfo)); - -static cl::opt<bool> - Wide("wide", cl::desc("Ignored for compatibility with GNU objdump"), - cl::cat(ObjdumpCat)); -static cl::alias WideShort("w", cl::Grouping, cl::aliasopt(Wide)); - -cl::opt<std::string> objdump::Prefix("prefix", - cl::desc("Add prefix to absolute paths"), - cl::cat(ObjdumpCat)); - -enum DebugVarsFormat { - DVDisabled, - DVUnicode, - DVASCII, +class ObjdumpOptTable : public CommonOptTable { +public: + ObjdumpOptTable() + : CommonOptTable(ObjdumpInfoTable, " [options] <input object files>", + "llvm object file dumper") {} +}; + +enum OtoolOptID { + OTOOL_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OTOOL_##ID, +#include "OtoolOpts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const OTOOL_##NAME[] = VALUE; +#include "OtoolOpts.inc" +#undef PREFIX + +static constexpr opt::OptTable::Info OtoolInfoTable[] = { +#define OTOOL_nullptr nullptr +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {OTOOL_##PREFIX, NAME, HELPTEXT, \ + METAVAR, OTOOL_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OTOOL_##GROUP, \ + OTOOL_##ALIAS, ALIASARGS, VALUES}, +#include "OtoolOpts.inc" +#undef OPTION +#undef OTOOL_nullptr +}; + +class OtoolOptTable : public CommonOptTable { +public: + OtoolOptTable() + : CommonOptTable(OtoolInfoTable, " [option...] [file...]", + "Mach-O object file displaying tool") {} }; -static cl::opt<DebugVarsFormat> DbgVariables( - "debug-vars", cl::init(DVDisabled), - cl::desc("Print the locations (in registers or memory) of " - "source-level variables alongside disassembly"), - cl::ValueOptional, - cl::values(clEnumValN(DVUnicode, "", "unicode"), - clEnumValN(DVUnicode, "unicode", "unicode"), - clEnumValN(DVASCII, "ascii", "unicode")), - cl::cat(ObjdumpCat)); - -static cl::opt<int> - DbgIndent("debug-vars-indent", cl::init(40), - cl::desc("Distance to indent the source-level variable display, " - "relative to the start of the disassembly"), - cl::cat(ObjdumpCat)); - -static cl::extrahelp - HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); +} // namespace + +#define DEBUG_TYPE "objdump" + +static uint64_t AdjustVMA; +static bool AllHeaders; +static std::string ArchName; +bool objdump::ArchiveHeaders; +bool objdump::Demangle; +bool objdump::Disassemble; +bool objdump::DisassembleAll; +bool objdump::SymbolDescription; +static std::vector<std::string> DisassembleSymbols; +static bool DisassembleZeroes; +static std::vector<std::string> DisassemblerOptions; +DIDumpType objdump::DwarfDumpType; +static bool DynamicRelocations; +static bool FaultMapSection; +static bool FileHeaders; +bool objdump::SectionContents; +static std::vector<std::string> InputFilenames; +bool objdump::PrintLines; +static bool MachOOpt; +std::string objdump::MCPU; +std::vector<std::string> objdump::MAttrs; +bool objdump::ShowRawInsn; +bool objdump::LeadingAddr; +static bool RawClangAST; +bool objdump::Relocations; +bool objdump::PrintImmHex; +bool objdump::PrivateHeaders; +std::vector<std::string> objdump::FilterSections; +bool objdump::SectionHeaders; +static bool ShowLMA; +bool objdump::PrintSource; + +static uint64_t StartAddress; +static bool HasStartAddressFlag; +static uint64_t StopAddress = UINT64_MAX; +static bool HasStopAddressFlag; + +bool objdump::SymbolTable; +static bool SymbolizeOperands; +static bool DynamicSymbolTable; +std::string objdump::TripleName; +bool objdump::UnwindInfo; +static bool Wide; +std::string objdump::Prefix; +uint32_t objdump::PrefixStrip; + +DebugVarsFormat objdump::DbgVariables = DVDisabled; + +int objdump::DbgIndent = 52; static StringSet<> DisasmSymbolSet; StringSet<> objdump::FoundSectionSet; @@ -588,515 +428,7 @@ namespace { /// Get the column at which we want to start printing the instruction /// disassembly, taking into account anything which appears to the left of it. unsigned getInstStartColumn(const MCSubtargetInfo &STI) { - return NoShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; -} - -/// Stores a single expression representing the location of a source-level -/// variable, along with the PC range for which that expression is valid. -struct LiveVariable { - DWARFLocationExpression LocExpr; - const char *VarName; - DWARFUnit *Unit; - const DWARFDie FuncDie; - - LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName, - DWARFUnit *Unit, const DWARFDie FuncDie) - : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {} - - bool liveAtAddress(object::SectionedAddress Addr) { - if (LocExpr.Range == None) - return false; - return LocExpr.Range->SectionIndex == Addr.SectionIndex && - LocExpr.Range->LowPC <= Addr.Address && - LocExpr.Range->HighPC > Addr.Address; - } - - void print(raw_ostream &OS, const MCRegisterInfo &MRI) const { - DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()}, - Unit->getContext().isLittleEndian(), 0); - DWARFExpression Expression(Data, Unit->getAddressByteSize()); - Expression.printCompact(OS, MRI); - } -}; - -/// Helper class for printing source variable locations alongside disassembly. -class LiveVariablePrinter { - // Information we want to track about one column in which we are printing a - // variable live range. - struct Column { - unsigned VarIdx = NullVarIdx; - bool LiveIn = false; - bool LiveOut = false; - bool MustDrawLabel = false; - - bool isActive() const { return VarIdx != NullVarIdx; } - - static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max(); - }; - - // All live variables we know about in the object/image file. - std::vector<LiveVariable> LiveVariables; - - // The columns we are currently drawing. - IndexedMap<Column> ActiveCols; - - const MCRegisterInfo &MRI; - const MCSubtargetInfo &STI; - - void addVariable(DWARFDie FuncDie, DWARFDie VarDie) { - uint64_t FuncLowPC, FuncHighPC, SectionIndex; - FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex); - const char *VarName = VarDie.getName(DINameKind::ShortName); - DWARFUnit *U = VarDie.getDwarfUnit(); - - Expected<DWARFLocationExpressionsVector> Locs = - VarDie.getLocations(dwarf::DW_AT_location); - if (!Locs) { - // If the variable doesn't have any locations, just ignore it. We don't - // report an error or warning here as that could be noisy on optimised - // code. - consumeError(Locs.takeError()); - return; - } - - for (const DWARFLocationExpression &LocExpr : *Locs) { - if (LocExpr.Range) { - LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie); - } else { - // If the LocExpr does not have an associated range, it is valid for - // the whole of the function. - // TODO: technically it is not valid for any range covered by another - // LocExpr, does that happen in reality? - DWARFLocationExpression WholeFuncExpr{ - DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), - LocExpr.Expr}; - LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie); - } - } - } - - void addFunction(DWARFDie D) { - for (const DWARFDie &Child : D.children()) { - if (Child.getTag() == dwarf::DW_TAG_variable || - Child.getTag() == dwarf::DW_TAG_formal_parameter) - addVariable(D, Child); - else - addFunction(Child); - } - } - - // Get the column number (in characters) at which the first live variable - // line should be printed. - unsigned getIndentLevel() const { - return DbgIndent + getInstStartColumn(STI); - } - - // Indent to the first live-range column to the right of the currently - // printed line, and return the index of that column. - // TODO: formatted_raw_ostream uses "column" to mean a number of characters - // since the last \n, and we use it to mean the number of slots in which we - // put live variable lines. Pick a less overloaded word. - unsigned moveToFirstVarColumn(formatted_raw_ostream &OS) { - // Logical column number: column zero is the first column we print in, each - // logical column is 2 physical columns wide. - unsigned FirstUnprintedLogicalColumn = - std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0); - // Physical column number: the actual column number in characters, with - // zero being the left-most side of the screen. - unsigned FirstUnprintedPhysicalColumn = - getIndentLevel() + FirstUnprintedLogicalColumn * 2; - - if (FirstUnprintedPhysicalColumn > OS.getColumn()) - OS.PadToColumn(FirstUnprintedPhysicalColumn); - - return FirstUnprintedLogicalColumn; - } - - unsigned findFreeColumn() { - for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx) - if (!ActiveCols[ColIdx].isActive()) - return ColIdx; - - size_t OldSize = ActiveCols.size(); - ActiveCols.grow(std::max<size_t>(OldSize * 2, 1)); - return OldSize; - } - -public: - LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) - : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {} - - void dump() const { - for (const LiveVariable &LV : LiveVariables) { - dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": "; - LV.print(dbgs(), MRI); - dbgs() << "\n"; - } - } - - void addCompileUnit(DWARFDie D) { - if (D.getTag() == dwarf::DW_TAG_subprogram) - addFunction(D); - else - for (const DWARFDie &Child : D.children()) - addFunction(Child); - } - - /// Update to match the state of the instruction between ThisAddr and - /// NextAddr. In the common case, any live range active at ThisAddr is - /// live-in to the instruction, and any live range active at NextAddr is - /// live-out of the instruction. If IncludeDefinedVars is false, then live - /// ranges starting at NextAddr will be ignored. - void update(object::SectionedAddress ThisAddr, - object::SectionedAddress NextAddr, bool IncludeDefinedVars) { - // First, check variables which have already been assigned a column, so - // that we don't change their order. - SmallSet<unsigned, 8> CheckedVarIdxs; - for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { - if (!ActiveCols[ColIdx].isActive()) - continue; - CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx); - LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx]; - ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr); - ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr); - LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-" - << NextAddr.Address << ", " << LV.VarName << ", Col " - << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn - << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n"); - - if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) - ActiveCols[ColIdx].VarIdx = Column::NullVarIdx; - } - - // Next, look for variables which don't already have a column, but which - // are now live. - if (IncludeDefinedVars) { - for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End; - ++VarIdx) { - if (CheckedVarIdxs.count(VarIdx)) - continue; - LiveVariable &LV = LiveVariables[VarIdx]; - bool LiveIn = LV.liveAtAddress(ThisAddr); - bool LiveOut = LV.liveAtAddress(NextAddr); - if (!LiveIn && !LiveOut) - continue; - - unsigned ColIdx = findFreeColumn(); - LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-" - << NextAddr.Address << ", " << LV.VarName << ", Col " - << ColIdx << ": LiveIn=" << LiveIn - << ", LiveOut=" << LiveOut << "\n"); - ActiveCols[ColIdx].VarIdx = VarIdx; - ActiveCols[ColIdx].LiveIn = LiveIn; - ActiveCols[ColIdx].LiveOut = LiveOut; - ActiveCols[ColIdx].MustDrawLabel = true; - } - } - } - - enum class LineChar { - RangeStart, - RangeMid, - RangeEnd, - LabelVert, - LabelCornerNew, - LabelCornerActive, - LabelHoriz, - }; - const char *getLineChar(LineChar C) const { - bool IsASCII = DbgVariables == DVASCII; - switch (C) { - case LineChar::RangeStart: - return IsASCII ? "^" : (const char *)u8"\u2548"; - case LineChar::RangeMid: - return IsASCII ? "|" : (const char *)u8"\u2503"; - case LineChar::RangeEnd: - return IsASCII ? "v" : (const char *)u8"\u253b"; - case LineChar::LabelVert: - return IsASCII ? "|" : (const char *)u8"\u2502"; - case LineChar::LabelCornerNew: - return IsASCII ? "/" : (const char *)u8"\u250c"; - case LineChar::LabelCornerActive: - return IsASCII ? "|" : (const char *)u8"\u2520"; - case LineChar::LabelHoriz: - return IsASCII ? "-" : (const char *)u8"\u2500"; - } - llvm_unreachable("Unhandled LineChar enum"); - } - - /// Print live ranges to the right of an existing line. This assumes the - /// line is not an instruction, so doesn't start or end any live ranges, so - /// we only need to print active ranges or empty columns. If AfterInst is - /// true, this is being printed after the last instruction fed to update(), - /// otherwise this is being printed before it. - void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst) { - if (ActiveCols.size()) { - unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); - for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); - ColIdx < End; ++ColIdx) { - if (ActiveCols[ColIdx].isActive()) { - if ((AfterInst && ActiveCols[ColIdx].LiveOut) || - (!AfterInst && ActiveCols[ColIdx].LiveIn)) - OS << getLineChar(LineChar::RangeMid); - else if (!AfterInst && ActiveCols[ColIdx].LiveOut) - OS << getLineChar(LineChar::LabelVert); - else - OS << " "; - } - OS << " "; - } - } - OS << "\n"; - } - - /// Print any live variable range info needed to the right of a - /// non-instruction line of disassembly. This is where we print the variable - /// names and expressions, with thin line-drawing characters connecting them - /// to the live range which starts at the next instruction. If MustPrint is - /// true, we have to print at least one line (with the continuation of any - /// already-active live ranges) because something has already been printed - /// earlier on this line. - void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint) { - bool PrintedSomething = false; - for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { - if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) { - // First we need to print the live range markers for any active - // columns to the left of this one. - OS.PadToColumn(getIndentLevel()); - for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) { - if (ActiveCols[ColIdx2].isActive()) { - if (ActiveCols[ColIdx2].MustDrawLabel && - !ActiveCols[ColIdx2].LiveIn) - OS << getLineChar(LineChar::LabelVert) << " "; - else - OS << getLineChar(LineChar::RangeMid) << " "; - } else - OS << " "; - } - - // Then print the variable name and location of the new live range, - // with box drawing characters joining it to the live range line. - OS << getLineChar(ActiveCols[ColIdx].LiveIn - ? LineChar::LabelCornerActive - : LineChar::LabelCornerNew) - << getLineChar(LineChar::LabelHoriz) << " "; - WithColor(OS, raw_ostream::GREEN) - << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName; - OS << " = "; - { - WithColor ExprColor(OS, raw_ostream::CYAN); - LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI); - } - - // If there are any columns to the right of the expression we just - // printed, then continue their live range lines. - unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); - for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size(); - ColIdx2 < End; ++ColIdx2) { - if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn) - OS << getLineChar(LineChar::RangeMid) << " "; - else - OS << " "; - } - - OS << "\n"; - PrintedSomething = true; - } - } - - for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) - if (ActiveCols[ColIdx].isActive()) - ActiveCols[ColIdx].MustDrawLabel = false; - - // If we must print something (because we printed a line/column number), - // but don't have any new variables to print, then print a line which - // just continues any existing live ranges. - if (MustPrint && !PrintedSomething) - printAfterOtherLine(OS, false); - } - - /// Print the live variable ranges to the right of a disassembled instruction. - void printAfterInst(formatted_raw_ostream &OS) { - if (!ActiveCols.size()) - return; - unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); - for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); - ColIdx < End; ++ColIdx) { - if (!ActiveCols[ColIdx].isActive()) - OS << " "; - else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut) - OS << getLineChar(LineChar::RangeMid) << " "; - else if (ActiveCols[ColIdx].LiveOut) - OS << getLineChar(LineChar::RangeStart) << " "; - else if (ActiveCols[ColIdx].LiveIn) - OS << getLineChar(LineChar::RangeEnd) << " "; - else - llvm_unreachable("var must be live in or out!"); - } - } -}; - -class SourcePrinter { -protected: - DILineInfo OldLineInfo; - const ObjectFile *Obj = nullptr; - std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer; - // File name to file contents of source. - std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache; - // Mark the line endings of the cached source. - std::unordered_map<std::string, std::vector<StringRef>> LineCache; - // Keep track of missing sources. - StringSet<> MissingSources; - // Only emit 'no debug info' warning once. - bool WarnedNoDebugInfo; - -private: - bool cacheSource(const DILineInfo& LineInfoFile); - - void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo, - StringRef Delimiter, LiveVariablePrinter &LVP); - - void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, - StringRef ObjectFilename, StringRef Delimiter, - LiveVariablePrinter &LVP); - -public: - SourcePrinter() = default; - SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) - : Obj(Obj), WarnedNoDebugInfo(false) { - symbolize::LLVMSymbolizer::Options SymbolizerOpts; - SymbolizerOpts.PrintFunctions = - DILineInfoSpecifier::FunctionNameKind::LinkageName; - SymbolizerOpts.Demangle = Demangle; - SymbolizerOpts.DefaultArch = std::string(DefaultArch); - Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts)); - } - virtual ~SourcePrinter() = default; - virtual void printSourceLine(formatted_raw_ostream &OS, - object::SectionedAddress Address, - StringRef ObjectFilename, - LiveVariablePrinter &LVP, - StringRef Delimiter = "; "); -}; - -bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { - std::unique_ptr<MemoryBuffer> Buffer; - if (LineInfo.Source) { - Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source); - } else { - auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName); - if (!BufferOrError) { - if (MissingSources.insert(LineInfo.FileName).second) - reportWarning("failed to find source " + LineInfo.FileName, - Obj->getFileName()); - return false; - } - Buffer = std::move(*BufferOrError); - } - // Chomp the file to get lines - const char *BufferStart = Buffer->getBufferStart(), - *BufferEnd = Buffer->getBufferEnd(); - std::vector<StringRef> &Lines = LineCache[LineInfo.FileName]; - const char *Start = BufferStart; - for (const char *I = BufferStart; I != BufferEnd; ++I) - if (*I == '\n') { - Lines.emplace_back(Start, I - Start - (BufferStart < I && I[-1] == '\r')); - Start = I + 1; - } - if (Start < BufferEnd) - Lines.emplace_back(Start, BufferEnd - Start); - SourceCache[LineInfo.FileName] = std::move(Buffer); - return true; -} - -void SourcePrinter::printSourceLine(formatted_raw_ostream &OS, - object::SectionedAddress Address, - StringRef ObjectFilename, - LiveVariablePrinter &LVP, - StringRef Delimiter) { - if (!Symbolizer) - return; - - DILineInfo LineInfo = DILineInfo(); - auto ExpectedLineInfo = Symbolizer->symbolizeCode(*Obj, Address); - std::string ErrorMessage; - if (!ExpectedLineInfo) - ErrorMessage = toString(ExpectedLineInfo.takeError()); - else - LineInfo = *ExpectedLineInfo; - - if (LineInfo.FileName == DILineInfo::BadString) { - if (!WarnedNoDebugInfo) { - std::string Warning = - "failed to parse debug information for " + ObjectFilename.str(); - if (!ErrorMessage.empty()) - Warning += ": " + ErrorMessage; - reportWarning(Warning, ObjectFilename); - WarnedNoDebugInfo = true; - } - } - - if (!Prefix.empty() && sys::path::is_absolute_gnu(LineInfo.FileName)) { - SmallString<128> FilePath; - sys::path::append(FilePath, Prefix, LineInfo.FileName); - - LineInfo.FileName = std::string(FilePath); - } - - if (PrintLines) - printLines(OS, LineInfo, Delimiter, LVP); - if (PrintSource) - printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP); - OldLineInfo = LineInfo; -} - -void SourcePrinter::printLines(formatted_raw_ostream &OS, - const DILineInfo &LineInfo, StringRef Delimiter, - LiveVariablePrinter &LVP) { - bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString && - LineInfo.FunctionName != OldLineInfo.FunctionName; - if (PrintFunctionName) { - OS << Delimiter << LineInfo.FunctionName; - // If demangling is successful, FunctionName will end with "()". Print it - // only if demangling did not run or was unsuccessful. - if (!StringRef(LineInfo.FunctionName).endswith("()")) - OS << "()"; - OS << ":\n"; - } - if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 && - (OldLineInfo.Line != LineInfo.Line || - OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) { - OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line; - LVP.printBetweenInsts(OS, true); - } -} - -void SourcePrinter::printSources(formatted_raw_ostream &OS, - const DILineInfo &LineInfo, - StringRef ObjectFilename, StringRef Delimiter, - LiveVariablePrinter &LVP) { - if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 || - (OldLineInfo.Line == LineInfo.Line && - OldLineInfo.FileName == LineInfo.FileName)) - return; - - if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) - if (!cacheSource(LineInfo)) - return; - auto LineBuffer = LineCache.find(LineInfo.FileName); - if (LineBuffer != LineCache.end()) { - if (LineInfo.Line > LineBuffer->second.size()) { - reportWarning( - formatv( - "debug info line number {0} exceeds the number of lines in {1}", - LineInfo.Line, LineInfo.FileName), - ObjectFilename); - return; - } - // Vector begins at 0, line numbers are non-zero - OS << Delimiter << LineBuffer->second[LineInfo.Line - 1]; - LVP.printBetweenInsts(OS, true); - } + return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; } static bool isAArch64Elf(const ObjectFile *Obj) { @@ -1139,9 +471,9 @@ public: LVP.printBetweenInsts(OS, false); size_t Start = OS.tell(); - if (!NoLeadingAddr) + if (LeadingAddr) OS << format("%8" PRIx64 ":", Address.Address); - if (!NoShowRawInsn) { + if (ShowRawInsn) { OS << ' '; dumpBytes(Bytes, OS); } @@ -1172,9 +504,9 @@ public: formatted_raw_ostream &OS) { uint32_t opcode = (Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0]; - if (!NoLeadingAddr) + if (LeadingAddr) OS << format("%8" PRIx64 ":", Address); - if (!NoShowRawInsn) { + if (ShowRawInsn) { OS << "\t"; dumpBytes(Bytes.slice(0, 4), OS); OS << format("\t%08" PRIx32, opcode); @@ -1309,9 +641,9 @@ public: LiveVariablePrinter &LVP) override { if (SP && (PrintSource || PrintLines)) SP->printSourceLine(OS, Address, ObjectFilename, LVP); - if (!NoLeadingAddr) + if (LeadingAddr) OS << format("%8" PRId64 ":", Address.Address / 8); - if (!NoShowRawInsn) { + if (ShowRawInsn) { OS << "\t"; dumpBytes(Bytes, OS); } @@ -1404,6 +736,43 @@ addDynamicElfSymbols(const ObjectFile *Obj, llvm_unreachable("Unsupported binary format"); } +static Optional<SectionRef> getWasmCodeSection(const WasmObjectFile *Obj) { + for (auto SecI : Obj->sections()) { + const WasmSection &Section = Obj->getWasmSection(SecI); + if (Section.Type == wasm::WASM_SEC_CODE) + return SecI; + } + return None; +} + +static void +addMissingWasmCodeSymbols(const WasmObjectFile *Obj, + std::map<SectionRef, SectionSymbolsTy> &AllSymbols) { + Optional<SectionRef> Section = getWasmCodeSection(Obj); + if (!Section) + return; + SectionSymbolsTy &Symbols = AllSymbols[*Section]; + + std::set<uint64_t> SymbolAddresses; + for (const auto &Sym : Symbols) + SymbolAddresses.insert(Sym.Addr); + + for (const wasm::WasmFunction &Function : Obj->functions()) { + uint64_t Address = Function.CodeSectionOffset; + // Only add fallback symbols for functions not already present in the symbol + // table. + if (SymbolAddresses.count(Address)) + continue; + // This function has no symbol, so it should have no SymbolName. + assert(Function.SymbolName.empty()); + // We use DebugName for the name, though it may be empty if there is no + // "name" custom section, or that section is missing a name for this + // function. + StringRef Name = Function.DebugName; + Symbols.emplace_back(Address, Name, ELF::STT_NOTYPE); + } +} + static void addPltEntries(const ObjectFile *Obj, std::map<SectionRef, SectionSymbolsTy> &AllSymbols, StringSaver &Saver) { @@ -1479,8 +848,7 @@ getRelocsMap(object::ObjectFile const &Obj) { if (Relocated == Obj.section_end() || !checkSectionFilter(*Relocated).Keep) continue; std::vector<RelocationRef> &V = Ret[*Relocated]; - for (const RelocationRef &R : Sec.relocations()) - V.push_back(R); + append_range(V, Sec.relocations()); // Sort relocations by address. llvm::stable_sort(V, isRelocAddressLess); } @@ -1641,6 +1009,63 @@ collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, const MCInstrAnalysis *MIA, } } +// Create an MCSymbolizer for the target and add it to the MCDisassembler. +// This is currently only used on AMDGPU, and assumes the format of the +// void * argument passed to AMDGPU's createMCSymbolizer. +static void addSymbolizer( + MCContext &Ctx, const Target *Target, StringRef TripleName, + MCDisassembler *DisAsm, uint64_t SectionAddr, ArrayRef<uint8_t> Bytes, + SectionSymbolsTy &Symbols, + std::vector<std::unique_ptr<std::string>> &SynthesizedLabelNames) { + + std::unique_ptr<MCRelocationInfo> RelInfo( + Target->createMCRelocationInfo(TripleName, Ctx)); + if (!RelInfo) + return; + std::unique_ptr<MCSymbolizer> Symbolizer(Target->createMCSymbolizer( + TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo))); + MCSymbolizer *SymbolizerPtr = &*Symbolizer; + DisAsm->setSymbolizer(std::move(Symbolizer)); + + if (!SymbolizeOperands) + return; + + // Synthesize labels referenced by branch instructions by + // disassembling, discarding the output, and collecting the referenced + // addresses from the symbolizer. + for (size_t Index = 0; Index != Bytes.size();) { + MCInst Inst; + uint64_t Size; + DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), SectionAddr + Index, + nulls()); + if (Size == 0) + Size = 1; + Index += Size; + } + ArrayRef<uint64_t> LabelAddrsRef = SymbolizerPtr->getReferencedAddresses(); + // Copy and sort to remove duplicates. + std::vector<uint64_t> LabelAddrs; + LabelAddrs.insert(LabelAddrs.end(), LabelAddrsRef.begin(), + LabelAddrsRef.end()); + llvm::sort(LabelAddrs); + LabelAddrs.resize(std::unique(LabelAddrs.begin(), LabelAddrs.end()) - + LabelAddrs.begin()); + // Add the labels. + for (unsigned LabelNum = 0; LabelNum != LabelAddrs.size(); ++LabelNum) { + auto Name = std::make_unique<std::string>(); + *Name = (Twine("L") + Twine(LabelNum)).str(); + SynthesizedLabelNames.push_back(std::move(Name)); + Symbols.push_back(SymbolInfoTy( + LabelAddrs[LabelNum], *SynthesizedLabelNames.back(), ELF::STT_NOTYPE)); + } + llvm::stable_sort(Symbols); + // Recreate the symbolizer with the new symbols list. + RelInfo.reset(Target->createMCRelocationInfo(TripleName, Ctx)); + Symbolizer.reset(Target->createMCSymbolizer( + TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo))); + DisAsm->setSymbolizer(std::move(Symbolizer)); +} + static StringRef getSegmentName(const MachOObjectFile *MachO, const SectionRef &Section) { if (MachO) { @@ -1651,6 +1076,29 @@ static StringRef getSegmentName(const MachOObjectFile *MachO, return ""; } +static void emitPostInstructionInfo(formatted_raw_ostream &FOS, + const MCAsmInfo &MAI, + const MCSubtargetInfo &STI, + StringRef Comments, + LiveVariablePrinter &LVP) { + do { + if (!Comments.empty()) { + // Emit a line of comments. + StringRef Comment; + std::tie(Comment, Comments) = Comments.split('\n'); + // MAI.getCommentColumn() assumes that instructions are printed at the + // position of 8, while getInstStartColumn() returns the actual position. + unsigned CommentColumn = + MAI.getCommentColumn() - 8 + getInstStartColumn(STI); + FOS.PadToColumn(CommentColumn); + FOS << MAI.getCommentString() << ' ' << Comment; + } + LVP.printAfterInst(FOS); + FOS << '\n'; + } while (!Comments.empty()); + FOS.flush(); +} + static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, MCContext &Ctx, MCDisassembler *PrimaryDisAsm, MCDisassembler *SecondaryDisAsm, @@ -1688,10 +1136,15 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, if (Obj->isELF() && getElfSymbolType(Obj, Symbol) == ELF::STT_SECTION) continue; - // Don't ask a Mach-O STAB symbol for its section unless you know that - // STAB symbol's section field refers to a valid section index. Otherwise - // the symbol may error trying to load a section that does not exist. if (MachO) { + // __mh_(execute|dylib|dylinker|bundle|preload|object)_header are special + // symbols that support MachO header introspection. They do not bind to + // code locations and are irrelevant for disassembly. + if (NameOrErr->startswith("__mh_") && NameOrErr->endswith("_header")) + continue; + // Don't ask a Mach-O STAB symbol for its section unless you know that + // STAB symbol's section field refers to a valid section index. Otherwise + // the symbol may error trying to load a section that does not exist. DataRefImpl SymDRI = Symbol.getRawDataRefImpl(); uint8_t NType = (MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type: @@ -1710,6 +1163,9 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, if (AllSymbols.empty() && Obj->isELF()) addDynamicElfSymbols(Obj, AllSymbols); + if (Obj->isWasm()) + addMissingWasmCodeSymbols(cast<WasmObjectFile>(Obj), AllSymbols); + BumpPtrAllocator A; StringSaver Saver(A); addPltEntries(Obj, AllSymbols, Saver); @@ -1803,16 +1259,14 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, llvm::sort(MappingSymbols); + ArrayRef<uint8_t> Bytes = arrayRefFromStringRef( + unwrapOrError(Section.getContents(), Obj->getFileName())); + + std::vector<std::unique_ptr<std::string>> SynthesizedLabelNames; if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) { // AMDGPU disassembler uses symbolizer for printing labels - std::unique_ptr<MCRelocationInfo> RelInfo( - TheTarget->createMCRelocationInfo(TripleName, Ctx)); - if (RelInfo) { - std::unique_ptr<MCSymbolizer> Symbolizer( - TheTarget->createMCSymbolizer( - TripleName, nullptr, nullptr, &Symbols, &Ctx, std::move(RelInfo))); - DisAsm->setSymbolizer(std::move(Symbolizer)); - } + addSymbolizer(Ctx, TheTarget, TripleName, DisAsm, SectionAddr, Bytes, + Symbols, SynthesizedLabelNames); } StringRef SegmentName = getSegmentName(MachO, Section); @@ -1828,9 +1282,6 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, SmallString<40> Comments; raw_svector_ostream CommentStream(Comments); - ArrayRef<uint8_t> Bytes = arrayRefFromStringRef( - unwrapOrError(Section.getContents(), Obj->getFileName())); - uint64_t VMAAdjustment = 0; if (shouldAdjustVA(Section)) VMAAdjustment = AdjustVMA; @@ -1877,7 +1328,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, } outs() << '\n'; - if (!NoLeadingAddr) + if (LeadingAddr) outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ", SectionAddr + Start + VMAAdjustment); if (Obj->isXCOFF() && SymbolDescription) { @@ -2008,18 +1459,22 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, LVP.update({Index, Section.getIndex()}, {Index + Size, Section.getIndex()}, Index + Size != End); + IP->setCommentStream(CommentStream); + PIP.printInst( *IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size), {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, FOS, "", *STI, &SP, Obj->getFileName(), &Rels, LVP); - FOS << CommentStream.str(); - Comments.clear(); + + IP->setCommentStream(llvm::nulls()); // If disassembly has failed, avoid analysing invalid/incomplete // instruction information. Otherwise, try to resolve the target // address (jump target or memory operand address) and print it on the // right of the instruction. if (Disassembled && MIA) { + // Branch targets are printed just after the instructions. + llvm::raw_ostream *TargetOS = &FOS; uint64_t Target; bool PrintTarget = MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target); @@ -2030,8 +1485,11 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, Target = *MaybeTarget; PrintTarget = true; // Do not print real address when symbolizing. - if (!SymbolizeOperands) - FOS << " # " << Twine::utohexstr(Target); + if (!SymbolizeOperands) { + // Memory operand addresses are printed as comments. + TargetOS = &CommentStream; + *TargetOS << "0x" << Twine::utohexstr(Target); + } } if (PrintTarget) { // In a relocatable object, the target's section must reside in @@ -2090,28 +1548,34 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, if (Demangle) TargetName = demangle(TargetName); - FOS << " <"; + *TargetOS << " <"; if (!Disp) { // Always Print the binary symbol precisely corresponding to // the target address. - FOS << TargetName; + *TargetOS << TargetName; } else if (!LabelAvailable) { // Always Print the binary symbol plus an offset if there's no // local label corresponding to the target address. - FOS << TargetName << "+0x" << Twine::utohexstr(Disp); + *TargetOS << TargetName << "+0x" << Twine::utohexstr(Disp); } else { - FOS << AllLabels[Target]; + *TargetOS << AllLabels[Target]; } - FOS << ">"; + *TargetOS << ">"; } else if (LabelAvailable) { - FOS << " <" << AllLabels[Target] << ">"; + *TargetOS << " <" << AllLabels[Target] << ">"; } + // By convention, each record in the comment stream should be + // terminated. + if (TargetOS == &CommentStream) + *TargetOS << "\n"; } } } - LVP.printAfterInst(FOS); - FOS << "\n"; + assert(Ctx.getAsmInfo()); + emitPostInstructionInfo(FOS, *Ctx.getAsmInfo(), *STI, + CommentStream.str(), LVP); + Comments.clear(); // Hexagon does this in pretty printer if (Obj->getArch() != Triple::hexagon) { @@ -2193,10 +1657,11 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { if (!MII) reportError(Obj->getFileName(), "no instruction info for target " + TripleName); - MCObjectFileInfo MOFI; - MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI); + MCContext Ctx(Triple(TripleName), AsmInfo.get(), MRI.get(), STI.get()); // FIXME: for now initialize MCObjectFileInfo with default values - MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx); + std::unique_ptr<MCObjectFileInfo> MOFI( + TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); + Ctx.setObjectFileInfo(MOFI.get()); std::unique_ptr<MCDisassembler> DisAsm( TheTarget->createMCDisassembler(*STI, Ctx)); @@ -2273,7 +1738,7 @@ void objdump::printRelocations(const ObjectFile *Obj) { for (std::pair<SectionRef, std::vector<SectionRef>> &P : SecToRelSec) { StringRef SecName = unwrapOrError(P.first.getName(), Obj->getFileName()); - outs() << "RELOCATION RECORDS FOR [" << SecName << "]:\n"; + outs() << "\nRELOCATION RECORDS FOR [" << SecName << "]:\n"; uint32_t OffsetPadding = (Obj->getBytesInAddress() > 4 ? 16 : 8); uint32_t TypePadding = 24; outs() << left_justify("OFFSET", OffsetPadding) << " " @@ -2296,7 +1761,6 @@ void objdump::printRelocations(const ObjectFile *Obj) { << "\n"; } } - outs() << "\n"; } } @@ -2356,16 +1820,13 @@ void objdump::printSectionHeaders(const ObjectFile *Obj) { size_t NameWidth = getMaxSectionNameWidth(Obj); size_t AddressWidth = 2 * Obj->getBytesInAddress(); bool HasLMAColumn = shouldDisplayLMA(Obj); + outs() << "\nSections:\n"; if (HasLMAColumn) - outs() << "Sections:\n" - "Idx " - << left_justify("Name", NameWidth) << " Size " + outs() << "Idx " << left_justify("Name", NameWidth) << " Size " << left_justify("VMA", AddressWidth) << " " << left_justify("LMA", AddressWidth) << " Type\n"; else - outs() << "Sections:\n" - "Idx " - << left_justify("Name", NameWidth) << " Size " + outs() << "Idx " << left_justify("Name", NameWidth) << " Size " << left_justify("VMA", AddressWidth) << " Type\n"; uint64_t Idx; @@ -2379,9 +1840,11 @@ void objdump::printSectionHeaders(const ObjectFile *Obj) { std::string Type = Section.isText() ? "TEXT" : ""; if (Section.isData()) - Type += Type.empty() ? "DATA" : " DATA"; + Type += Type.empty() ? "DATA" : ", DATA"; if (Section.isBSS()) - Type += Type.empty() ? "BSS" : " BSS"; + Type += Type.empty() ? "BSS" : ", BSS"; + if (Section.isDebugSection()) + Type += Type.empty() ? "DEBUG" : ", DEBUG"; if (HasLMAColumn) outs() << format("%3" PRIu64 " %-*s %08" PRIx64 " ", Idx, NameWidth, @@ -2394,7 +1857,6 @@ void objdump::printSectionHeaders(const ObjectFile *Obj) { Name.str().c_str(), Size) << format_hex_no_prefix(VMA, AddressWidth) << " " << Type << "\n"; } - outs() << "\n"; } void objdump::printSectionContents(const ObjectFile *Obj) { @@ -2450,7 +1912,7 @@ void objdump::printSectionContents(const ObjectFile *Obj) { void objdump::printSymbolTable(const ObjectFile *O, StringRef ArchiveName, StringRef ArchitectureName, bool DumpDynamic) { if (O->isCOFF() && !DumpDynamic) { - outs() << "SYMBOL TABLE:\n"; + outs() << "\nSYMBOL TABLE:\n"; printCOFFSymbolTable(cast<const COFFObjectFile>(O)); return; } @@ -2458,13 +1920,13 @@ void objdump::printSymbolTable(const ObjectFile *O, StringRef ArchiveName, const StringRef FileName = O->getFileName(); if (!DumpDynamic) { - outs() << "SYMBOL TABLE:\n"; + outs() << "\nSYMBOL TABLE:\n"; for (auto I = O->symbol_begin(); I != O->symbol_end(); ++I) printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic); return; } - outs() << "DYNAMIC SYMBOL TABLE:\n"; + outs() << "\nDYNAMIC SYMBOL TABLE:\n"; if (!O->isELF()) { reportWarning( "this operation is not currently supported for this file format", @@ -2736,7 +2198,7 @@ static void printFileHeaders(const ObjectFile *O) { StringRef Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; outs() << "start address: " - << "0x" << format(Fmt.data(), Address) << "\n\n"; + << "0x" << format(Fmt.data(), Address) << "\n"; } static void printArchiveChild(StringRef Filename, const Archive::Child &C) { @@ -2809,11 +2271,11 @@ static void checkForInvalidStartStopAddress(ObjectFile *Obj, return; } - if (StartAddress.getNumOccurrences() == 0) + if (!HasStartAddressFlag) reportWarning("no section has address less than 0x" + Twine::utohexstr(Stop) + " specified by --stop-address", Obj->getFileName()); - else if (StopAddress.getNumOccurrences() == 0) + else if (!HasStopAddressFlag) reportWarning("no section has address greater than or equal to 0x" + Twine::utohexstr(Start) + " specified by --start-address", Obj->getFileName()); @@ -2833,10 +2295,10 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr, outs() << A->getFileName() << "(" << O->getFileName() << ")"; else outs() << O->getFileName(); - outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n\n"; + outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n"; } - if (StartAddress.getNumOccurrences() || StopAddress.getNumOccurrences()) + if (HasStartAddressFlag || HasStopAddressFlag) checkForInvalidStartStopAddress(O, StartAddress, StopAddress); // Note: the order here matches GNU objdump for compatability. @@ -2956,33 +2418,246 @@ static void dumpInput(StringRef file) { reportError(errorCodeToError(object_error::invalid_file_type), file); } +template <typename T> +static void parseIntArg(const llvm::opt::InputArgList &InputArgs, int ID, + T &Value) { + if (const opt::Arg *A = InputArgs.getLastArg(ID)) { + StringRef V(A->getValue()); + if (!llvm::to_integer(V, Value, 0)) { + reportCmdLineError(A->getSpelling() + + ": expected a non-negative integer, but got '" + V + + "'"); + } + } +} + +static std::vector<std::string> +commaSeparatedValues(const llvm::opt::InputArgList &InputArgs, int ID) { + std::vector<std::string> Values; + for (StringRef Value : InputArgs.getAllArgValues(ID)) { + llvm::SmallVector<StringRef, 2> SplitValues; + llvm::SplitString(Value, SplitValues, ","); + for (StringRef SplitValue : SplitValues) + Values.push_back(SplitValue.str()); + } + return Values; +} + +static void parseOtoolOptions(const llvm::opt::InputArgList &InputArgs) { + MachOOpt = true; + FullLeadingAddr = true; + PrintImmHex = true; + + ArchName = InputArgs.getLastArgValue(OTOOL_arch).str(); + LinkOptHints = InputArgs.hasArg(OTOOL_C); + if (InputArgs.hasArg(OTOOL_d)) + FilterSections.push_back("__DATA,__data"); + DylibId = InputArgs.hasArg(OTOOL_D); + UniversalHeaders = InputArgs.hasArg(OTOOL_f); + DataInCode = InputArgs.hasArg(OTOOL_G); + FirstPrivateHeader = InputArgs.hasArg(OTOOL_h); + IndirectSymbols = InputArgs.hasArg(OTOOL_I); + ShowRawInsn = InputArgs.hasArg(OTOOL_j); + PrivateHeaders = InputArgs.hasArg(OTOOL_l); + DylibsUsed = InputArgs.hasArg(OTOOL_L); + MCPU = InputArgs.getLastArgValue(OTOOL_mcpu_EQ).str(); + ObjcMetaData = InputArgs.hasArg(OTOOL_o); + DisSymName = InputArgs.getLastArgValue(OTOOL_p).str(); + InfoPlist = InputArgs.hasArg(OTOOL_P); + Relocations = InputArgs.hasArg(OTOOL_r); + if (const Arg *A = InputArgs.getLastArg(OTOOL_s)) { + auto Filter = (A->getValue(0) + StringRef(",") + A->getValue(1)).str(); + FilterSections.push_back(Filter); + } + if (InputArgs.hasArg(OTOOL_t)) + FilterSections.push_back("__TEXT,__text"); + Verbose = InputArgs.hasArg(OTOOL_v) || InputArgs.hasArg(OTOOL_V) || + InputArgs.hasArg(OTOOL_o); + SymbolicOperands = InputArgs.hasArg(OTOOL_V); + if (InputArgs.hasArg(OTOOL_x)) + FilterSections.push_back(",__text"); + LeadingAddr = LeadingHeaders = !InputArgs.hasArg(OTOOL_X); + + InputFilenames = InputArgs.getAllArgValues(OTOOL_INPUT); + if (InputFilenames.empty()) + reportCmdLineError("no input file"); + + for (const Arg *A : InputArgs) { + const Option &O = A->getOption(); + if (O.getGroup().isValid() && O.getGroup().getID() == OTOOL_grp_obsolete) { + reportCmdLineWarning(O.getPrefixedName() + + " is obsolete and not implemented"); + } + } +} + +static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) { + parseIntArg(InputArgs, OBJDUMP_adjust_vma_EQ, AdjustVMA); + AllHeaders = InputArgs.hasArg(OBJDUMP_all_headers); + ArchName = InputArgs.getLastArgValue(OBJDUMP_arch_name_EQ).str(); + ArchiveHeaders = InputArgs.hasArg(OBJDUMP_archive_headers); + Demangle = InputArgs.hasArg(OBJDUMP_demangle); + Disassemble = InputArgs.hasArg(OBJDUMP_disassemble); + DisassembleAll = InputArgs.hasArg(OBJDUMP_disassemble_all); + SymbolDescription = InputArgs.hasArg(OBJDUMP_symbol_description); + DisassembleSymbols = + commaSeparatedValues(InputArgs, OBJDUMP_disassemble_symbols_EQ); + DisassembleZeroes = InputArgs.hasArg(OBJDUMP_disassemble_zeroes); + if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_dwarf_EQ)) { + DwarfDumpType = + StringSwitch<DIDumpType>(A->getValue()).Case("frames", DIDT_DebugFrame); + } + DynamicRelocations = InputArgs.hasArg(OBJDUMP_dynamic_reloc); + FaultMapSection = InputArgs.hasArg(OBJDUMP_fault_map_section); + FileHeaders = InputArgs.hasArg(OBJDUMP_file_headers); + SectionContents = InputArgs.hasArg(OBJDUMP_full_contents); + PrintLines = InputArgs.hasArg(OBJDUMP_line_numbers); + InputFilenames = InputArgs.getAllArgValues(OBJDUMP_INPUT); + MachOOpt = InputArgs.hasArg(OBJDUMP_macho); + MCPU = InputArgs.getLastArgValue(OBJDUMP_mcpu_EQ).str(); + MAttrs = commaSeparatedValues(InputArgs, OBJDUMP_mattr_EQ); + ShowRawInsn = !InputArgs.hasArg(OBJDUMP_no_show_raw_insn); + LeadingAddr = !InputArgs.hasArg(OBJDUMP_no_leading_addr); + RawClangAST = InputArgs.hasArg(OBJDUMP_raw_clang_ast); + Relocations = InputArgs.hasArg(OBJDUMP_reloc); + PrintImmHex = + InputArgs.hasFlag(OBJDUMP_print_imm_hex, OBJDUMP_no_print_imm_hex, false); + PrivateHeaders = InputArgs.hasArg(OBJDUMP_private_headers); + FilterSections = InputArgs.getAllArgValues(OBJDUMP_section_EQ); + SectionHeaders = InputArgs.hasArg(OBJDUMP_section_headers); + ShowLMA = InputArgs.hasArg(OBJDUMP_show_lma); + PrintSource = InputArgs.hasArg(OBJDUMP_source); + parseIntArg(InputArgs, OBJDUMP_start_address_EQ, StartAddress); + HasStartAddressFlag = InputArgs.hasArg(OBJDUMP_start_address_EQ); + parseIntArg(InputArgs, OBJDUMP_stop_address_EQ, StopAddress); + HasStopAddressFlag = InputArgs.hasArg(OBJDUMP_stop_address_EQ); + SymbolTable = InputArgs.hasArg(OBJDUMP_syms); + SymbolizeOperands = InputArgs.hasArg(OBJDUMP_symbolize_operands); + DynamicSymbolTable = InputArgs.hasArg(OBJDUMP_dynamic_syms); + TripleName = InputArgs.getLastArgValue(OBJDUMP_triple_EQ).str(); + UnwindInfo = InputArgs.hasArg(OBJDUMP_unwind_info); + Wide = InputArgs.hasArg(OBJDUMP_wide); + Prefix = InputArgs.getLastArgValue(OBJDUMP_prefix).str(); + parseIntArg(InputArgs, OBJDUMP_prefix_strip, PrefixStrip); + if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_debug_vars_EQ)) { + DbgVariables = StringSwitch<DebugVarsFormat>(A->getValue()) + .Case("ascii", DVASCII) + .Case("unicode", DVUnicode); + } + parseIntArg(InputArgs, OBJDUMP_debug_vars_indent_EQ, DbgIndent); + + parseMachOOptions(InputArgs); + + // Parse -M (--disassembler-options) and deprecated + // --x86-asm-syntax={att,intel}. + // + // Note, for x86, the asm dialect (AssemblerDialect) is initialized when the + // MCAsmInfo is constructed. MCInstPrinter::applyTargetSpecificCLOption is + // called too late. For now we have to use the internal cl::opt option. + const char *AsmSyntax = nullptr; + for (const auto *A : InputArgs.filtered(OBJDUMP_disassembler_options_EQ, + OBJDUMP_x86_asm_syntax_att, + OBJDUMP_x86_asm_syntax_intel)) { + switch (A->getOption().getID()) { + case OBJDUMP_x86_asm_syntax_att: + AsmSyntax = "--x86-asm-syntax=att"; + continue; + case OBJDUMP_x86_asm_syntax_intel: + AsmSyntax = "--x86-asm-syntax=intel"; + continue; + } + + SmallVector<StringRef, 2> Values; + llvm::SplitString(A->getValue(), Values, ","); + for (StringRef V : Values) { + if (V == "att") + AsmSyntax = "--x86-asm-syntax=att"; + else if (V == "intel") + AsmSyntax = "--x86-asm-syntax=intel"; + else + DisassemblerOptions.push_back(V.str()); + } + } + if (AsmSyntax) { + const char *Argv[] = {"llvm-objdump", AsmSyntax}; + llvm::cl::ParseCommandLineOptions(2, Argv); + } + + // objdump defaults to a.out if no filenames specified. + if (InputFilenames.empty()) + InputFilenames.push_back("a.out"); +} + int main(int argc, char **argv) { using namespace llvm; InitLLVM X(argc, argv); - const cl::OptionCategory *OptionFilters[] = {&ObjdumpCat, &MachOCat}; - cl::HideUnrelatedOptions(OptionFilters); + + ToolName = argv[0]; + std::unique_ptr<CommonOptTable> T; + OptSpecifier Unknown, HelpFlag, HelpHiddenFlag, VersionFlag; + + StringRef Stem = sys::path::stem(ToolName); + auto Is = [=](StringRef Tool) { + // We need to recognize the following filenames: + // + // llvm-objdump -> objdump + // llvm-otool-10.exe -> otool + // powerpc64-unknown-freebsd13-objdump -> objdump + auto I = Stem.rfind_insensitive(Tool); + return I != StringRef::npos && + (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); + }; + if (Is("otool")) { + T = std::make_unique<OtoolOptTable>(); + Unknown = OTOOL_UNKNOWN; + HelpFlag = OTOOL_help; + HelpHiddenFlag = OTOOL_help_hidden; + VersionFlag = OTOOL_version; + } else { + T = std::make_unique<ObjdumpOptTable>(); + Unknown = OBJDUMP_UNKNOWN; + HelpFlag = OBJDUMP_help; + HelpHiddenFlag = OBJDUMP_help_hidden; + VersionFlag = OBJDUMP_version; + } + + BumpPtrAllocator A; + StringSaver Saver(A); + opt::InputArgList InputArgs = + T->parseArgs(argc, argv, Unknown, Saver, + [&](StringRef Msg) { reportCmdLineError(Msg); }); + + if (InputArgs.size() == 0 || InputArgs.hasArg(HelpFlag)) { + T->printHelp(ToolName); + return 0; + } + if (InputArgs.hasArg(HelpHiddenFlag)) { + T->printHelp(ToolName, /*show_hidden=*/true); + return 0; + } // Initialize targets and assembly printers/parsers. InitializeAllTargetInfos(); InitializeAllTargetMCs(); InitializeAllDisassemblers(); - // Register the target printer for --version. - cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + if (InputArgs.hasArg(VersionFlag)) { + cl::PrintVersionMessage(); + if (!Is("otool")) { + outs() << '\n'; + TargetRegistry::printRegisteredTargetsForVersion(outs()); + } + return 0; + } - cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n", nullptr, - /*EnvVar=*/nullptr, - /*LongOptionsUseDoubleDash=*/true); + if (Is("otool")) + parseOtoolOptions(InputArgs); + else + parseObjdumpOptions(InputArgs); if (StartAddress >= StopAddress) reportCmdLineError("start address should be less than stop address"); - ToolName = argv[0]; - - // Defaults to a.out if no filenames specified. - if (InputFilenames.empty()) - InputFilenames.push_back("a.out"); - // Removes trailing separators from prefix. while (!Prefix.empty() && sys::path::is_separator(Prefix.back())) Prefix.pop_back(); @@ -3001,10 +2676,10 @@ int main(int argc, char **argv) { !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && !(MachOOpt && (Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie || - FirstPrivateHeader || IndirectSymbols || InfoPlist || LazyBind || - LinkOptHints || ObjcMetaData || Rebase || UniversalHeaders || - WeakBind || !FilterSections.empty()))) { - cl::PrintHelpMessage(); + FirstPrivateHeader || FunctionStarts || IndirectSymbols || InfoPlist || + LazyBind || LinkOptHints || ObjcMetaData || Rebase || Rpaths || + UniversalHeaders || WeakBind || !FilterSections.empty()))) { + T->printHelp(ToolName); return 2; } diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h index 99bf191a301e..33fb3f207f8e 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -1,3 +1,4 @@ +//===--- llvm-objdump.h -----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,7 +13,6 @@ #include "llvm/DebugInfo/DIContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/Object/Archive.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DataTypes.h" @@ -30,26 +30,37 @@ class RelocationRef; namespace objdump { -extern cl::opt<bool> ArchiveHeaders; -extern cl::opt<bool> Demangle; -extern cl::opt<bool> Disassemble; -extern cl::opt<bool> DisassembleAll; -extern cl::opt<DIDumpType> DwarfDumpType; -extern cl::list<std::string> FilterSections; -extern cl::list<std::string> MAttrs; -extern cl::opt<std::string> MCPU; -extern cl::opt<bool> NoShowRawInsn; -extern cl::opt<bool> NoLeadingAddr; -extern cl::opt<std::string> Prefix; -extern cl::opt<bool> PrintImmHex; -extern cl::opt<bool> PrivateHeaders; -extern cl::opt<bool> Relocations; -extern cl::opt<bool> SectionHeaders; -extern cl::opt<bool> SectionContents; -extern cl::opt<bool> SymbolDescription; -extern cl::opt<bool> SymbolTable; -extern cl::opt<std::string> TripleName; -extern cl::opt<bool> UnwindInfo; +enum DebugVarsFormat { + DVDisabled, + DVUnicode, + DVASCII, +}; + +extern bool ArchiveHeaders; +extern int DbgIndent; +extern DebugVarsFormat DbgVariables; +extern bool Demangle; +extern bool Disassemble; +extern bool DisassembleAll; +extern DIDumpType DwarfDumpType; +extern std::vector<std::string> FilterSections; +extern bool LeadingAddr; +extern std::vector<std::string> MAttrs; +extern std::string MCPU; +extern std::string Prefix; +extern uint32_t PrefixStrip; +extern bool PrintImmHex; +extern bool PrintLines; +extern bool PrintSource; +extern bool PrivateHeaders; +extern bool Relocations; +extern bool SectionHeaders; +extern bool SectionContents; +extern bool ShowRawInsn; +extern bool SymbolDescription; +extern bool SymbolTable; +extern std::string TripleName; +extern bool UnwindInfo; extern StringSet<> FoundSectionSet; diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp index babdb56a718c..ef299ea9d482 100644 --- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -350,13 +350,13 @@ static bool isMyCode(const SymbolGroup &Group) { StringRef Name = Group.name(); if (Name.startswith("Import:")) return false; - if (Name.endswith_lower(".dll")) + if (Name.endswith_insensitive(".dll")) return false; - if (Name.equals_lower("* linker *")) + if (Name.equals_insensitive("* linker *")) return false; - if (Name.startswith_lower("f:\\binaries\\Intermediate\\vctools")) + if (Name.startswith_insensitive("f:\\binaries\\Intermediate\\vctools")) return false; - if (Name.startswith_lower("f:\\dd\\vctools\\crt")) + if (Name.startswith_insensitive("f:\\dd\\vctools\\crt")) return false; return true; } diff --git a/llvm/tools/llvm-pdbutil/InputFile.cpp b/llvm/tools/llvm-pdbutil/InputFile.cpp index b316882de64d..40b35625b6f8 100644 --- a/llvm/tools/llvm-pdbutil/InputFile.cpp +++ b/llvm/tools/llvm-pdbutil/InputFile.cpp @@ -288,7 +288,8 @@ Expected<InputFile> InputFile::open(StringRef Path, bool AllowUnknownFile) { formatv("File {0} is not a supported file type", Path), inconvertibleErrorCode()); - auto Result = MemoryBuffer::getFile(Path, -1LL, false); + auto Result = MemoryBuffer::getFile(Path, /*IsText=*/false, + /*RequiresNullTerminator=*/false); if (!Result) return make_error<StringError>( formatv("File {0} could not be opened", Path), Result.getError()); diff --git a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp index 787785c34b78..3e8b1e88657b 100644 --- a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -261,6 +261,9 @@ static std::string formatMachineType(CPUType Cpu) { RETURN_CASE(CPUType, ARM_WMMX, "arm wmmx"); RETURN_CASE(CPUType, ARM7, "arm 7"); RETURN_CASE(CPUType, ARM64, "arm64"); + RETURN_CASE(CPUType, ARM64EC, "arm64ec"); + RETURN_CASE(CPUType, ARM64X, "arm64x"); + RETURN_CASE(CPUType, HybridX86ARM64, "hybrid x86 arm64"); RETURN_CASE(CPUType, Omni, "omni"); RETURN_CASE(CPUType, Ia64, "intel itanium ia64"); RETURN_CASE(CPUType, Ia64_2, "intel itanium ia64 2"); @@ -559,7 +562,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, P.format(" `{0}`", Constant.Name); AutoIndent Indent(P, 7); P.formatLine("type = {0}, value = {1}", typeIndex(Constant.Type), - Constant.Value.toString(10)); + toString(Constant.Value, 10)); return Error::success(); } diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp index 8e46a97272d5..08006e9c62d4 100644 --- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -557,7 +557,7 @@ Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, EnumeratorRecord &Enum) { P.format(" [{0} = {1}]", Enum.Name, - Enum.Value.toString(10, Enum.Value.isSigned())); + toString(Enum.Value, 10, Enum.Value.isSigned())); return Error::success(); } diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp index 19f4880ab5eb..b152ebd6dccb 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -748,7 +748,7 @@ static ExitOnError ExitOnErr; static void yamlToPdb(StringRef Path) { BumpPtrAllocator Allocator; ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = - MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + MemoryBuffer::getFileOrSTDIN(Path, /*IsText=*/false, /*RequiresNullTerminator=*/false); if (ErrorOrBuffer.getError()) { @@ -868,7 +868,6 @@ static void pdb2Yaml(StringRef Path) { auto &File = loadPDB(Path, Session); auto O = std::make_unique<YAMLOutputStyle>(File); - O = std::make_unique<YAMLOutputStyle>(File); ExitOnErr(O->dump()); } @@ -1431,6 +1430,8 @@ int main(int Argc, const char **Argv) { InitLLVM X(Argc, Argv); ExitOnErr.setBanner("llvm-pdbutil: "); + cl::HideUnrelatedOptions( + {&opts::TypeCategory, &opts::FilterCategory, &opts::OtherOptions}); cl::ParseCommandLineOptions(Argc, Argv, "LLVM PDB Dumper\n"); if (opts::BytesSubcommand) { diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index 8dc43924c067..66d70120ac9b 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -20,6 +20,7 @@ #include "llvm/ProfileData/SampleProfReader.h" #include "llvm/ProfileData/SampleProfWriter.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Discriminator.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" @@ -54,6 +55,14 @@ static void warn(Twine Message, std::string Whence = "", WithColor::note() << Hint << "\n"; } +static void warn(Error E, StringRef Whence = "") { + if (E.isA<InstrProfError>()) { + handleAllErrors(std::move(E), [&](const InstrProfError &IPE) { + warn(IPE.message(), std::string(Whence), std::string("")); + }); + } +} + static void exitWithError(Twine Message, std::string Whence = "", std::string Hint = "") { WithColor::error(); @@ -243,7 +252,8 @@ static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, auto Reader = std::move(ReaderOrErr.get()); bool IsIRProfile = Reader->isIRLevelProfile(); bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); - if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { + if (Error E = WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { + consumeError(std::move(E)); WC->Errors.emplace_back( make_error<StringError>( "Merge IR generated profile with Clang generated profile.", @@ -297,16 +307,19 @@ static void writeInstrProfile(StringRef OutputFilename, InstrProfWriter &Writer) { std::error_code EC; raw_fd_ostream Output(OutputFilename.data(), EC, - OutputFormat == PF_Text ? sys::fs::OF_Text + OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF : sys::fs::OF_None); if (EC) exitWithErrorCode(EC, OutputFilename); if (OutputFormat == PF_Text) { if (Error E = Writer.writeText(Output)) - exitWithError(std::move(E)); + warn(std::move(E)); } else { - Writer.write(Output); + if (Output.is_displayed()) + exitWithError("cannot write a non-text format profile to the terminal"); + if (Error E = Writer.write(Output)) + warn(std::move(E)); } } @@ -315,12 +328,9 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, unsigned NumThreads, FailureMode FailMode) { - if (OutputFilename.compare("-") == 0) - exitWithError("Cannot write indexed profdata format to stdout."); - if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) - exitWithError("Unknown format is specified."); + exitWithError("unknown format is specified"); std::mutex ErrorLock; SmallSet<instrprof_error, 4> WriterErrorCodes; @@ -383,7 +393,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, } if (NumErrors == Inputs.size() || (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) - exitWithError("No profiles could be merged."); + exitWithError("no profile can be merged"); writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer); } @@ -441,6 +451,25 @@ static void updateInstrProfileEntry(InstrProfileEntry &IFE, const uint64_t ColdPercentileIdx = 15; const uint64_t HotPercentileIdx = 11; +using sampleprof::FSDiscriminatorPass; + +// Internal options to set FSDiscriminatorPass. Used in merge and show +// commands. +static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption( + "fs-discriminator-pass", cl::init(PassLast), cl::Hidden, + cl::desc("Zero out the discriminator bits for the FS discrimiantor " + "pass beyond this value. The enum values are defined in " + "Support/Discriminator.h"), + cl::values(clEnumVal(Base, "Use base discriminators only"), + clEnumVal(Pass1, "Use base and pass 1 discriminators"), + clEnumVal(Pass2, "Use base and pass 1-2 discriminators"), + clEnumVal(Pass3, "Use base and pass 1-3 discriminators"), + clEnumVal(PassLast, "Use all discriminator bits (default)"))); + +static unsigned getDiscriminatorMask() { + return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue())); +} + /// Adjust the instr profile in \p WC based on the sample profile in /// \p Reader. static void @@ -522,18 +551,18 @@ static void supplementInstrProfile( unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, unsigned InstrProfColdThreshold) { if (OutputFilename.compare("-") == 0) - exitWithError("Cannot write indexed profdata format to stdout."); + exitWithError("cannot write indexed profdata format to stdout"); if (Inputs.size() != 1) - exitWithError("Expect one input to be an instr profile."); + exitWithError("expect one input to be an instr profile"); if (Inputs[0].Weight != 1) - exitWithError("Expect instr profile doesn't have weight."); + exitWithError("expect instr profile doesn't have weight"); StringRef InstrFilename = Inputs[0].Filename; // Read sample profile. LLVMContext Context; - auto ReaderOrErr = - sampleprof::SampleProfileReader::create(SampleFilename.str(), Context); + auto ReaderOrErr = sampleprof::SampleProfileReader::create( + SampleFilename.str(), Context, FSDiscriminatorPassOption); if (std::error_code EC = ReaderOrErr.getError()) exitWithErrorCode(EC, SampleFilename); auto Reader = std::move(ReaderOrErr.get()); @@ -564,12 +593,13 @@ remapSamples(const sampleprof::FunctionSamples &Samples, Result.addTotalSamples(Samples.getTotalSamples()); Result.addHeadSamples(Samples.getHeadSamples()); for (const auto &BodySample : Samples.getBodySamples()) { - Result.addBodySamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, + uint32_t MaskedDiscriminator = + BodySample.first.Discriminator & getDiscriminatorMask(); + Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator, BodySample.second.getSamples()); for (const auto &Target : BodySample.second.getCallTargets()) { Result.addCalledTargetSamples(BodySample.first.LineOffset, - BodySample.first.Discriminator, + MaskedDiscriminator, Remapper(Target.first()), Target.second); } } @@ -656,15 +686,19 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, StringRef OutputFilename, ProfileFormat OutputFormat, StringRef ProfileSymbolListFile, bool CompressAllSections, - bool UseMD5, bool GenPartialProfile, FailureMode FailMode) { + bool UseMD5, bool GenPartialProfile, + bool SampleMergeColdContext, bool SampleTrimColdContext, + bool SampleColdContextFrameDepth, FailureMode FailMode) { using namespace sampleprof; StringMap<FunctionSamples> ProfileMap; SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; LLVMContext Context; sampleprof::ProfileSymbolList WriterList; Optional<bool> ProfileIsProbeBased; + Optional<bool> ProfileIsCS; for (const auto &Input : Inputs) { - auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); + auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, + FSDiscriminatorPassOption); if (std::error_code EC = ReaderOrErr.getError()) { warnOrExitGivenError(FailMode, EC, Input.Filename); continue; @@ -683,11 +717,14 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, } StringMap<FunctionSamples> &Profiles = Reader->getProfiles(); - if (ProfileIsProbeBased && + if (ProfileIsProbeBased.hasValue() && ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased) exitWithError( "cannot merge probe-based profile with non-probe-based profile"); ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased; + if (ProfileIsCS.hasValue() && ProfileIsCS != FunctionSamples::ProfileIsCS) + exitWithError("cannot merge CS profile with non-CS profile"); + ProfileIsCS = FunctionSamples::ProfileIsCS; for (StringMap<FunctionSamples>::iterator I = Profiles.begin(), E = Profiles.end(); I != E; ++I) { @@ -696,7 +733,7 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, Remapper ? remapSamples(I->second, *Remapper, Result) : FunctionSamples(); FunctionSamples &Samples = Remapper ? Remapped : I->second; - StringRef FName = Samples.getName(); + StringRef FName = Samples.getNameWithContext(); MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight)); if (Result != sampleprof_error::success) { std::error_code EC = make_error_code(Result); @@ -709,6 +746,22 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, if (ReaderList) WriterList.merge(*ReaderList); } + + if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) { + // Use threshold calculated from profile summary unless specified. + SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); + auto Summary = Builder.computeSummaryForProfiles(ProfileMap); + uint64_t SampleProfColdThreshold = + ProfileSummaryBuilder::getColdCountThreshold( + (Summary->getDetailedSummary())); + + // Trim and merge cold context profile using cold threshold above; + SampleContextTrimmer(ProfileMap) + .trimAndMergeColdContextProfiles( + SampleProfColdThreshold, SampleTrimColdContext, + SampleMergeColdContext, SampleColdContextFrameDepth); + } + auto WriterOrErr = SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); if (std::error_code EC = WriterOrErr.getError()) @@ -720,7 +773,8 @@ mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, auto Buffer = getInputFileBuf(ProfileSymbolListFile); handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, CompressAllSections, UseMD5, GenPartialProfile); - Writer->write(ProfileMap); + if (std::error_code EC = Writer->write(ProfileMap)) + exitWithErrorCode(std::move(EC)); } static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { @@ -729,7 +783,7 @@ static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { uint64_t Weight; if (WeightStr.getAsInteger(10, Weight) || Weight < 1) - exitWithError("Input weight must be a positive integer."); + exitWithError("input weight must be a positive integer"); return {std::string(FileName), Weight}; } @@ -808,8 +862,7 @@ static int merge_main(int argc, const char *argv[]) { cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), cl::aliasopt(RemappingFile)); cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::Required, - cl::desc("Output file")); + cl::init("-"), cl::desc("Output file")); cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), cl::aliasopt(OutputFilename)); cl::opt<ProfileKinds> ProfileKind( @@ -851,6 +904,18 @@ static int merge_main(int argc, const char *argv[]) { "use-md5", cl::init(false), cl::Hidden, cl::desc("Choose to use MD5 to represent string in name table (only " "meaningful for -extbinary)")); + cl::opt<bool> SampleMergeColdContext( + "sample-merge-cold-context", cl::init(false), cl::Hidden, + cl::desc( + "Merge context sample profiles whose count is below cold threshold")); + cl::opt<bool> SampleTrimColdContext( + "sample-trim-cold-context", cl::init(false), cl::Hidden, + cl::desc( + "Trim context sample profiles whose count is below cold threshold")); + cl::opt<uint32_t> SampleColdContextFrameDepth( + "sample-frame-depth-for-cold-context", cl::init(1), cl::ZeroOrMore, + cl::desc("Keep the last K frames while merging cold profile. 1 means the " + "context-less base profile")); cl::opt<bool> GenPartialProfile( "gen-partial-profile", cl::init(false), cl::Hidden, cl::desc("Generate a partial profile (only meaningful for -extbinary)")); @@ -866,16 +931,16 @@ static int merge_main(int argc, const char *argv[]) { "sample profile, if the ratio of the number of zero counters " "divided by the the total number of counters is above the " "threshold, the profile of the function will be regarded as " - "being harmful for performance and will be dropped. ")); + "being harmful for performance and will be dropped.")); cl::opt<unsigned> SupplMinSizeThreshold( "suppl-min-size-threshold", cl::init(10), cl::Hidden, cl::desc("If the size of a function is smaller than the threshold, " "assume it can be inlined by PGO early inliner and it won't " - "be adjusted based on sample profile. ")); + "be adjusted based on sample profile.")); cl::opt<unsigned> InstrProfColdThreshold( "instr-prof-cold-threshold", cl::init(0), cl::Hidden, cl::desc("User specified cold threshold for instr profile which will " - "override the cold threshold got from profile summary. ")); + "override the cold threshold got from profile summary.")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); @@ -891,7 +956,7 @@ static int merge_main(int argc, const char *argv[]) { parseInputFilenamesFile(Buffer.get(), WeightedInputs); if (WeightedInputs.empty()) - exitWithError("No input files specified. See " + + exitWithError("no input files specified. See " + sys::path::filename(argv[0]) + " -help"); if (DumpInputFileList) { @@ -921,7 +986,9 @@ static int merge_main(int argc, const char *argv[]) { else mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, OutputFormat, ProfileSymbolListFile, CompressAllSections, - UseMD5, GenPartialProfile, FailureMode); + UseMD5, GenPartialProfile, SampleMergeColdContext, + SampleTrimColdContext, SampleColdContextFrameDepth, + FailureMode); return 0; } @@ -938,7 +1005,7 @@ static void overlapInstrProfile(const std::string &BaseFilename, OverlapStats Overlap; Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS); if (E) - exitWithError(std::move(E), "Error in getting profile count sums"); + exitWithError(std::move(E), "error in getting profile count sums"); if (Overlap.Base.CountSum < 1.0f) { OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n"; exit(0); @@ -1548,14 +1615,15 @@ void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) { StringMap<const FunctionSamples *> BaseFuncProf; const auto &BaseProfiles = BaseReader->getProfiles(); for (const auto &BaseFunc : BaseProfiles) { - BaseFuncProf.try_emplace(BaseFunc.second.getName(), &(BaseFunc.second)); + BaseFuncProf.try_emplace(BaseFunc.second.getNameWithContext(), + &(BaseFunc.second)); } ProfOverlap.UnionCount = BaseFuncProf.size(); const auto &TestProfiles = TestReader->getProfiles(); for (const auto &TestFunc : TestProfiles) { SampleOverlapStats FuncOverlap; - FuncOverlap.TestName = TestFunc.second.getName(); + FuncOverlap.TestName = TestFunc.second.getNameWithContext(); assert(TestStats.count(FuncOverlap.TestName) && "TestStats should have records for all functions in test profile " "except inlinees"); @@ -1582,7 +1650,7 @@ void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) { // Two functions match with each other. Compute function-level overlap and // aggregate them into profile-level overlap. - FuncOverlap.BaseName = Match->second->getName(); + FuncOverlap.BaseName = Match->second->getNameWithContext(); assert(BaseStats.count(FuncOverlap.BaseName) && "BaseStats should have records for all functions in base profile " "except inlinees"); @@ -1631,10 +1699,11 @@ void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) { // Traverse through functions in base profile but not in test profile. for (const auto &F : BaseFuncProf) { - assert(BaseStats.count(F.second->getName()) && + assert(BaseStats.count(F.second->getNameWithContext()) && "BaseStats should have records for all functions in base profile " "except inlinees"); - const FuncSampleStats &FuncStats = BaseStats[F.second->getName()]; + const FuncSampleStats &FuncStats = + BaseStats[F.second->getNameWithContext()]; ++ProfOverlap.BaseUniqueCount; ProfOverlap.BaseUniqueSample += FuncStats.SampleSum; @@ -1665,7 +1734,7 @@ void SampleOverlapAggregator::initializeSampleProfileOverlap() { FuncSampleStats FuncStats; getFuncSampleStats(I.second, FuncStats, BaseHotThreshold); ProfOverlap.BaseSample += FuncStats.SampleSum; - BaseStats.try_emplace(I.second.getName(), FuncStats); + BaseStats.try_emplace(I.second.getNameWithContext(), FuncStats); } const auto &TestProf = TestReader->getProfiles(); @@ -1674,7 +1743,7 @@ void SampleOverlapAggregator::initializeSampleProfileOverlap() { FuncSampleStats FuncStats; getFuncSampleStats(I.second, FuncStats, TestHotThreshold); ProfOverlap.TestSample += FuncStats.SampleSum; - TestStats.try_emplace(I.second.getName(), FuncStats); + TestStats.try_emplace(I.second.getNameWithContext(), FuncStats); } ProfOverlap.BaseName = StringRef(BaseFilename); @@ -1815,11 +1884,13 @@ std::error_code SampleOverlapAggregator::loadProfiles() { using namespace sampleprof; LLVMContext Context; - auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context); + auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, + FSDiscriminatorPassOption); if (std::error_code EC = BaseReaderOrErr.getError()) exitWithErrorCode(EC, BaseFilename); - auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context); + auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, + FSDiscriminatorPassOption); if (std::error_code EC = TestReaderOrErr.getError()) exitWithErrorCode(EC, TestFilename); @@ -1833,6 +1904,8 @@ std::error_code SampleOverlapAggregator::loadProfiles() { if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased()) exitWithError( "cannot compare probe-based profile with non-probe-based profile"); + if (BaseReader->profileIsCS() != TestReader->profileIsCS()) + exitWithError("cannot compare CS profile with non-CS profile"); // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in // profile summary. @@ -1888,21 +1961,24 @@ static int overlap_main(int argc, const char *argv[]) { cl::opt<std::string> Output("output", cl::value_desc("output"), cl::init("-"), cl::desc("Output file")); cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); - cl::opt<bool> IsCS("cs", cl::init(false), - cl::desc("For context sensitive counts")); + cl::opt<bool> IsCS( + "cs", cl::init(false), + cl::desc("For context sensitive PGO counts. Does not work with CSSPGO.")); cl::opt<unsigned long long> ValueCutoff( "value-cutoff", cl::init(-1), cl::desc( - "Function level overlap information for every function in test " + "Function level overlap information for every function (with calling " + "context for csspgo) in test " "profile with max count value greater then the parameter value")); cl::opt<std::string> FuncNameFilter( "function", - cl::desc("Function level overlap information for matching functions")); + cl::desc("Function level overlap information for matching functions. For " + "CSSPGO this takes a a function name with calling context")); cl::opt<unsigned long long> SimilarityCutoff( "similarity-cutoff", cl::init(0), - cl::desc( - "For sample profiles, list function names for overlapped functions " - "with similarities below the cutoff (percentage times 10000).")); + cl::desc("For sample profiles, list function names (with calling context " + "for csspgo) for overlapped functions " + "with similarities below the cutoff (percentage times 10000).")); cl::opt<ProfileKinds> ProfileKind( cl::desc("Profile kind:"), cl::init(instr), cl::values(clEnumVal(instr, "Instrumentation profile (default)"), @@ -1910,7 +1986,7 @@ static int overlap_main(int argc, const char *argv[]) { cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); std::error_code EC; - raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_Text); + raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF); if (EC) exitWithErrorCode(EC, Output); @@ -1926,7 +2002,8 @@ static int overlap_main(int argc, const char *argv[]) { return 0; } -typedef struct ValueSitesStats { +namespace { +struct ValueSitesStats { ValueSitesStats() : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), TotalNumValues(0) {} @@ -1934,7 +2011,8 @@ typedef struct ValueSitesStats { uint64_t TotalNumValueSitesWithValueProfile; uint64_t TotalNumValues; std::vector<unsigned> ValueSitesHistogram; -} ValueSitesStats; +}; +} // namespace static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, ValueSitesStats &Stats, raw_fd_ostream &OS, @@ -1991,7 +2069,7 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, bool ShowAllFunctions, bool ShowCS, uint64_t ValueCutoff, bool OnlyListBelow, const std::string &ShowFunction, bool TextFormat, - raw_fd_ostream &OS) { + bool ShowBinaryIds, raw_fd_ostream &OS) { auto ReaderOrErr = InstrProfReader::create(Filename); std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs); if (ShowDetailedSummary && Cutoffs.empty()) { @@ -2173,6 +2251,11 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, OS << "Total count: " << PS->getTotalCount() << "\n"; PS->printDetailedSummary(OS); } + + if (ShowBinaryIds) + if (Error E = Reader->printBinaryIds(OS)) + exitWithError(std::move(E), Filename); + return 0; } @@ -2307,9 +2390,9 @@ showHotFunctionList(const StringMap<sampleprof::FunctionSamples> &Profiles, (ProfileTotalSample > 0) ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample : 0; - PrintValues.emplace_back( - HotFuncInfo(Func.getName(), Func.getTotalSamples(), TotalSamplePercent, - FuncPair.second.second, Func.getEntrySamples())); + PrintValues.emplace_back(HotFuncInfo( + Func.getNameWithContext(), Func.getTotalSamples(), TotalSamplePercent, + FuncPair.second.second, Func.getEntrySamples())); } dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, Profiles.size(), HotFuncSample, ProfileTotalSample, @@ -2326,12 +2409,12 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, raw_fd_ostream &OS) { using namespace sampleprof; LLVMContext Context; - auto ReaderOrErr = SampleProfileReader::create(Filename, Context); + auto ReaderOrErr = + SampleProfileReader::create(Filename, Context, FSDiscriminatorPassOption); if (std::error_code EC = ReaderOrErr.getError()) exitWithErrorCode(EC, Filename); auto Reader = std::move(ReaderOrErr.get()); - if (ShowSectionInfoOnly) { showSectionInfo(Reader.get(), OS); return 0; @@ -2423,12 +2506,11 @@ static int show_main(int argc, const char *argv[]) { cl::desc("Show the information of each section in the sample profile. " "The flag is only usable when the sample profile is in " "extbinary format")); + cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false), + cl::desc("Show binary ids in the profile. ")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - if (OutputFilename.empty()) - OutputFilename = "-"; - if (Filename == OutputFilename) { errs() << sys::path::filename(argv[0]) << ": Input file name cannot be the same as the output file name!\n"; @@ -2436,7 +2518,7 @@ static int show_main(int argc, const char *argv[]) { } std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text); + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); if (EC) exitWithErrorCode(EC, OutputFilename); @@ -2444,11 +2526,11 @@ static int show_main(int argc, const char *argv[]) { WithColor::warning() << "-function argument ignored: showing all functions\n"; if (ProfileKind == instr) - return showInstrProfile(Filename, ShowCounts, TopNFunctions, - ShowIndirectCallTargets, ShowMemOPSizes, - ShowDetailedSummary, DetailedSummaryCutoffs, - ShowAllFunctions, ShowCS, ValueCutoff, - OnlyListBelow, ShowFunction, TextFormat, OS); + return showInstrProfile( + Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, + ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, + ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction, + TextFormat, ShowBinaryIds, OS); else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, ShowDetailedSummary, ShowFunction, diff --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp index 5995a09514c8..99ee639fc45d 100644 --- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp +++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp @@ -100,7 +100,7 @@ static std::string formatSymbol(StringRef Name, uint64_t Address, OS << Name << " "; if (Offset) - OS << format("+0x%X (0x%" PRIX64 ")", Offset, Address); + OS << format("+0x%" PRIX64 " (0x%" PRIX64 ")", Offset, Address); else if (!Name.empty()) OS << format("(0x%" PRIX64 ")", Address); else @@ -184,31 +184,16 @@ void Decoder::printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask) const uint16_t VFPMask = std::get<1>(RegisterMask); OS << '{'; - bool Comma = false; - for (unsigned RI = 0, RE = 11; RI < RE; ++RI) { - if (GPRMask & (1 << RI)) { - if (Comma) - OS << ", "; - OS << GPRRegisterNames[RI]; - Comma = true; - } - } - for (unsigned RI = 0, RE = 32; RI < RE; ++RI) { - if (VFPMask & (1 << RI)) { - if (Comma) - OS << ", "; - OS << "d" << unsigned(RI); - Comma = true; - } - } - for (unsigned RI = 11, RE = 16; RI < RE; ++RI) { - if (GPRMask & (1 << RI)) { - if (Comma) - OS << ", "; - OS << GPRRegisterNames[RI]; - Comma = true; - } - } + ListSeparator LS; + for (unsigned RI = 0, RE = 11; RI < RE; ++RI) + if (GPRMask & (1 << RI)) + OS << LS << GPRRegisterNames[RI]; + for (unsigned RI = 0, RE = 32; RI < RE; ++RI) + if (VFPMask & (1 << RI)) + OS << LS << "d" << unsigned(RI); + for (unsigned RI = 11, RE = 16; RI < RE; ++RI) + if (GPRMask & (1 << RI)) + OS << LS << GPRRegisterNames[RI]; OS << '}'; } @@ -253,6 +238,65 @@ ErrorOr<SymbolRef> Decoder::getRelocatedSymbol(const COFFObjectFile &, return inconvertibleErrorCode(); } +SymbolRef Decoder::getPreferredSymbol(const COFFObjectFile &COFF, + SymbolRef Sym) { + // The symbol resolved by getRelocatedSymbol can be any internal + // nondescriptive symbol; try to resolve a more descriptive one. + COFFSymbolRef CoffSym = COFF.getCOFFSymbol(Sym); + if (CoffSym.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) + return Sym; + for (const auto &S : COFF.symbols()) { + COFFSymbolRef CS = COFF.getCOFFSymbol(S); + if (CS.getSectionNumber() == CoffSym.getSectionNumber() && + CS.getValue() == CoffSym.getValue()) { + if (CS.isExternal()) + return S; + if (CS.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) { + Sym = S; + CoffSym = CS; + } + } + } + return Sym; +} + +ErrorOr<SymbolRef> Decoder::getSymbolForLocation( + const COFFObjectFile &COFF, const SectionRef &Section, + uint64_t OffsetInSection, uint64_t ImmediateOffset, uint64_t &SymbolAddress, + uint64_t &SymbolOffset, bool FunctionOnly) { + // Try to locate a relocation that points at the offset in the section + ErrorOr<SymbolRef> SymOrErr = + getRelocatedSymbol(COFF, Section, OffsetInSection); + if (SymOrErr) { + // We found a relocation symbol; the immediate offset needs to be added + // to the symbol address. + SymbolOffset = ImmediateOffset; + + Expected<uint64_t> AddressOrErr = SymOrErr->getAddress(); + if (!AddressOrErr) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + logAllUnhandledErrors(AddressOrErr.takeError(), OS); + OS.flush(); + report_fatal_error(Buf); + } + // We apply SymbolOffset here directly. We return it separately to allow + // the caller to print it as an offset on the symbol name. + SymbolAddress = *AddressOrErr + SymbolOffset; + } else { + // No matching relocation found; operating on a linked image. Try to + // find a descriptive symbol if possible. The immediate offset contains + // the image relative address, and we shouldn't add any offset to the + // symbol. + SymbolAddress = COFF.getImageBase() + ImmediateOffset; + SymbolOffset = 0; + SymOrErr = getSymbol(COFF, SymbolAddress, FunctionOnly); + } + if (SymOrErr && FunctionOnly) // Resolve label symbols into function names + SymOrErr = getPreferredSymbol(COFF, *SymOrErr); + return SymOrErr; +} + bool Decoder::opcode_0xxxxxxx(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { uint8_t Imm = OC[Offset] & 0x7f; @@ -934,16 +978,16 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, } if (XData.X()) { - const uint64_t Address = COFF.getImageBase() + XData.ExceptionHandlerRVA(); const uint32_t Parameter = XData.ExceptionHandlerParameter(); - const size_t HandlerOffset = HeaderWords(XData) - + (XData.E() ? 0 : XData.EpilogueCount()) - + XData.CodeWords(); - - ErrorOr<SymbolRef> Symbol = getRelocatedSymbol( - COFF, Section, Offset + HandlerOffset * sizeof(uint32_t)); - if (!Symbol) - Symbol = getSymbol(COFF, Address, /*FunctionOnly=*/true); + const size_t HandlerOffset = HeaderWords(XData) + + (XData.E() ? 0 : XData.EpilogueCount()) + + XData.CodeWords(); + + uint64_t Address, SymbolOffset; + ErrorOr<SymbolRef> Symbol = getSymbolForLocation( + COFF, Section, Offset + HandlerOffset * sizeof(uint32_t), + XData.ExceptionHandlerRVA(), Address, SymbolOffset, + /*FunctionOnly=*/true); if (!Symbol) { ListScope EHS(SW, "ExceptionHandler"); SW.printHex("Routine", Address); @@ -961,7 +1005,7 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, } ListScope EHS(SW, "ExceptionHandler"); - SW.printString("Routine", formatSymbol(*Name, Address)); + SW.printString("Routine", formatSymbol(*Name, Address, SymbolOffset)); SW.printHex("Parameter", Parameter); } @@ -974,14 +1018,15 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, assert(RF.Flag() == RuntimeFunctionFlag::RFF_Unpacked && "packed entry cannot be treated as an unpacked entry"); - ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset); - if (!Function) - Function = getSymbol(COFF, COFF.getImageBase() + RF.BeginAddress, - /*FunctionOnly=*/true); + uint64_t FunctionAddress, FunctionOffset; + ErrorOr<SymbolRef> Function = getSymbolForLocation( + COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset, + /*FunctionOnly=*/true); - ErrorOr<SymbolRef> XDataRecord = getRelocatedSymbol(COFF, Section, Offset + 4); - if (!XDataRecord) - XDataRecord = getSymbol(COFF, RF.ExceptionInformationRVA()); + uint64_t XDataAddress, XDataOffset; + ErrorOr<SymbolRef> XDataRecord = getSymbolForLocation( + COFF, Section, Offset + 4, RF.ExceptionInformationRVA(), XDataAddress, + XDataOffset); if (!RF.BeginAddress && !Function) return false; @@ -989,7 +1034,6 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, return false; StringRef FunctionName; - uint64_t FunctionAddress; if (Function) { Expected<StringRef> FunctionNameOrErr = Function->getName(); if (!FunctionNameOrErr) { @@ -1000,20 +1044,10 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, report_fatal_error(Buf); } FunctionName = *FunctionNameOrErr; - Expected<uint64_t> FunctionAddressOrErr = Function->getAddress(); - if (!FunctionAddressOrErr) { - std::string Buf; - llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS); - OS.flush(); - report_fatal_error(Buf); - } - FunctionAddress = *FunctionAddressOrErr; - } else { - FunctionAddress = COFF.getImageBase() + RF.BeginAddress; } - SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); + SW.printString("Function", + formatSymbol(FunctionName, FunctionAddress, FunctionOffset)); if (XDataRecord) { Expected<StringRef> Name = XDataRecord->getName(); @@ -1025,17 +1059,8 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, report_fatal_error(Buf); } - Expected<uint64_t> AddressOrErr = XDataRecord->getAddress(); - if (!AddressOrErr) { - std::string Buf; - llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(AddressOrErr.takeError(), OS); - OS.flush(); - report_fatal_error(Buf); - } - uint64_t Address = *AddressOrErr; - - SW.printString("ExceptionRecord", formatSymbol(*Name, Address)); + SW.printString("ExceptionRecord", + formatSymbol(*Name, XDataAddress, XDataOffset)); Expected<section_iterator> SIOrErr = XDataRecord->getSection(); if (!SIOrErr) { @@ -1045,18 +1070,15 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, } section_iterator SI = *SIOrErr; - // FIXME: Do we need to add an offset from the relocation? - return dumpXDataRecord(COFF, *SI, FunctionAddress, - RF.ExceptionInformationRVA()); + return dumpXDataRecord(COFF, *SI, FunctionAddress, XDataAddress); } else { - uint64_t Address = COFF.getImageBase() + RF.ExceptionInformationRVA(); - SW.printString("ExceptionRecord", formatSymbol("", Address)); + SW.printString("ExceptionRecord", formatSymbol("", XDataAddress)); - ErrorOr<SectionRef> Section = getSectionContaining(COFF, Address); + ErrorOr<SectionRef> Section = getSectionContaining(COFF, XDataAddress); if (!Section) return false; - return dumpXDataRecord(COFF, *Section, FunctionAddress, Address); + return dumpXDataRecord(COFF, *Section, FunctionAddress, XDataAddress); } } @@ -1067,12 +1089,12 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF, RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && "unpacked entry cannot be treated as a packed entry"); - ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset); - if (!Function) - Function = getSymbol(COFF, RF.BeginAddress, /*FunctionOnly=*/true); + uint64_t FunctionAddress, FunctionOffset; + ErrorOr<SymbolRef> Function = getSymbolForLocation( + COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset, + /*FunctionOnly=*/true); StringRef FunctionName; - uint64_t FunctionAddress; if (Function) { Expected<StringRef> FunctionNameOrErr = Function->getName(); if (!FunctionNameOrErr) { @@ -1083,20 +1105,10 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF, report_fatal_error(Buf); } FunctionName = *FunctionNameOrErr; - Expected<uint64_t> FunctionAddressOrErr = Function->getAddress(); - if (!FunctionAddressOrErr) { - std::string Buf; - llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS); - OS.flush(); - report_fatal_error(Buf); - } - FunctionAddress = *FunctionAddressOrErr; - } else { - FunctionAddress = COFF.getPE32Header()->ImageBase + RF.BeginAddress; } - SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); + SW.printString("Function", + formatSymbol(FunctionName, FunctionAddress, FunctionOffset)); if (!isAArch64) SW.printBoolean("Fragment", RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment); @@ -1119,12 +1131,12 @@ bool Decoder::dumpPackedARM64Entry(const object::COFFObjectFile &COFF, RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment) && "unpacked entry cannot be treated as a packed entry"); - ErrorOr<SymbolRef> Function = getRelocatedSymbol(COFF, Section, Offset); - if (!Function) - Function = getSymbol(COFF, RF.BeginAddress, /*FunctionOnly=*/true); + uint64_t FunctionAddress, FunctionOffset; + ErrorOr<SymbolRef> Function = getSymbolForLocation( + COFF, Section, Offset, RF.BeginAddress, FunctionAddress, FunctionOffset, + /*FunctionOnly=*/true); StringRef FunctionName; - uint64_t FunctionAddress; if (Function) { Expected<StringRef> FunctionNameOrErr = Function->getName(); if (!FunctionNameOrErr) { @@ -1135,20 +1147,10 @@ bool Decoder::dumpPackedARM64Entry(const object::COFFObjectFile &COFF, report_fatal_error(Buf); } FunctionName = *FunctionNameOrErr; - Expected<uint64_t> FunctionAddressOrErr = Function->getAddress(); - if (!FunctionAddressOrErr) { - std::string Buf; - llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS); - OS.flush(); - report_fatal_error(Buf); - } - FunctionAddress = *FunctionAddressOrErr; - } else { - FunctionAddress = COFF.getPE32PlusHeader()->ImageBase + RF.BeginAddress; } - SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); + SW.printString("Function", + formatSymbol(FunctionName, FunctionAddress, FunctionOffset)); SW.printBoolean("Fragment", RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment); SW.printNumber("FunctionLength", RF.FunctionLength()); diff --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.h b/llvm/tools/llvm-readobj/ARMWinEHPrinter.h index 3263841a267b..efe16850c7fa 100644 --- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.h +++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.h @@ -146,6 +146,16 @@ class Decoder { getRelocatedSymbol(const object::COFFObjectFile &COFF, const object::SectionRef &Section, uint64_t Offset); + ErrorOr<object::SymbolRef> + getSymbolForLocation(const object::COFFObjectFile &COFF, + const object::SectionRef &Section, + uint64_t OffsetInSection, uint64_t ImmediateOffset, + uint64_t &SymbolAddress, uint64_t &SymbolOffset, + bool FunctionOnly = false); + + object::SymbolRef getPreferredSymbol(const object::COFFObjectFile &COFF, + object::SymbolRef Sym); + bool dumpXDataRecord(const object::COFFObjectFile &COFF, const object::SectionRef &Section, uint64_t FunctionAddress, uint64_t VA); diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp index 684967f93393..96124cc03484 100644 --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -71,6 +71,8 @@ struct LoadConfigTables { uint64_t GuardIatTableCount = 0; uint64_t GuardLJmpTableVA = 0; uint64_t GuardLJmpTableCount = 0; + uint64_t GuardEHContTableVA = 0; + uint64_t GuardEHContTableCount = 0; }; class COFFDumper : public ObjDumper { @@ -593,8 +595,7 @@ void COFFDumper::cacheRelocations() { for (const SectionRef &S : Obj->sections()) { const coff_section *Section = Obj->getCOFFSection(S); - for (const RelocationRef &Reloc : S.relocations()) - RelocMap[Section].push_back(Reloc); + append_range(RelocMap[Section], S.relocations()); // Sort relocations by address. llvm::sort(RelocMap[Section], [](RelocationRef L, RelocationRef R) { @@ -794,19 +795,19 @@ void COFFDumper::printCOFFLoadConfig() { printRVATable(Tables.SEHTableVA, Tables.SEHTableCount, 4); } + auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) { + uint8_t Flags = *reinterpret_cast<const uint8_t *>(Entry + 4); + if (Flags) + OS << " flags " << utohexstr(Flags); + }; + if (Tables.GuardFidTableVA) { ListScope LS(W, "GuardFidTable"); - if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags)) { - auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) { - uint8_t Flags = *reinterpret_cast<const uint8_t *>(Entry + 4); - if (Flags) - OS << " flags " << utohexstr(Flags); - }; + if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags)) printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 5, PrintGuardFlags); - } else { + else printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 4); - } } if (Tables.GuardIatTableVA) { @@ -818,6 +819,12 @@ void COFFDumper::printCOFFLoadConfig() { ListScope LS(W, "GuardLJmpTable"); printRVATable(Tables.GuardLJmpTableVA, Tables.GuardLJmpTableCount, 4); } + + if (Tables.GuardEHContTableVA) { + ListScope LS(W, "GuardEHContTable"); + printRVATable(Tables.GuardEHContTableVA, Tables.GuardEHContTableCount, 5, + PrintGuardFlags); + } } template <typename T> @@ -876,8 +883,8 @@ void COFFDumper::printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables) { Tables.GuardFidTableCount = Conf->GuardCFFunctionCount; Tables.GuardFlags = Conf->GuardFlags; - // Print the rest. (2017) - if (Conf->Size < sizeof(T)) + // Print everything before Reserved3. (2017) + if (Conf->Size < offsetof(T, Reserved3)) return; W.printHex("GuardAddressTakenIatEntryTable", Conf->GuardAddressTakenIatEntryTable); @@ -903,6 +910,17 @@ void COFFDumper::printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables) { Tables.GuardLJmpTableVA = Conf->GuardLongJumpTargetTable; Tables.GuardLJmpTableCount = Conf->GuardLongJumpTargetCount; + + // Print the rest. (2019) + if (Conf->Size < sizeof(T)) + return; + W.printHex("EnclaveConfigurationPointer", Conf->EnclaveConfigurationPointer); + W.printHex("VolatileMetadataPointer", Conf->VolatileMetadataPointer); + W.printHex("GuardEHContinuationTable", Conf->GuardEHContinuationTable); + W.printNumber("GuardEHContinuationCount", Conf->GuardEHContinuationCount); + + Tables.GuardEHContTableVA = Conf->GuardEHContinuationTable; + Tables.GuardEHContTableCount = Conf->GuardEHContinuationCount; } void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) { @@ -1856,7 +1874,7 @@ void COFFDumper::printResourceDirectoryTable( OS << ": (ID " << Entry.Identifier.ID << ")"; } } - Name = StringRef(IDStr); + Name = IDStr; ListScope ResourceType(W, Level.str() + Name.str()); if (Entry.Offset.isSubDir()) { W.printHex("Table Offset", Entry.Offset.value()); diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 0f508f8dc0f2..f221acba979a 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -298,6 +298,13 @@ protected: std::vector<GroupSection> getGroups(); + // Returns the function symbol index for the given address. Matches the + // symbol's section with FunctionSec when specified. + // Returns None if no function symbol can be found for the address or in case + // it is not defined in the specified section. + SmallVector<uint32_t> + getSymbolIndexesForFunctionAddress(uint64_t SymValue, + Optional<const Elf_Shdr *> FunctionSec); bool printFunctionStackSize(uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec, const Elf_Shdr &StackSizeSec, DataExtractor Data, @@ -306,11 +313,18 @@ protected: unsigned Ndx, const Elf_Shdr *SymTab, const Elf_Shdr *FunctionSec, const Elf_Shdr &StackSizeSec, const RelocationResolver &Resolver, DataExtractor Data); - virtual void printStackSizeEntry(uint64_t Size, StringRef FuncName) = 0; + virtual void printStackSizeEntry(uint64_t Size, + ArrayRef<std::string> FuncNames) = 0; void printRelocatableStackSizes(std::function<void()> PrintHeader); void printNonRelocatableStackSizes(std::function<void()> PrintHeader); + /// Retrieves sections with corresponding relocation sections based on + /// IsMatch. + void getSectionAndRelocations( + std::function<bool(const Elf_Shdr &)> IsMatch, + llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> &SecToRelocMap); + const object::ELFObjectFile<ELFT> &ObjF; const ELFFile<ELFT> &Obj; StringRef FileName; @@ -349,10 +363,10 @@ protected: const Elf_GnuHash *GnuHashTable = nullptr; const Elf_Shdr *DotSymtabSec = nullptr; const Elf_Shdr *DotDynsymSec = nullptr; - const Elf_Shdr *DotCGProfileSec = nullptr; const Elf_Shdr *DotAddrsigSec = nullptr; DenseMap<const Elf_Shdr *, ArrayRef<Elf_Word>> ShndxTables; Optional<uint64_t> SONameOffset; + Optional<DenseMap<uint64_t, std::vector<uint32_t>>> AddressToIndexMap; const Elf_Shdr *SymbolVersionSection = nullptr; // .gnu.version const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r @@ -550,6 +564,7 @@ public: void printVersionDependencySection(const Elf_Shdr *Sec) override; void printHashHistograms() override; void printCGProfile() override; + void printBBAddrMaps() override; void printAddrsig() override; void printNotes() override; void printELFLinkerOptions() override; @@ -631,7 +646,8 @@ private: void printGNUVersionSectionProlog(const typename ELFT::Shdr &Sec, const Twine &Label, unsigned EntriesNum); - void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; + void printStackSizeEntry(uint64_t Size, + ArrayRef<std::string> FuncNames) override; void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override; @@ -660,6 +676,7 @@ public: void printVersionDependencySection(const Elf_Shdr *Sec) override; void printHashHistograms() override; void printCGProfile() override; + void printBBAddrMaps() override; void printAddrsig() override; void printNotes() override; void printELFLinkerOptions() override; @@ -678,7 +695,8 @@ private: bool /*NonVisibilityBitsUsed*/) const override; void printProgramHeaders() override; void printSectionMapping() override {} - void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; + void printStackSizeEntry(uint64_t Size, + ArrayRef<std::string> FuncNames) override; void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override; @@ -1138,7 +1156,7 @@ static const EnumEntry<unsigned> ElfMachineType[] = { ENUM_ENT(EM_METAG, "Imagination Technologies Meta processor architecture"), ENUM_ENT(EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture"), ENUM_ENT(EM_ECOG16, "Cyan Technology eCOG16 family"), - ENUM_ENT(EM_CR16, "Xilinx MicroBlaze"), + ENUM_ENT(EM_CR16, "National Semiconductor CompactRISC 16-bit processor"), ENUM_ENT(EM_ETPU, "Freescale Extended Time Processing Unit"), ENUM_ENT(EM_SLE9X, "Infineon Technologies SLE9X core"), ENUM_ENT(EM_L10M, "EM_L10M"), @@ -1148,6 +1166,7 @@ static const EnumEntry<unsigned> ElfMachineType[] = { ENUM_ENT(EM_STM8, "STMicroeletronics STM8 8-bit microcontroller"), ENUM_ENT(EM_TILE64, "Tilera TILE64 multicore architecture family"), ENUM_ENT(EM_TILEPRO, "Tilera TILEPro multicore architecture family"), + ENUM_ENT(EM_MICROBLAZE, "Xilinx MicroBlaze 32-bit RISC soft processor core"), ENUM_ENT(EM_CUDA, "NVIDIA CUDA architecture"), ENUM_ENT(EM_TILEGX, "Tilera TILE-Gx multicore architecture family"), ENUM_ENT(EM_CLOUDSHIELD, "EM_CLOUDSHIELD"), @@ -1201,6 +1220,7 @@ static const EnumEntry<unsigned> ElfSectionFlags[] = { ENUM_ENT(SHF_GROUP, "G"), ENUM_ENT(SHF_TLS, "T"), ENUM_ENT(SHF_COMPRESSED, "C"), + ENUM_ENT(SHF_GNU_RETAIN, "R"), ENUM_ENT(SHF_EXCLUDE, "E"), }; @@ -1427,7 +1447,7 @@ static const EnumEntry<unsigned> ElfHeaderMipsFlags[] = { ENUM_ENT(EF_MIPS_ARCH_64R6, "mips64r6") }; -static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = { +static const EnumEntry<unsigned> ElfHeaderAMDGPUFlagsABIVersion3[] = { LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_NONE), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R600), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R630), @@ -1465,16 +1485,78 @@ static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX906), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX908), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90A), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90C), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1010), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1011), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1012), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1013), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1030), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1031), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1032), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1033), - LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_XNACK), - LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_SRAM_ECC) + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1034), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1035), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_V3), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_V3) +}; + +static const EnumEntry<unsigned> ElfHeaderAMDGPUFlagsABIVersion4[] = { + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_NONE), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R600), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R630), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RS880), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV670), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV710), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV730), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV770), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CEDAR), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CYPRESS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_JUNIPER), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_REDWOOD), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_SUMO), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_BARTS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAICOS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAYMAN), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_TURKS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX600), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX601), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX602), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX700), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX701), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX702), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX703), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX704), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX705), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX801), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX802), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX803), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX805), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX810), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX900), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX902), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX904), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX906), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX908), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90A), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX90C), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1010), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1011), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1012), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1013), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1030), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1031), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1032), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1033), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1034), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1035), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_ANY_V4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_OFF_V4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_XNACK_ON_V4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_ANY_V4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_OFF_V4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_FEATURE_SRAMECC_ON_V4) }; static const EnumEntry<unsigned> ElfHeaderRISCVFlags[] = { @@ -1485,6 +1567,29 @@ static const EnumEntry<unsigned> ElfHeaderRISCVFlags[] = { ENUM_ENT(EF_RISCV_RVE, "RVE") }; +static const EnumEntry<unsigned> ElfHeaderAVRFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR1), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR2), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR25), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR3), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR31), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR35), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR5), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR51), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVR6), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_AVRTINY), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA1), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA2), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA3), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA5), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA6), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AVR_ARCH_XMEGA7), + ENUM_ENT(EF_AVR_LINKRELAX_PREPARED, "relaxable"), +}; + + static const EnumEntry<unsigned> ElfSymOtherFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, STV_INTERNAL), LLVM_READOBJ_ENUM_ENT(ELF, STV_HIDDEN), @@ -1743,10 +1848,6 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> &O, if (!SymbolVersionNeedSection) SymbolVersionNeedSection = &Sec; break; - case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: - if (!DotCGProfileSec) - DotCGProfileSec = &Sec; - break; case ELF::SHT_LLVM_ADDRSIG: if (!DotAddrsigSec) DotAddrsigSec = &Sec; @@ -3170,6 +3271,9 @@ template <class ELFT> void GNUELFDumper<ELFT>::printFileHeaders() { unsigned(ELF::EF_MIPS_MACH)); else if (e.e_machine == EM_RISCV) ElfFlags = printFlags(e.e_flags, makeArrayRef(ElfHeaderRISCVFlags)); + else if (e.e_machine == EM_AVR) + ElfFlags = printFlags(e.e_flags, makeArrayRef(ElfHeaderAVRFlags), + unsigned(ELF::EF_AVR_ARCH_MASK)); Str = "0x" + to_hexString(e.e_flags); if (!ElfFlags.empty()) Str = Str + ", " + ElfFlags; @@ -3483,15 +3587,14 @@ static void printSectionDescription(formatted_raw_ostream &OS, OS << " L (link order), O (extra OS processing required), G (group), T " "(TLS),\n"; OS << " C (compressed), x (unknown), o (OS specific), E (exclude),\n"; + OS << " R (retain)"; if (EMachine == EM_X86_64) - OS << " l (large), "; + OS << ", l (large)"; else if (EMachine == EM_ARM) - OS << " y (purecode), "; - else - OS << " "; + OS << ", y (purecode)"; - OS << "p (processor specific)\n"; + OS << ", p (processor specific)\n"; } template <class ELFT> void GNUELFDumper<ELFT>::printSectionHeaders() { @@ -3934,21 +4037,17 @@ template <class ELFT> void GNUELFDumper<ELFT>::printSectionDetails() { uint64_t Flags = S.sh_flags; uint64_t UnknownFlags = 0; - bool NeedsComma = false; + ListSeparator LS; while (Flags) { // Take the least significant bit as a flag. uint64_t Flag = Flags & -Flags; Flags -= Flag; auto It = FlagToName.find(Flag); - if (It != FlagToName.end()) { - if (NeedsComma) - OS << ", "; - NeedsComma = true; - OS << It->second; - } else { + if (It != FlagToName.end()) + OS << LS << It->second; + else UnknownFlags |= Flag; - } } auto PrintUnknownFlags = [&](uint64_t Mask, StringRef Name) { @@ -3956,12 +4055,9 @@ template <class ELFT> void GNUELFDumper<ELFT>::printSectionDetails() { if (!FlagsToPrint) return; - if (NeedsComma) - OS << ", "; - OS << Name << " (" + OS << LS << Name << " (" << to_string(format_hex_no_prefix(FlagsToPrint, AddrSize)) << ")"; UnknownFlags &= ~Mask; - NeedsComma = true; }; PrintUnknownFlags(SHF_MASKOS, "OS"); @@ -4623,6 +4719,10 @@ template <class ELFT> void GNUELFDumper<ELFT>::printCGProfile() { OS << "GNUStyle::printCGProfile not implemented\n"; } +template <class ELFT> void GNUELFDumper<ELFT>::printBBAddrMaps() { + OS << "GNUStyle::printBBAddrMaps not implemented\n"; +} + static Expected<std::vector<uint64_t>> toULEB128Array(ArrayRef<uint8_t> Data) { std::vector<uint64_t> Ret; const uint8_t *Cur = Data.begin(); @@ -4736,47 +4836,6 @@ static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, if (PrData) OS << format("<unknown flags: 0x%x>", PrData); return OS.str(); - case GNU_PROPERTY_X86_ISA_1_NEEDED: - case GNU_PROPERTY_X86_ISA_1_USED: - OS << "x86 ISA " - << (Type == GNU_PROPERTY_X86_ISA_1_NEEDED ? "needed: " : "used: "); - if (DataSize != 4) { - OS << format("<corrupt length: 0x%x>", DataSize); - return OS.str(); - } - PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data()); - if (PrData == 0) { - OS << "<None>"; - return OS.str(); - } - DumpBit(GNU_PROPERTY_X86_ISA_1_CMOV, "CMOV"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSE, "SSE"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSE2, "SSE2"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSE3, "SSE3"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSSE3, "SSSE3"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSE4_1, "SSE4_1"); - DumpBit(GNU_PROPERTY_X86_ISA_1_SSE4_2, "SSE4_2"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX, "AVX"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX2, "AVX2"); - DumpBit(GNU_PROPERTY_X86_ISA_1_FMA, "FMA"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512F, "AVX512F"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512CD, "AVX512CD"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512ER, "AVX512ER"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512PF, "AVX512PF"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512VL, "AVX512VL"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512DQ, "AVX512DQ"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512BW, "AVX512BW"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_4FMAPS, "AVX512_4FMAPS"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_4VNNIW, "AVX512_4VNNIW"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_BITALG, "AVX512_BITALG"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_IFMA, "AVX512_IFMA"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VBMI, "AVX512_VBMI"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VBMI2, "AVX512_VBMI2"); - DumpBit(GNU_PROPERTY_X86_ISA_1_AVX512_VNNI, "AVX512_VNNI"); - if (PrData) - OS << format("<unknown flags: 0x%x>", PrData); - return OS.str(); - break; case GNU_PROPERTY_X86_FEATURE_2_NEEDED: case GNU_PROPERTY_X86_FEATURE_2_USED: OS << "x86 feature " @@ -4803,6 +4862,26 @@ static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, if (PrData) OS << format("<unknown flags: 0x%x>", PrData); return OS.str(); + case GNU_PROPERTY_X86_ISA_1_NEEDED: + case GNU_PROPERTY_X86_ISA_1_USED: + OS << "x86 ISA " + << (Type == GNU_PROPERTY_X86_ISA_1_NEEDED ? "needed: " : "used: "); + if (DataSize != 4) { + OS << format("<corrupt length: 0x%x>", DataSize); + return OS.str(); + } + PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data()); + if (PrData == 0) { + OS << "<None>"; + return OS.str(); + } + DumpBit(GNU_PROPERTY_X86_ISA_1_BASELINE, "x86-64-baseline"); + DumpBit(GNU_PROPERTY_X86_ISA_1_V2, "x86-64-v2"); + DumpBit(GNU_PROPERTY_X86_ISA_1_V3, "x86-64-v3"); + DumpBit(GNU_PROPERTY_X86_ISA_1_V4, "x86-64-v4"); + if (PrData) + OS << format("<unknown flags: 0x%x>", PrData); + return OS.str(); } } @@ -4877,11 +4956,12 @@ static StringRef getGNUGoldVersion(ArrayRef<uint8_t> Desc) { } template <typename ELFT> -static void printGNUNote(raw_ostream &OS, uint32_t NoteType, +static bool printGNUNote(raw_ostream &OS, uint32_t NoteType, ArrayRef<uint8_t> Desc) { + // Return true if we were able to pretty-print the note, false otherwise. switch (NoteType) { default: - return; + return false; case ELF::NT_GNU_ABI_TAG: { const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc); if (!AbiTag.IsValid) @@ -4904,6 +4984,54 @@ static void printGNUNote(raw_ostream &OS, uint32_t NoteType, break; } OS << '\n'; + return true; +} + +static const EnumEntry<unsigned> FreeBSDFeatureCtlFlags[] = { + {"ASLR_DISABLE", NT_FREEBSD_FCTL_ASLR_DISABLE}, + {"PROTMAX_DISABLE", NT_FREEBSD_FCTL_PROTMAX_DISABLE}, + {"STKGAP_DISABLE", NT_FREEBSD_FCTL_STKGAP_DISABLE}, + {"WXNEEDED", NT_FREEBSD_FCTL_WXNEEDED}, + {"LA48", NT_FREEBSD_FCTL_LA48}, + {"ASG_DISABLE", NT_FREEBSD_FCTL_ASG_DISABLE}, +}; + +struct FreeBSDNote { + std::string Type; + std::string Value; +}; + +template <typename ELFT> +static Optional<FreeBSDNote> +getFreeBSDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc, bool IsCore) { + if (IsCore) + return None; // No pretty-printing yet. + switch (NoteType) { + case ELF::NT_FREEBSD_ABI_TAG: + if (Desc.size() != 4) + return None; + return FreeBSDNote{ + "ABI tag", + utostr(support::endian::read32<ELFT::TargetEndianness>(Desc.data()))}; + case ELF::NT_FREEBSD_ARCH_TAG: + return FreeBSDNote{"Arch tag", toStringRef(Desc).str()}; + case ELF::NT_FREEBSD_FEATURE_CTL: { + if (Desc.size() != 4) + return None; + unsigned Value = + support::endian::read32<ELFT::TargetEndianness>(Desc.data()); + std::string FlagsStr; + raw_string_ostream OS(FlagsStr); + printFlags(Value, makeArrayRef(FreeBSDFeatureCtlFlags), OS); + if (OS.str().empty()) + OS << "0x" << utohexstr(Value); + else + OS << "(0x" << utohexstr(Value) << ")"; + return FreeBSDNote{"Feature flags", OS.str()}; + } + default: + return None; + } } struct AMDNote { @@ -4916,15 +5044,98 @@ static AMDNote getAMDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) { switch (NoteType) { default: return {"", ""}; - case ELF::NT_AMD_AMDGPU_HSA_METADATA: + case ELF::NT_AMD_HSA_CODE_OBJECT_VERSION: { + struct CodeObjectVersion { + uint32_t MajorVersion; + uint32_t MinorVersion; + }; + if (Desc.size() != sizeof(CodeObjectVersion)) + return {"AMD HSA Code Object Version", + "Invalid AMD HSA Code Object Version"}; + std::string VersionString; + raw_string_ostream StrOS(VersionString); + auto Version = reinterpret_cast<const CodeObjectVersion *>(Desc.data()); + StrOS << "[Major: " << Version->MajorVersion + << ", Minor: " << Version->MinorVersion << "]"; + return {"AMD HSA Code Object Version", VersionString}; + } + case ELF::NT_AMD_HSA_HSAIL: { + struct HSAILProperties { + uint32_t HSAILMajorVersion; + uint32_t HSAILMinorVersion; + uint8_t Profile; + uint8_t MachineModel; + uint8_t DefaultFloatRound; + }; + if (Desc.size() != sizeof(HSAILProperties)) + return {"AMD HSA HSAIL Properties", "Invalid AMD HSA HSAIL Properties"}; + auto Properties = reinterpret_cast<const HSAILProperties *>(Desc.data()); + std::string HSAILPropetiesString; + raw_string_ostream StrOS(HSAILPropetiesString); + StrOS << "[HSAIL Major: " << Properties->HSAILMajorVersion + << ", HSAIL Minor: " << Properties->HSAILMinorVersion + << ", Profile: " << uint32_t(Properties->Profile) + << ", Machine Model: " << uint32_t(Properties->MachineModel) + << ", Default Float Round: " + << uint32_t(Properties->DefaultFloatRound) << "]"; + return {"AMD HSA HSAIL Properties", HSAILPropetiesString}; + } + case ELF::NT_AMD_HSA_ISA_VERSION: { + struct IsaVersion { + uint16_t VendorNameSize; + uint16_t ArchitectureNameSize; + uint32_t Major; + uint32_t Minor; + uint32_t Stepping; + }; + if (Desc.size() < sizeof(IsaVersion)) + return {"AMD HSA ISA Version", "Invalid AMD HSA ISA Version"}; + auto Isa = reinterpret_cast<const IsaVersion *>(Desc.data()); + if (Desc.size() < sizeof(IsaVersion) + + Isa->VendorNameSize + Isa->ArchitectureNameSize || + Isa->VendorNameSize == 0 || Isa->ArchitectureNameSize == 0) + return {"AMD HSA ISA Version", "Invalid AMD HSA ISA Version"}; + std::string IsaString; + raw_string_ostream StrOS(IsaString); + StrOS << "[Vendor: " + << StringRef((const char*)Desc.data() + sizeof(IsaVersion), Isa->VendorNameSize - 1) + << ", Architecture: " + << StringRef((const char*)Desc.data() + sizeof(IsaVersion) + Isa->VendorNameSize, + Isa->ArchitectureNameSize - 1) + << ", Major: " << Isa->Major << ", Minor: " << Isa->Minor + << ", Stepping: " << Isa->Stepping << "]"; + return {"AMD HSA ISA Version", IsaString}; + } + case ELF::NT_AMD_HSA_METADATA: { + if (Desc.size() == 0) + return {"AMD HSA Metadata", ""}; return { - "HSA Metadata", - std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())}; - case ELF::NT_AMD_AMDGPU_ISA: + "AMD HSA Metadata", + std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size() - 1)}; + } + case ELF::NT_AMD_HSA_ISA_NAME: { + if (Desc.size() == 0) + return {"AMD HSA ISA Name", ""}; return { - "ISA Version", + "AMD HSA ISA Name", std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())}; } + case ELF::NT_AMD_PAL_METADATA: { + struct PALMetadata { + uint32_t Key; + uint32_t Value; + }; + if (Desc.size() % sizeof(PALMetadata) != 0) + return {"AMD PAL Metadata", "Invalid AMD PAL Metadata"}; + auto Isa = reinterpret_cast<const PALMetadata *>(Desc.data()); + std::string MetadataString; + raw_string_ostream StrOS(MetadataString); + for (size_t I = 0, E = Desc.size() / sizeof(PALMetadata); I < E; ++I) { + StrOS << "[" << Isa[I].Key << ": " << Isa[I].Value << "]"; + } + return {"AMD PAL Metadata", MetadataString}; + } + } } struct AMDGPUNote { @@ -4942,16 +5153,22 @@ static AMDGPUNote getAMDGPUNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) { StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size()); msgpack::Document MsgPackDoc; if (!MsgPackDoc.readFromBlob(MsgPackString, /*Multi=*/false)) - return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"}; + return {"", ""}; AMDGPU::HSAMD::V3::MetadataVerifier Verifier(true); - std::string HSAMetadataString; + std::string MetadataString; if (!Verifier.verify(MsgPackDoc.getRoot())) - HSAMetadataString = "Invalid AMDGPU Metadata\n"; - - raw_string_ostream StrOS(HSAMetadataString); + MetadataString = "Invalid AMDGPU Metadata\n"; + + raw_string_ostream StrOS(MetadataString); + if (MsgPackDoc.getRoot().isScalar()) { + // TODO: passing a scalar root to toYAML() asserts: + // (PolymorphicTraits<T>::getKind(Val) != NodeKind::Scalar && + // "plain scalar documents are not supported") + // To avoid this crash we print the raw data instead. + return {"", ""}; + } MsgPackDoc.toYAML(StrOS); - return {"AMDGPU Metadata", StrOS.str()}; } } @@ -5049,7 +5266,7 @@ static const NoteType GNUNoteTypes[] = { {ELF::NT_GNU_PROPERTY_TYPE_0, "NT_GNU_PROPERTY_TYPE_0 (property note)"}, }; -static const NoteType FreeBSDNoteTypes[] = { +static const NoteType FreeBSDCoreNoteTypes[] = { {ELF::NT_FREEBSD_THRMISC, "NT_THRMISC (thrmisc structure)"}, {ELF::NT_FREEBSD_PROCSTAT_PROC, "NT_PROCSTAT_PROC (proc data)"}, {ELF::NT_FREEBSD_PROCSTAT_FILES, "NT_PROCSTAT_FILES (files data)"}, @@ -5063,12 +5280,22 @@ static const NoteType FreeBSDNoteTypes[] = { {ELF::NT_FREEBSD_PROCSTAT_AUXV, "NT_PROCSTAT_AUXV (auxv data)"}, }; +static const NoteType FreeBSDNoteTypes[] = { + {ELF::NT_FREEBSD_ABI_TAG, "NT_FREEBSD_ABI_TAG (ABI version tag)"}, + {ELF::NT_FREEBSD_NOINIT_TAG, "NT_FREEBSD_NOINIT_TAG (no .init tag)"}, + {ELF::NT_FREEBSD_ARCH_TAG, "NT_FREEBSD_ARCH_TAG (architecture tag)"}, + {ELF::NT_FREEBSD_FEATURE_CTL, + "NT_FREEBSD_FEATURE_CTL (FreeBSD feature control)"}, +}; + static const NoteType AMDNoteTypes[] = { - {ELF::NT_AMD_AMDGPU_HSA_METADATA, - "NT_AMD_AMDGPU_HSA_METADATA (HSA Metadata)"}, - {ELF::NT_AMD_AMDGPU_ISA, "NT_AMD_AMDGPU_ISA (ISA Version)"}, - {ELF::NT_AMD_AMDGPU_PAL_METADATA, - "NT_AMD_AMDGPU_PAL_METADATA (PAL Metadata)"}, + {ELF::NT_AMD_HSA_CODE_OBJECT_VERSION, + "NT_AMD_HSA_CODE_OBJECT_VERSION (AMD HSA Code Object Version)"}, + {ELF::NT_AMD_HSA_HSAIL, "NT_AMD_HSA_HSAIL (AMD HSA HSAIL Properties)"}, + {ELF::NT_AMD_HSA_ISA_VERSION, "NT_AMD_HSA_ISA_VERSION (AMD HSA ISA Version)"}, + {ELF::NT_AMD_HSA_METADATA, "NT_AMD_HSA_METADATA (AMD HSA Metadata)"}, + {ELF::NT_AMD_HSA_ISA_NAME, "NT_AMD_HSA_ISA_NAME (AMD HSA ISA Name)"}, + {ELF::NT_AMD_PAL_METADATA, "NT_AMD_PAL_METADATA (AMD PAL Metadata)"}, }; static const NoteType AMDGPUNoteTypes[] = { @@ -5141,8 +5368,7 @@ static const NoteType CoreNoteTypes[] = { }; template <class ELFT> -const StringRef getNoteTypeName(const typename ELFT::Note &Note, - unsigned ELFType) { +StringRef getNoteTypeName(const typename ELFT::Note &Note, unsigned ELFType) { uint32_t Type = Note.getType(); auto FindNote = [&](ArrayRef<NoteType> V) -> StringRef { for (const NoteType &N : V) @@ -5154,8 +5380,17 @@ const StringRef getNoteTypeName(const typename ELFT::Note &Note, StringRef Name = Note.getName(); if (Name == "GNU") return FindNote(GNUNoteTypes); - if (Name == "FreeBSD") - return FindNote(FreeBSDNoteTypes); + if (Name == "FreeBSD") { + if (ELFType == ELF::ET_CORE) { + // FreeBSD also places the generic core notes in the FreeBSD namespace. + StringRef Result = FindNote(FreeBSDCoreNoteTypes); + if (!Result.empty()) + return Result; + return FindNote(CoreNoteTypes); + } else { + return FindNote(FreeBSDNoteTypes); + } + } if (Name == "AMD") return FindNote(AMDNoteTypes); if (Name == "AMDGPU") @@ -5172,12 +5407,13 @@ static void printNotesHelper( llvm::function_ref<void(Optional<StringRef>, typename ELFT::Off, typename ELFT::Addr)> StartNotesFn, - llvm::function_ref<Error(const typename ELFT::Note &)> ProcessNoteFn, + llvm::function_ref<Error(const typename ELFT::Note &, bool)> ProcessNoteFn, llvm::function_ref<void()> FinishNotesFn) { const ELFFile<ELFT> &Obj = Dumper.getElfObject().getELFFile(); + bool IsCoreFile = Obj.getHeader().e_type == ELF::ET_CORE; ArrayRef<typename ELFT::Shdr> Sections = cantFail(Obj.sections()); - if (Obj.getHeader().e_type != ELF::ET_CORE && !Sections.empty()) { + if (!IsCoreFile && !Sections.empty()) { for (const typename ELFT::Shdr &S : Sections) { if (S.sh_type != SHT_NOTE) continue; @@ -5186,7 +5422,7 @@ static void printNotesHelper( Error Err = Error::success(); size_t I = 0; for (const typename ELFT::Note Note : Obj.notes(S, Err)) { - if (Error E = ProcessNoteFn(Note)) + if (Error E = ProcessNoteFn(Note, IsCoreFile)) Dumper.reportUniqueWarning( "unable to read note with index " + Twine(I) + " from the " + describe(Obj, S) + ": " + toString(std::move(E))); @@ -5217,7 +5453,7 @@ static void printNotesHelper( Error Err = Error::success(); size_t Index = 0; for (const typename ELFT::Note Note : Obj.notes(P, Err)) { - if (Error E = ProcessNoteFn(Note)) + if (Error E = ProcessNoteFn(Note, IsCoreFile)) Dumper.reportUniqueWarning("unable to read note with index " + Twine(Index) + " from the PT_NOTE segment with index " + @@ -5233,9 +5469,17 @@ static void printNotesHelper( } template <class ELFT> void GNUELFDumper<ELFT>::printNotes() { + bool IsFirstHeader = true; auto PrintHeader = [&](Optional<StringRef> SecName, const typename ELFT::Off Offset, const typename ELFT::Addr Size) { + // Print a newline between notes sections to match GNU readelf. + if (!IsFirstHeader) { + OS << '\n'; + } else { + IsFirstHeader = false; + } + OS << "Displaying notes found "; if (SecName) @@ -5247,7 +5491,7 @@ template <class ELFT> void GNUELFDumper<ELFT>::printNotes() { OS << " Owner Data size \tDescription\n"; }; - auto ProcessNote = [&](const Elf_Note &Note) -> Error { + auto ProcessNote = [&](const Elf_Note &Note, bool IsCore) -> Error { StringRef Name = Note.getName(); ArrayRef<uint8_t> Descriptor = Note.getDesc(); Elf_Word Type = Note.getType(); @@ -5264,28 +5508,42 @@ template <class ELFT> void GNUELFDumper<ELFT>::printNotes() { OS << "Unknown note type: (" << format_hex(Type, 10) << ")\n"; // Print the description, or fallback to printing raw bytes for unknown - // owners. + // owners/if we fail to pretty-print the contents. if (Name == "GNU") { - printGNUNote<ELFT>(OS, Type, Descriptor); + if (printGNUNote<ELFT>(OS, Type, Descriptor)) + return Error::success(); + } else if (Name == "FreeBSD") { + if (Optional<FreeBSDNote> N = + getFreeBSDNote<ELFT>(Type, Descriptor, IsCore)) { + OS << " " << N->Type << ": " << N->Value << '\n'; + return Error::success(); + } } else if (Name == "AMD") { const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); - if (!N.Type.empty()) + if (!N.Type.empty()) { OS << " " << N.Type << ":\n " << N.Value << '\n'; + return Error::success(); + } } else if (Name == "AMDGPU") { const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); - if (!N.Type.empty()) + if (!N.Type.empty()) { OS << " " << N.Type << ":\n " << N.Value << '\n'; + return Error::success(); + } } else if (Name == "CORE") { if (Type == ELF::NT_FILE) { DataExtractor DescExtractor(Descriptor, ELFT::TargetEndianness == support::little, sizeof(Elf_Addr)); - if (Expected<CoreNote> NoteOrErr = readCoreNote(DescExtractor)) + if (Expected<CoreNote> NoteOrErr = readCoreNote(DescExtractor)) { printCoreNote<ELFT>(OS, *NoteOrErr); - else + return Error::success(); + } else { return NoteOrErr.takeError(); + } } - } else if (!Descriptor.empty()) { + } + if (!Descriptor.empty()) { OS << " description data:"; for (uint8_t B : Descriptor) OS << " " << format("%02x", B); @@ -5461,64 +5719,81 @@ template <class ELFT> void GNUELFDumper<ELFT>::printDependentLibs() { } template <class ELFT> -bool ELFDumper<ELFT>::printFunctionStackSize( - uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec, - const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) { - uint32_t FuncSymIndex = 0; - if (this->DotSymtabSec) { - if (Expected<Elf_Sym_Range> SymsOrError = Obj.symbols(this->DotSymtabSec)) { - uint32_t Index = (uint32_t)-1; - for (const Elf_Sym &Sym : *SymsOrError) { - ++Index; - - if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC) - continue; - - if (Expected<uint64_t> SymAddrOrErr = - ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress()) { - if (SymValue != *SymAddrOrErr) +SmallVector<uint32_t> ELFDumper<ELFT>::getSymbolIndexesForFunctionAddress( + uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec) { + SmallVector<uint32_t> SymbolIndexes; + if (!this->AddressToIndexMap.hasValue()) { + // Populate the address to index map upon the first invocation of this + // function. + this->AddressToIndexMap.emplace(); + if (this->DotSymtabSec) { + if (Expected<Elf_Sym_Range> SymsOrError = + Obj.symbols(this->DotSymtabSec)) { + uint32_t Index = (uint32_t)-1; + for (const Elf_Sym &Sym : *SymsOrError) { + ++Index; + + if (Sym.st_shndx == ELF::SHN_UNDEF || Sym.getType() != ELF::STT_FUNC) continue; - } else { - std::string Name = this->getStaticSymbolName(Index); - reportUniqueWarning("unable to get address of symbol '" + Name + - "': " + toString(SymAddrOrErr.takeError())); - break; - } - // Check if the symbol is in the right section. FunctionSec == None - // means "any section". - if (FunctionSec) { - if (Expected<const Elf_Shdr *> SecOrErr = - Obj.getSection(Sym, this->DotSymtabSec, - this->getShndxTable(this->DotSymtabSec))) { - if (*FunctionSec != *SecOrErr) - continue; - } else { + Expected<uint64_t> SymAddrOrErr = + ObjF.toSymbolRef(this->DotSymtabSec, Index).getAddress(); + if (!SymAddrOrErr) { std::string Name = this->getStaticSymbolName(Index); - // Note: it is impossible to trigger this error currently, it is - // untested. - reportUniqueWarning("unable to get section of symbol '" + Name + - "': " + toString(SecOrErr.takeError())); - break; + reportUniqueWarning("unable to get address of symbol '" + Name + + "': " + toString(SymAddrOrErr.takeError())); + return SymbolIndexes; } + + (*this->AddressToIndexMap)[*SymAddrOrErr].push_back(Index); } + } else { + reportUniqueWarning("unable to read the symbol table: " + + toString(SymsOrError.takeError())); + } + } + } - FuncSymIndex = Index; - break; + auto Symbols = this->AddressToIndexMap->find(SymValue); + if (Symbols == this->AddressToIndexMap->end()) + return SymbolIndexes; + + for (uint32_t Index : Symbols->second) { + // Check if the symbol is in the right section. FunctionSec == None + // means "any section". + if (FunctionSec) { + const Elf_Sym &Sym = *cantFail(Obj.getSymbol(this->DotSymtabSec, Index)); + if (Expected<const Elf_Shdr *> SecOrErr = + Obj.getSection(Sym, this->DotSymtabSec, + this->getShndxTable(this->DotSymtabSec))) { + if (*FunctionSec != *SecOrErr) + continue; + } else { + std::string Name = this->getStaticSymbolName(Index); + // Note: it is impossible to trigger this error currently, it is + // untested. + reportUniqueWarning("unable to get section of symbol '" + Name + + "': " + toString(SecOrErr.takeError())); + return SymbolIndexes; } - } else { - reportUniqueWarning("unable to read the symbol table: " + - toString(SymsOrError.takeError())); } + + SymbolIndexes.push_back(Index); } - std::string FuncName = "?"; - if (!FuncSymIndex) + return SymbolIndexes; +} + +template <class ELFT> +bool ELFDumper<ELFT>::printFunctionStackSize( + uint64_t SymValue, Optional<const Elf_Shdr *> FunctionSec, + const Elf_Shdr &StackSizeSec, DataExtractor Data, uint64_t *Offset) { + SmallVector<uint32_t> FuncSymIndexes = + this->getSymbolIndexesForFunctionAddress(SymValue, FunctionSec); + if (FuncSymIndexes.empty()) reportUniqueWarning( "could not identify function symbol for stack size entry in " + describe(StackSizeSec)); - else - FuncName = this->getStaticSymbolName(FuncSymIndex); // Extract the size. The expectation is that Offset is pointing to the right // place, i.e. past the function address. @@ -5530,17 +5805,27 @@ bool ELFDumper<ELFT>::printFunctionStackSize( toString(std::move(Err))); return false; } - printStackSizeEntry(StackSize, FuncName); + + if (FuncSymIndexes.empty()) { + printStackSizeEntry(StackSize, {"?"}); + } else { + SmallVector<std::string> FuncSymNames; + for (uint32_t Index : FuncSymIndexes) + FuncSymNames.push_back(this->getStaticSymbolName(Index)); + printStackSizeEntry(StackSize, FuncSymNames); + } + return true; } template <class ELFT> void GNUELFDumper<ELFT>::printStackSizeEntry(uint64_t Size, - StringRef FuncName) { + ArrayRef<std::string> FuncNames) { OS.PadToColumn(2); OS << format_decimal(Size, 11); OS.PadToColumn(18); - OS << FuncName << "\n"; + + OS << join(FuncNames.begin(), FuncNames.end(), ", ") << "\n"; } template <class ELFT> @@ -5628,28 +5913,15 @@ void ELFDumper<ELFT>::printNonRelocatableStackSizes( } template <class ELFT> -void ELFDumper<ELFT>::printRelocatableStackSizes( - std::function<void()> PrintHeader) { - // Build a map between stack size sections and their corresponding relocation - // sections. - llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> StackSizeRelocMap; +void ELFDumper<ELFT>::getSectionAndRelocations( + std::function<bool(const Elf_Shdr &)> IsMatch, + llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> &SecToRelocMap) { for (const Elf_Shdr &Sec : cantFail(Obj.sections())) { - StringRef SectionName; - if (Expected<StringRef> NameOrErr = Obj.getSectionName(Sec)) - SectionName = *NameOrErr; - else - consumeError(NameOrErr.takeError()); - - // A stack size section that we haven't encountered yet is mapped to the - // null section until we find its corresponding relocation section. - if (SectionName == ".stack_sizes") - if (StackSizeRelocMap - .insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr)) + if (IsMatch(Sec)) + if (SecToRelocMap.insert(std::make_pair(&Sec, (const Elf_Shdr *)nullptr)) .second) continue; - // Check relocation sections if they are relocating contents of a - // stack sizes section. if (Sec.sh_type != ELF::SHT_RELA && Sec.sh_type != ELF::SHT_REL) continue; @@ -5660,14 +5932,28 @@ void ELFDumper<ELFT>::printRelocatableStackSizes( toString(RelSecOrErr.takeError())); continue; } - const Elf_Shdr *ContentsSec = *RelSecOrErr; - if (this->getPrintableSectionName(**RelSecOrErr) != ".stack_sizes") - continue; - - // Insert a mapping from the stack sizes section to its relocation section. - StackSizeRelocMap[ContentsSec] = &Sec; + if (IsMatch(*ContentsSec)) + SecToRelocMap[ContentsSec] = &Sec; } +} + +template <class ELFT> +void ELFDumper<ELFT>::printRelocatableStackSizes( + std::function<void()> PrintHeader) { + // Build a map between stack size sections and their corresponding relocation + // sections. + llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> StackSizeRelocMap; + auto IsMatch = [&](const Elf_Shdr &Sec) -> bool { + StringRef SectionName; + if (Expected<StringRef> NameOrErr = Obj.getSectionName(Sec)) + SectionName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + return SectionName == ".stack_sizes"; + }; + getSectionAndRelocations(IsMatch, StackSizeRelocMap); for (const auto &StackSizeMapEntry : StackSizeRelocMap) { PrintHeader(); @@ -5728,7 +6014,7 @@ void GNUELFDumper<ELFT>::printStackSizes() { OS.PadToColumn(9); OS << "Size"; OS.PadToColumn(18); - OS << "Function\n"; + OS << "Functions\n"; HeaderHasBeenPrinted = true; }; @@ -5977,11 +6263,32 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printFileHeaders() { W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderMipsFlags), unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI), unsigned(ELF::EF_MIPS_MACH)); - else if (E.e_machine == EM_AMDGPU) - W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderAMDGPUFlags), - unsigned(ELF::EF_AMDGPU_MACH)); - else if (E.e_machine == EM_RISCV) + else if (E.e_machine == EM_AMDGPU) { + switch (E.e_ident[ELF::EI_ABIVERSION]) { + default: + W.printHex("Flags", E.e_flags); + break; + case 0: + // ELFOSABI_AMDGPU_PAL, ELFOSABI_AMDGPU_MESA3D support *_V3 flags. + LLVM_FALLTHROUGH; + case ELF::ELFABIVERSION_AMDGPU_HSA_V3: + W.printFlags("Flags", E.e_flags, + makeArrayRef(ElfHeaderAMDGPUFlagsABIVersion3), + unsigned(ELF::EF_AMDGPU_MACH)); + break; + case ELF::ELFABIVERSION_AMDGPU_HSA_V4: + W.printFlags("Flags", E.e_flags, + makeArrayRef(ElfHeaderAMDGPUFlagsABIVersion4), + unsigned(ELF::EF_AMDGPU_MACH), + unsigned(ELF::EF_AMDGPU_FEATURE_XNACK_V4), + unsigned(ELF::EF_AMDGPU_FEATURE_SRAMECC_V4)); + break; + } + } else if (E.e_machine == EM_RISCV) W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderRISCVFlags)); + else if (E.e_machine == EM_AVR) + W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderAVRFlags), + unsigned(ELF::EF_AVR_ARCH_MASK)); else W.printFlags("Flags", E.e_flags); W.printNumber("HeaderSize", E.e_ehsize); @@ -6407,28 +6714,142 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printHashHistograms() { W.startLine() << "Hash Histogram not implemented!\n"; } +// Returns true if rel/rela section exists, and populates SymbolIndices. +// Otherwise returns false. +template <class ELFT> +static bool getSymbolIndices(const typename ELFT::Shdr *CGRelSection, + const ELFFile<ELFT> &Obj, + const LLVMELFDumper<ELFT> *Dumper, + SmallVector<uint32_t, 128> &SymbolIndices) { + if (!CGRelSection) { + Dumper->reportUniqueWarning( + "relocation section for a call graph section doesn't exist"); + return false; + } + + if (CGRelSection->sh_type == SHT_REL) { + typename ELFT::RelRange CGProfileRel; + Expected<typename ELFT::RelRange> CGProfileRelOrError = + Obj.rels(*CGRelSection); + if (!CGProfileRelOrError) { + Dumper->reportUniqueWarning("unable to load relocations for " + "SHT_LLVM_CALL_GRAPH_PROFILE section: " + + toString(CGProfileRelOrError.takeError())); + return false; + } + + CGProfileRel = *CGProfileRelOrError; + for (const typename ELFT::Rel &Rel : CGProfileRel) + SymbolIndices.push_back(Rel.getSymbol(Obj.isMips64EL())); + } else { + // MC unconditionally produces SHT_REL, but GNU strip/objcopy may convert + // the format to SHT_RELA + // (https://sourceware.org/bugzilla/show_bug.cgi?id=28035) + typename ELFT::RelaRange CGProfileRela; + Expected<typename ELFT::RelaRange> CGProfileRelaOrError = + Obj.relas(*CGRelSection); + if (!CGProfileRelaOrError) { + Dumper->reportUniqueWarning("unable to load relocations for " + "SHT_LLVM_CALL_GRAPH_PROFILE section: " + + toString(CGProfileRelaOrError.takeError())); + return false; + } + + CGProfileRela = *CGProfileRelaOrError; + for (const typename ELFT::Rela &Rela : CGProfileRela) + SymbolIndices.push_back(Rela.getSymbol(Obj.isMips64EL())); + } + + return true; +} + template <class ELFT> void LLVMELFDumper<ELFT>::printCGProfile() { - ListScope L(W, "CGProfile"); - if (!this->DotCGProfileSec) - return; + llvm::MapVector<const Elf_Shdr *, const Elf_Shdr *> SecToRelocMap; - Expected<ArrayRef<Elf_CGProfile>> CGProfileOrErr = - this->Obj.template getSectionContentsAsArray<Elf_CGProfile>( - *this->DotCGProfileSec); - if (!CGProfileOrErr) { - this->reportUniqueWarning( - "unable to dump the SHT_LLVM_CALL_GRAPH_PROFILE section: " + - toString(CGProfileOrErr.takeError())); - return; + auto IsMatch = [](const Elf_Shdr &Sec) -> bool { + return Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE; + }; + this->getSectionAndRelocations(IsMatch, SecToRelocMap); + + for (const auto &CGMapEntry : SecToRelocMap) { + const Elf_Shdr *CGSection = CGMapEntry.first; + const Elf_Shdr *CGRelSection = CGMapEntry.second; + + Expected<ArrayRef<Elf_CGProfile>> CGProfileOrErr = + this->Obj.template getSectionContentsAsArray<Elf_CGProfile>(*CGSection); + if (!CGProfileOrErr) { + this->reportUniqueWarning( + "unable to load the SHT_LLVM_CALL_GRAPH_PROFILE section: " + + toString(CGProfileOrErr.takeError())); + return; + } + + SmallVector<uint32_t, 128> SymbolIndices; + bool UseReloc = + getSymbolIndices<ELFT>(CGRelSection, this->Obj, this, SymbolIndices); + if (UseReloc && SymbolIndices.size() != CGProfileOrErr->size() * 2) { + this->reportUniqueWarning( + "number of from/to pairs does not match number of frequencies"); + UseReloc = false; + } + + ListScope L(W, "CGProfile"); + for (uint32_t I = 0, Size = CGProfileOrErr->size(); I != Size; ++I) { + const Elf_CGProfile &CGPE = (*CGProfileOrErr)[I]; + DictScope D(W, "CGProfileEntry"); + if (UseReloc) { + uint32_t From = SymbolIndices[I * 2]; + uint32_t To = SymbolIndices[I * 2 + 1]; + W.printNumber("From", this->getStaticSymbolName(From), From); + W.printNumber("To", this->getStaticSymbolName(To), To); + } + W.printNumber("Weight", CGPE.cgp_weight); + } } +} - for (const Elf_CGProfile &CGPE : *CGProfileOrErr) { - DictScope D(W, "CGProfileEntry"); - W.printNumber("From", this->getStaticSymbolName(CGPE.cgp_from), - CGPE.cgp_from); - W.printNumber("To", this->getStaticSymbolName(CGPE.cgp_to), - CGPE.cgp_to); - W.printNumber("Weight", CGPE.cgp_weight); +template <class ELFT> void LLVMELFDumper<ELFT>::printBBAddrMaps() { + bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL; + for (const Elf_Shdr &Sec : cantFail(this->Obj.sections())) { + if (Sec.sh_type != SHT_LLVM_BB_ADDR_MAP) + continue; + Optional<const Elf_Shdr *> FunctionSec = None; + if (IsRelocatable) + FunctionSec = + unwrapOrError(this->FileName, this->Obj.getSection(Sec.sh_link)); + ListScope L(W, "BBAddrMap"); + Expected<std::vector<Elf_BBAddrMap>> BBAddrMapOrErr = + this->Obj.decodeBBAddrMap(Sec); + if (!BBAddrMapOrErr) { + this->reportUniqueWarning("unable to dump " + this->describe(Sec) + ": " + + toString(BBAddrMapOrErr.takeError())); + continue; + } + for (const Elf_BBAddrMap &AM : *BBAddrMapOrErr) { + DictScope D(W, "Function"); + W.printHex("At", AM.Addr); + SmallVector<uint32_t> FuncSymIndex = + this->getSymbolIndexesForFunctionAddress(AM.Addr, FunctionSec); + std::string FuncName = "<?>"; + if (FuncSymIndex.empty()) + this->reportUniqueWarning( + "could not identify function symbol for address (0x" + + Twine::utohexstr(AM.Addr) + ") in " + this->describe(Sec)); + else + FuncName = this->getStaticSymbolName(FuncSymIndex.front()); + W.printString("Name", FuncName); + + ListScope L(W, "BB entries"); + for (const typename Elf_BBAddrMap::BBEntry &BBE : AM.BBEntries) { + DictScope L(W); + W.printHex("Offset", BBE.Offset); + W.printHex("Size", BBE.Size); + W.printBoolean("HasReturn", BBE.HasReturn); + W.printBoolean("HasTailCall", BBE.HasTailCall); + W.printBoolean("IsEHPad", BBE.IsEHPad); + W.printBoolean("CanFallThrough", BBE.CanFallThrough); + } + } } } @@ -6449,15 +6870,17 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printAddrsig() { } template <typename ELFT> -static void printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc, +static bool printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc, ScopedPrinter &W) { + // Return true if we were able to pretty-print the note, false otherwise. switch (NoteType) { default: - return; + return false; case ELF::NT_GNU_ABI_TAG: { const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc); if (!AbiTag.IsValid) { W.printString("ABI", "<corrupt GNU_ABI_TAG>"); + return false; } else { W.printString("OS", AbiTag.OSName); W.printString("ABI", AbiTag.ABI); @@ -6477,6 +6900,7 @@ static void printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc, W.printString(Property); break; } + return true; } static void printCoreNoteLLVMStyle(const CoreNote &Note, ScopedPrinter &W) { @@ -6505,7 +6929,7 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printNotes() { auto EndNotes = [&] { NoteScope.reset(); }; - auto ProcessNote = [&](const Elf_Note &Note) -> Error { + auto ProcessNote = [&](const Elf_Note &Note, bool IsCore) -> Error { DictScope D2(W, "Note"); StringRef Name = Note.getName(); ArrayRef<uint8_t> Descriptor = Note.getDesc(); @@ -6524,28 +6948,42 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printNotes() { "Unknown (" + to_string(format_hex(Type, 10)) + ")"); // Print the description, or fallback to printing raw bytes for unknown - // owners. + // owners/if we fail to pretty-print the contents. if (Name == "GNU") { - printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W); + if (printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W)) + return Error::success(); + } else if (Name == "FreeBSD") { + if (Optional<FreeBSDNote> N = + getFreeBSDNote<ELFT>(Type, Descriptor, IsCore)) { + W.printString(N->Type, N->Value); + return Error::success(); + } } else if (Name == "AMD") { const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); - if (!N.Type.empty()) + if (!N.Type.empty()) { W.printString(N.Type, N.Value); + return Error::success(); + } } else if (Name == "AMDGPU") { const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); - if (!N.Type.empty()) + if (!N.Type.empty()) { W.printString(N.Type, N.Value); + return Error::success(); + } } else if (Name == "CORE") { if (Type == ELF::NT_FILE) { DataExtractor DescExtractor(Descriptor, ELFT::TargetEndianness == support::little, sizeof(Elf_Addr)); - if (Expected<CoreNote> Note = readCoreNote(DescExtractor)) - printCoreNoteLLVMStyle(*Note, W); - else - return Note.takeError(); + if (Expected<CoreNote> N = readCoreNote(DescExtractor)) { + printCoreNoteLLVMStyle(*N, W); + return Error::success(); + } else { + return N.takeError(); + } } - } else if (!Descriptor.empty()) { + } + if (!Descriptor.empty()) { W.printBinaryBlock("Description data", Descriptor); } return Error::success(); @@ -6614,9 +7052,10 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printStackSizes() { } template <class ELFT> -void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size, StringRef FuncName) { +void LLVMELFDumper<ELFT>::printStackSizeEntry(uint64_t Size, + ArrayRef<std::string> FuncNames) { DictScope D(W, "Entry"); - W.printString("Function", FuncName); + W.printList("Functions", FuncNames); W.printHex("Size", Size); } diff --git a/llvm/tools/llvm-readobj/MachODumper.cpp b/llvm/tools/llvm-readobj/MachODumper.cpp index c13b1f3bf2a0..433ca9335324 100644 --- a/llvm/tools/llvm-readobj/MachODumper.cpp +++ b/llvm/tools/llvm-readobj/MachODumper.cpp @@ -256,11 +256,14 @@ static const EnumEntry<unsigned> MachOSymbolRefTypes[] = { }; static const EnumEntry<unsigned> MachOSymbolFlags[] = { + { "ThumbDef", 0x8 }, { "ReferencedDynamically", 0x10 }, { "NoDeadStrip", 0x20 }, { "WeakRef", 0x40 }, { "WeakDef", 0x80 }, + { "SymbolResolver", 0x100 }, { "AltEntry", 0x200 }, + { "ColdFunc", 0x400 }, }; static const EnumEntry<unsigned> MachOSymbolTypes[] = { @@ -651,9 +654,9 @@ void MachODumper::printSymbol(const SymbolRef &Symbol) { makeArrayRef(MachOSymbolTypes)); } W.printHex("Section", SectionName, MOSymbol.SectionIndex); - W.printEnum("RefType", static_cast<uint16_t>(MOSymbol.Flags & 0xF), + W.printEnum("RefType", static_cast<uint16_t>(MOSymbol.Flags & 0x7), makeArrayRef(MachOSymbolRefTypes)); - W.printFlags("Flags", static_cast<uint16_t>(MOSymbol.Flags & ~0xF), + W.printFlags("Flags", static_cast<uint16_t>(MOSymbol.Flags & ~0x7), makeArrayRef(MachOSymbolFlags)); W.printHex("Value", MOSymbol.Value); } diff --git a/llvm/tools/llvm-readobj/ObjDumper.cpp b/llvm/tools/llvm-readobj/ObjDumper.cpp index fc91d81f0784..87c229356e20 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.cpp +++ b/llvm/tools/llvm-readobj/ObjDumper.cpp @@ -52,6 +52,25 @@ static void printAsPrintable(raw_ostream &W, const uint8_t *Start, size_t Len) { W << (isPrint(Start[i]) ? static_cast<char>(Start[i]) : '.'); } +void ObjDumper::printAsStringList(StringRef StringContent) { + const uint8_t *StrContent = StringContent.bytes_begin(); + const uint8_t *CurrentWord = StrContent; + const uint8_t *StrEnd = StringContent.bytes_end(); + + while (CurrentWord <= StrEnd) { + size_t WordSize = strnlen(reinterpret_cast<const char *>(CurrentWord), + StrEnd - CurrentWord); + if (!WordSize) { + CurrentWord++; + continue; + } + W.startLine() << format("[%6tx] ", CurrentWord - StrContent); + printAsPrintable(W.startLine(), CurrentWord, WordSize); + W.startLine() << '\n'; + CurrentWord += WordSize + 1; + } +} + static std::vector<object::SectionRef> getSectionRefsByNameOrIndex(const object::ObjectFile &Obj, ArrayRef<std::string> Sections) { @@ -109,23 +128,7 @@ void ObjDumper::printSectionsAsString(const object::ObjectFile &Obj, StringRef SectionContent = unwrapOrError(Obj.getFileName(), Section.getContents()); - - const uint8_t *SecContent = SectionContent.bytes_begin(); - const uint8_t *CurrentWord = SecContent; - const uint8_t *SecEnd = SectionContent.bytes_end(); - - while (CurrentWord <= SecEnd) { - size_t WordSize = strnlen(reinterpret_cast<const char *>(CurrentWord), - SecEnd - CurrentWord); - if (!WordSize) { - CurrentWord++; - continue; - } - W.startLine() << format("[%6tx] ", CurrentWord - SecContent); - printAsPrintable(W.startLine(), CurrentWord, WordSize); - W.startLine() << '\n'; - CurrentWord += WordSize + 1; - } + printAsStringList(SectionContent); } } diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index d4e166b504cf..7e1c0ca35127 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -72,6 +72,7 @@ public: virtual void printGroupSections() {} virtual void printHashHistograms() {} virtual void printCGProfile() {} + virtual void printBBAddrMaps() {} virtual void printAddrsig() {} virtual void printNotes() {} virtual void printELFLinkerOptions() {} @@ -104,8 +105,13 @@ public: virtual void printMachOIndirectSymbols() { } virtual void printMachOLinkerOptions() { } + // Currently only implemented for XCOFF. + virtual void printStringTable() { } + virtual void printStackMap() const = 0; + void printAsStringList(StringRef StringContent); + void printSectionsAsString(const object::ObjectFile &Obj, ArrayRef<std::string> Sections); void printSectionsAsHex(const object::ObjectFile &Obj, diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td new file mode 100644 index 000000000000..493b93769eb4 --- /dev/null +++ b/llvm/tools/llvm-readobj/Opts.td @@ -0,0 +1,128 @@ +include "llvm/Option/OptParser.td" + +class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>; +class FF<string name, string help> : Flag<["--"], name>, HelpText<help>; + +multiclass BB<string name, string help1, string help2> { + def NAME: Flag<["--"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; +} + +multiclass Eq<string name, string help> { + def NAME #_EQ : Joined<["--"], name #"=">, HelpText<help>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; +} + +def addrsig : FF<"addrsig", "Display address-significance table">; +def all : FF<"all", "Equivalent to setting: --file-header, --program-headers, --section-headers, " + "--symbols, --relocations, --dynamic-table, --notes, --version-info, --unwind, " + "--section-groups and --histogram">; +def arch_specific : FF<"arch-specific", "Display architecture-specific information">; +def bb_addr_map : FF<"bb-addr-map", "Display the BB address map section">; +def cg_profile : FF<"cg-profile", "Display call graph profile section">; +defm demangle : BB<"demangle", "Demangle symbol names", "Do not demangle symbol names (default)">; +def dependent_libraries : FF<"dependent-libraries", "Display the dependent libraries section">; +def dyn_relocations : FF<"dyn-relocations", "Display the dynamic relocation entries in the file">; +def dyn_syms : FF<"dyn-syms", "Display the dynamic symbol table">; +def expand_relocs : FF<"expand-relocs", "Expand each shown relocation to multiple lines">; +def file_header : FF<"file-header", "Display file header">; +def headers : FF<"headers", "Equivalent to setting: --file-header, --program-headers, --section-headers">; +defm hex_dump : Eq<"hex-dump", "Display the specified section(s) as hexadecimal bytes">, MetaVarName<"<name or index>">; +def relocs : FF<"relocs", "Display the relocation entries in the file">; +def section_data : FF<"section-data", "Display section data for each section shown">; +def section_details : FF<"section-details", "Display the section details">; +def section_headers : FF<"section-headers", "Display section headers">; +def section_mapping : FF<"section-mapping", "Display the section to segment mapping">; +def section_mapping_EQ_false : FF<"section-mapping=false", "Don't display the section to segment mapping">, Flags<[HelpHidden]>; +def section_relocations : FF<"section-relocations", "Display relocations for each section shown">; +def section_symbols : FF<"section-symbols", "Display symbols for each section shown">; +def stack_sizes : FF<"stack-sizes", "Display contents of all stack sizes sections">; +def stackmap : FF<"stackmap", "Display contents of stackmap section">; +defm string_dump : Eq<"string-dump", "Display the specified section(s) as a list of strings">, MetaVarName<"<name or index>">; +def string_table : FF<"string-table", "Display the string table (only for XCOFF now)">; +def symbols : FF<"symbols", "Display the symbol table. Also display the dynamic symbol table when using GNU output style for ELF">; +def unwind : FF<"unwind", "Display unwind information">; + +// ELF specific options. +def grp_elf : OptionGroup<"kind">, HelpText<"OPTIONS (ELF specific)">; +def dynamic_table : FF<"dynamic-table", "Display the dynamic section table">, Group<grp_elf>; +def elf_linker_options : FF<"elf-linker-options", "Display the .linker-options section">, Group<grp_elf>; +defm elf_output_style : Eq<"elf-output-style", "Specify ELF dump style">, Group<grp_elf>; +def histogram : FF<"histogram", "Display bucket list histogram for hash sections">, Group<grp_elf>; +def section_groups : FF<"section-groups", "Display section groups">, Group<grp_elf>; +def gnu_hash_table : FF<"gnu-hash-table", "Display .gnu.hash section">, Group<grp_elf>; +def hash_symbols : FF<"hash-symbols", "Display the dynamic symbols derived from the hash section">, Group<grp_elf>; +def hash_table : FF<"hash-table", "Display .hash section">, Group<grp_elf>; +def needed_libs : FF<"needed-libs", "Display the needed libraries">, Group<grp_elf>; +def notes : FF<"notes", "Display notes">, Group<grp_elf>; +def program_headers : FF<"program-headers", "Display program headers">, Group<grp_elf>; +def raw_relr : FF<"raw-relr", "Do not decode relocations in SHT_RELR section, display raw contents">, Group<grp_elf>; +def version_info : FF<"version-info", "Display version sections">, Group<grp_elf>; + +// Mach-O specific options. +def grp_mach_o : OptionGroup<"kind">, HelpText<"OPTIONS (Mach-O specific)">; +def macho_data_in_code : FF<"macho-data-in-code", "Display Data in Code command">, Group<grp_mach_o>; +def macho_dysymtab : FF<"macho-dysymtab", "Display Dysymtab command">, Group<grp_mach_o>; +def macho_indirect_symbols : FF<"macho-indirect-symbols", "Display indirect symbols">, Group<grp_mach_o>; +def macho_linker_options : FF<"macho-linker-options", "Display linker options">, Group<grp_mach_o>; +def macho_segment : FF<"macho-segment", "Display Segment command">, Group<grp_mach_o>; +def macho_version_min : FF<"macho-version-min", "Display version min command">, Group<grp_mach_o>; + +// PE/COFF specific options. +def grp_coff : OptionGroup<"kind">, HelpText<"OPTIONS (PE/COFF specific)">; +def codeview : FF<"codeview", "Display CodeView debug information">, Group<grp_coff>; +def codeview_ghash : FF<"codeview-ghash", "Enable global hashing for CodeView type stream de-duplication">, Group<grp_coff>; +def codeview_merged_types : FF<"codeview-merged-types", "Display the merged CodeView type stream">, Group<grp_coff>; +def codeview_subsection_bytes : FF<"codeview-subsection-bytes", "Dump raw contents of codeview debug sections and records">, Group<grp_coff>; +def coff_basereloc : FF<"coff-basereloc", "Display .reloc section">, Group<grp_coff>; +def coff_debug_directory : FF<"coff-debug-directory", "Display debug directory">, Group<grp_coff>; +def coff_directives : FF<"coff-directives", "Display .drectve section">, Group<grp_coff>; +def coff_exports : FF<"coff-exports", "Display export table">, Group<grp_coff>; +def coff_imports : FF<"coff-imports", "Display import table">, Group<grp_coff>; +def coff_load_config : FF<"coff-load-config", "Display load config">, Group<grp_coff>; +def coff_resources : FF<"coff-resources", "Display .rsrc section">, Group<grp_coff>; +def coff_tls_directory : FF<"coff-tls-directory", "Display TLS directory">, Group<grp_coff>; + +def help : FF<"help", "Display this help">; +def version : FF<"version", "Display the version">; + +// Ignored for GNU readelf compatibility. +def : F<"W", "Ignored for GNU readelf compatibility">; +def : FF<"wide", "Ignored for GNU readelf compatibility">; + +// Traditional llvm-readobj Aliases. +def : Flag<["--"], "dt">, Alias<dyn_syms>, HelpText<"Alias for --dyn-syms">; +def : Flag<["--"], "sd">, Alias<section_data>, HelpText<"Alias for --section-data">; +def : Flag<["--"], "st">, Alias<section_symbols>, HelpText<"Alias for --section-symbols">; +def : Flag<["--"], "sr">, Alias<section_relocations>, HelpText<"Alias for --section-relocations">; + +// Aliases. +def : FF<"dyn-symbols", "Alias for --dyn-syms">, Alias<dyn_syms>; +def : FF<"dynamic", "Alias for --dynamic-table">, Alias<dynamic_table>; +def : FF<"elf-cg-profile", "Alias for --cg-profile">, Alias<cg_profile>, Flags<[HelpHidden]>; +def : FF<"elf-hash-histogram", "Alias for --histogram">, Alias<histogram>, Flags<[HelpHidden]>; +def : FF<"elf-section-groups", "Alias for --section-groups">, Alias<section_groups>, Flags<[HelpHidden]>; +def : FF<"file-headers", "Alias for --file-header">, Alias<file_header>, Flags<[HelpHidden]>; +def : FF<"relocations", "Alias for --relocs">, Alias<relocs>; +def : FF<"sections", "Alias for --section-headers">, Alias<section_headers>; +def : FF<"segments", "Alias for --program-headers">, Alias<program_headers>, Group<grp_elf>; +def : FF<"syms", "Alias for --symbols">, Alias<symbols>; + +def : F<"A", "Alias for --arch-specific">, Alias<arch_specific>; +def : F<"a", "Alias for --all">, Alias<all>; +def : F<"C", "Alias for --demangle">, Alias<demangle>; +def : F<"d", "Alias for --dynamic-table">, Alias<dynamic_table>, Group<grp_elf>; +def : F<"e", "Alias for --headers">, Alias<headers>; +def : F<"g", "Alias for --section-groups">, Alias<section_groups>, Group<grp_elf>; +def : F<"h", "Alias for --file-header">, Alias<file_header>; +def : F<"I", "Alias for --histogram">, Alias<histogram>, Group<grp_elf>; +def : F<"l", "Alias for --program-headers">, Alias<program_headers>; +def : F<"n", "Alias for --notes">, Alias<notes>; +def : JoinedOrSeparate<["-"], "p">, Alias<string_dump_EQ>, HelpText<"Alias for --string-dump">, MetaVarName<"<name or index>">; +def : F<"r", "Alias for --relocs">, Alias<relocs>; +def : F<"S", "Alias for --section-headers">, Alias<section_headers>; +def : F<"s", "Alias for --symbols">, Alias<symbols>; +def : F<"t", "Alias for --section-details">, Alias<section_details>; +def : F<"u", "Alias for --unwind">, Alias<unwind>; +def : F<"V", "Alias for --version-info">, Alias<version_info>, Group<grp_elf>; +def : JoinedOrSeparate<["-"], "x">, Alias<hex_dump_EQ>, HelpText<"Alias for --hex-dump">, MetaVarName<"<name or index>">; diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp index fb7134d20a85..f7dcaa35656f 100644 --- a/llvm/tools/llvm-readobj/WasmDumper.cpp +++ b/llvm/tools/llvm-readobj/WasmDumper.cpp @@ -23,8 +23,8 @@ namespace { static const EnumEntry<unsigned> WasmSymbolTypes[] = { #define ENUM_ENTRY(X) \ { #X, wasm::WASM_SYMBOL_TYPE_##X } - ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL), - ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT), ENUM_ENTRY(TABLE), + ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL), + ENUM_ENTRY(SECTION), ENUM_ENTRY(TAG), ENUM_ENTRY(TABLE), #undef ENUM_ENTRY }; @@ -33,7 +33,7 @@ static const EnumEntry<uint32_t> WasmSectionTypes[] = { { #X, wasm::WASM_SEC_##X } ENUM_ENTRY(CUSTOM), ENUM_ENTRY(TYPE), ENUM_ENTRY(IMPORT), ENUM_ENTRY(FUNCTION), ENUM_ENTRY(TABLE), ENUM_ENTRY(MEMORY), - ENUM_ENTRY(GLOBAL), ENUM_ENTRY(EVENT), ENUM_ENTRY(EXPORT), + ENUM_ENTRY(GLOBAL), ENUM_ENTRY(TAG), ENUM_ENTRY(EXPORT), ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE), ENUM_ENTRY(DATA), ENUM_ENTRY(DATACOUNT), #undef ENUM_ENTRY @@ -192,7 +192,7 @@ void WasmDumper::printSectionHeaders() { ListScope Group(W, "Memories"); for (const wasm::WasmLimits &Memory : Obj->memories()) { DictScope Group(W, "Memory"); - W.printNumber("InitialPages", Memory.Initial); + W.printNumber("MinPages", Memory.Minimum); if (Memory.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) { W.printNumber("MaxPages", WasmSec.Offset); } diff --git a/llvm/tools/llvm-readobj/XCOFFDumper.cpp b/llvm/tools/llvm-readobj/XCOFFDumper.cpp index 8f0f18cedceb..94ef96e447ce 100644 --- a/llvm/tools/llvm-readobj/XCOFFDumper.cpp +++ b/llvm/tools/llvm-readobj/XCOFFDumper.cpp @@ -34,13 +34,14 @@ public: void printUnwindInfo() override; void printStackMap() const override; void printNeededLibraries() override; + void printStringTable() override; private: template <typename T> void printSectionHeaders(ArrayRef<T> Sections); template <typename T> void printGenericSectionHeader(T &Sec) const; template <typename T> void printOverflowSectionHeader(T &Sec) const; void printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr); - void printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr); + void printCsectAuxEnt(XCOFFCsectAuxRef AuxEntRef); void printSectAuxEntForStat(const XCOFFSectAuxEntForStat *AuxEntPtr); void printSymbol(const SymbolRef &); void printRelocations(ArrayRef<XCOFFSectionHeader32> Sections); @@ -164,10 +165,17 @@ static const EnumEntry<XCOFF::CFileStringType> FileStringType[] = { #undef ECase }; +static const EnumEntry<XCOFF::SymbolAuxType> SymAuxType[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(AUX_EXCEPT), ECase(AUX_FCN), ECase(AUX_SYM), ECase(AUX_FILE), + ECase(AUX_CSECT), ECase(AUX_SECT) +#undef ECase +}; + void XCOFFDumper::printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr) { - if (Obj.is64Bit()) - report_fatal_error( - "Printing for File Auxiliary Entry in 64-bit is unimplemented."); + assert((!Obj.is64Bit() || AuxEntPtr->AuxType == XCOFF::AUX_FILE) && + "Mismatched auxiliary type!"); StringRef FileName = unwrapOrError(Obj.getFileName(), Obj.getCFileName(AuxEntPtr)); DictScope SymDs(W, "File Auxiliary Entry"); @@ -176,19 +184,22 @@ void XCOFFDumper::printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr) { W.printString("Name", FileName); W.printEnum("Type", static_cast<uint8_t>(AuxEntPtr->Type), makeArrayRef(FileStringType)); + if (Obj.is64Bit()) { + W.printEnum("Auxiliary Type", static_cast<uint8_t>(AuxEntPtr->AuxType), + makeArrayRef(SymAuxType)); + } } static const EnumEntry<XCOFF::StorageMappingClass> CsectStorageMappingClass[] = { #define ECase(X) \ { #X, XCOFF::X } - ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB), - ECase(XMC_GL), ECase(XMC_XO), ECase(XMC_SV), - ECase(XMC_SV64), ECase(XMC_SV3264), ECase(XMC_TI), - ECase(XMC_TB), ECase(XMC_RW), ECase(XMC_TC0), - ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS), - ECase(XMC_UA), ECase(XMC_BS), ECase(XMC_UC), - ECase(XMC_TL), ECase(XMC_TE) + ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB), ECase(XMC_GL), + ECase(XMC_XO), ECase(XMC_SV), ECase(XMC_SV64), ECase(XMC_SV3264), + ECase(XMC_TI), ECase(XMC_TB), ECase(XMC_RW), ECase(XMC_TC0), + ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS), ECase(XMC_UA), + ECase(XMC_BS), ECase(XMC_UC), ECase(XMC_TL), ECase(XMC_UL), + ECase(XMC_TE) #undef ECase }; @@ -199,27 +210,32 @@ static const EnumEntry<XCOFF::SymbolType> CsectSymbolTypeClass[] = { #undef ECase }; -void XCOFFDumper::printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr) { - assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file."); +void XCOFFDumper::printCsectAuxEnt(XCOFFCsectAuxRef AuxEntRef) { + assert((!Obj.is64Bit() || AuxEntRef.getAuxType64() == XCOFF::AUX_CSECT) && + "Mismatched auxiliary type!"); DictScope SymDs(W, "CSECT Auxiliary Entry"); - W.printNumber("Index", - Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr))); - if (AuxEntPtr->isLabel()) - W.printNumber("ContainingCsectSymbolIndex", AuxEntPtr->SectionOrLength); - else - W.printNumber("SectionLen", AuxEntPtr->SectionOrLength); - W.printHex("ParameterHashIndex", AuxEntPtr->ParameterHashIndex); - W.printHex("TypeChkSectNum", AuxEntPtr->TypeChkSectNum); + W.printNumber("Index", Obj.getSymbolIndex(AuxEntRef.getEntryAddress())); + W.printNumber(AuxEntRef.isLabel() ? "ContainingCsectSymbolIndex" + : "SectionLen", + AuxEntRef.getSectionOrLength()); + W.printHex("ParameterHashIndex", AuxEntRef.getParameterHashIndex()); + W.printHex("TypeChkSectNum", AuxEntRef.getTypeChkSectNum()); // Print out symbol alignment and type. - W.printNumber("SymbolAlignmentLog2", AuxEntPtr->getAlignmentLog2()); - W.printEnum("SymbolType", AuxEntPtr->getSymbolType(), + W.printNumber("SymbolAlignmentLog2", AuxEntRef.getAlignmentLog2()); + W.printEnum("SymbolType", AuxEntRef.getSymbolType(), makeArrayRef(CsectSymbolTypeClass)); W.printEnum("StorageMappingClass", - static_cast<uint8_t>(AuxEntPtr->StorageMappingClass), + static_cast<uint8_t>(AuxEntRef.getStorageMappingClass()), makeArrayRef(CsectStorageMappingClass)); - W.printHex("StabInfoIndex", AuxEntPtr->StabInfoIndex); - W.printHex("StabSectNum", AuxEntPtr->StabSectNum); + + if (Obj.is64Bit()) { + W.printEnum("Auxiliary Type", static_cast<uint8_t>(XCOFF::AUX_CSECT), + makeArrayRef(SymAuxType)); + } else { + W.printHex("StabInfoIndex", AuxEntRef.getStabInfoIndex32()); + W.printHex("StabSectNum", AuxEntRef.getStabSectNum32()); + } } void XCOFFDumper::printSectAuxEntForStat( @@ -301,53 +317,62 @@ static const EnumEntry<XCOFF::CFileCpuId> CFileCpuIdClass[] = { }; void XCOFFDumper::printSymbol(const SymbolRef &S) { - if (Obj.is64Bit()) - report_fatal_error("64-bit support is unimplemented."); - DataRefImpl SymbolDRI = S.getRawDataRefImpl(); - const XCOFFSymbolEntry *SymbolEntPtr = Obj.toSymbolEntry(SymbolDRI); + XCOFFSymbolRef SymbolEntRef = Obj.toSymbolRef(SymbolDRI); - XCOFFSymbolRef XCOFFSymRef(SymbolDRI, &Obj); - uint8_t NumberOfAuxEntries = XCOFFSymRef.getNumberOfAuxEntries(); + uint8_t NumberOfAuxEntries = SymbolEntRef.getNumberOfAuxEntries(); DictScope SymDs(W, "Symbol"); StringRef SymbolName = - unwrapOrError(Obj.getFileName(), Obj.getSymbolName(SymbolDRI)); + unwrapOrError(Obj.getFileName(), SymbolEntRef.getName()); - W.printNumber("Index", - Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(SymbolEntPtr))); + W.printNumber("Index", Obj.getSymbolIndex(SymbolEntRef.getEntryAddress())); W.printString("Name", SymbolName); - W.printHex(GetSymbolValueName(SymbolEntPtr->StorageClass), - SymbolEntPtr->Value); + W.printHex(GetSymbolValueName(SymbolEntRef.getStorageClass()), + SymbolEntRef.getValue()); StringRef SectionName = - unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntPtr)); + unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntRef)); W.printString("Section", SectionName); - if (XCOFFSymRef.getStorageClass() == XCOFF::C_FILE) { - W.printEnum("Source Language ID", - SymbolEntPtr->CFileLanguageIdAndTypeId.LanguageId, + if (SymbolEntRef.getStorageClass() == XCOFF::C_FILE) { + W.printEnum("Source Language ID", SymbolEntRef.getLanguageIdForCFile(), makeArrayRef(CFileLangIdClass)); - W.printEnum("CPU Version ID", - SymbolEntPtr->CFileLanguageIdAndTypeId.CpuTypeId, + W.printEnum("CPU Version ID", SymbolEntRef.getCPUTypeIddForCFile(), makeArrayRef(CFileCpuIdClass)); } else - W.printHex("Type", SymbolEntPtr->SymbolType); + W.printHex("Type", SymbolEntRef.getSymbolType()); - W.printEnum("StorageClass", static_cast<uint8_t>(SymbolEntPtr->StorageClass), + W.printEnum("StorageClass", + static_cast<uint8_t>(SymbolEntRef.getStorageClass()), makeArrayRef(SymStorageClass)); - W.printNumber("NumberOfAuxEntries", SymbolEntPtr->NumberOfAuxEntries); + W.printNumber("NumberOfAuxEntries", NumberOfAuxEntries); if (NumberOfAuxEntries == 0) return; - switch (XCOFFSymRef.getStorageClass()) { + switch (SymbolEntRef.getStorageClass()) { case XCOFF::C_FILE: // If the symbol is C_FILE and has auxiliary entries... - for (int i = 1; i <= NumberOfAuxEntries; i++) { + for (int I = 1; I <= NumberOfAuxEntries; I++) { + uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress( + SymbolEntRef.getEntryAddress(), I); + + if (Obj.is64Bit() && + *Obj.getSymbolAuxType(AuxAddress) != XCOFF::SymbolAuxType::AUX_FILE) { + W.startLine() << "!Unexpected raw auxiliary entry data:\n"; + W.startLine() << format_bytes( + ArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(AuxAddress), + XCOFF::SymbolTableEntrySize), + 0, XCOFF::SymbolTableEntrySize) + << "\n"; + continue; + } + const XCOFFFileAuxEnt *FileAuxEntPtr = - reinterpret_cast<const XCOFFFileAuxEnt *>(SymbolEntPtr + i); + reinterpret_cast<const XCOFFFileAuxEnt *>(AuxAddress); #ifndef NDEBUG Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(FileAuxEntPtr)); #endif @@ -356,34 +381,52 @@ void XCOFFDumper::printSymbol(const SymbolRef &S) { break; case XCOFF::C_EXT: case XCOFF::C_WEAKEXT: - case XCOFF::C_HIDEXT: + case XCOFF::C_HIDEXT: { // If the symbol is for a function, and it has more than 1 auxiliary entry, // then one of them must be function auxiliary entry which we do not // support yet. - if (XCOFFSymRef.isFunction() && NumberOfAuxEntries >= 2) + if (SymbolEntRef.isFunction() && NumberOfAuxEntries >= 2) report_fatal_error("Function auxiliary entry printing is unimplemented."); // If there is more than 1 auxiliary entry, instead of printing out - // error information, print out the raw Auxiliary entry from 1st till - // the last - 1. The last one must be a CSECT Auxiliary Entry. - for (int i = 1; i < NumberOfAuxEntries; i++) { + // error information, print out the raw Auxiliary entry. + // For 32-bit object, print from first to the last - 1. The last one must be + // a CSECT Auxiliary Entry. + // For 64-bit object, print from first to last and skips if SymbolAuxType is + // AUX_CSECT. + for (int I = 1; I <= NumberOfAuxEntries; I++) { + if (I == NumberOfAuxEntries && !Obj.is64Bit()) + break; + + uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress( + SymbolEntRef.getEntryAddress(), I); + if (Obj.is64Bit() && + *Obj.getSymbolAuxType(AuxAddress) == XCOFF::SymbolAuxType::AUX_CSECT) + continue; + W.startLine() << "!Unexpected raw auxiliary entry data:\n"; W.startLine() << format_bytes( - ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i), + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(AuxAddress), XCOFF::SymbolTableEntrySize)); } - // The symbol's last auxiliary entry is a CSECT Auxiliary Entry. - printCsectAuxEnt32(XCOFFSymRef.getXCOFFCsectAuxEnt32()); + auto ErrOrCsectAuxRef = SymbolEntRef.getXCOFFCsectAuxRef(); + if (!ErrOrCsectAuxRef) + reportUniqueWarning(ErrOrCsectAuxRef.takeError()); + else + printCsectAuxEnt(*ErrOrCsectAuxRef); + break; + } case XCOFF::C_STAT: if (NumberOfAuxEntries > 1) report_fatal_error( "C_STAT symbol should not have more than 1 auxiliary entry."); const XCOFFSectAuxEntForStat *StatAuxEntPtr; - StatAuxEntPtr = - reinterpret_cast<const XCOFFSectAuxEntForStat *>(SymbolEntPtr + 1); + StatAuxEntPtr = reinterpret_cast<const XCOFFSectAuxEntForStat *>( + XCOFFObjectFile::getAdvancedSymbolEntryAddress( + SymbolEntRef.getEntryAddress(), 1)); #ifndef NDEBUG Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(StatAuxEntPtr)); #endif @@ -399,7 +442,9 @@ void XCOFFDumper::printSymbol(const SymbolRef &S) { for (int i = 1; i <= NumberOfAuxEntries; i++) { W.startLine() << "!Unexpected raw auxiliary entry data:\n"; W.startLine() << format_bytes( - ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i), + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>( + XCOFFObjectFile::getAdvancedSymbolEntryAddress( + SymbolEntRef.getEntryAddress(), i)), XCOFF::SymbolTableEntrySize)); } break; @@ -412,6 +457,12 @@ void XCOFFDumper::printSymbols() { printSymbol(S); } +void XCOFFDumper::printStringTable() { + DictScope DS(W, "StringTable"); + StringRef StrTable = Obj.getStringTable(); + printAsStringList(StrTable); +} + void XCOFFDumper::printDynamicSymbols() { llvm_unreachable("Unimplemented functionality for XCOFFDumper"); } diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index 41cd4414d051..0b49f03f4275 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -31,6 +31,9 @@ #include "llvm/Object/Wasm.h" #include "llvm/Object/WindowsResource.h" #include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DataTypes.h" @@ -47,339 +50,107 @@ using namespace llvm; using namespace llvm::object; +namespace { +using namespace llvm::opt; // for HelpHidden in Opts.inc +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class ReadobjOptTable : public opt::OptTable { +public: + ReadobjOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } +}; + +enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols }; +} // namespace + namespace opts { - cl::list<std::string> InputFilenames(cl::Positional, - cl::desc("<input object files>"), - cl::ZeroOrMore); - - // --all, -a - cl::opt<bool> - All("all", - cl::desc("Equivalent to setting: --file-headers, --program-headers, " - "--section-headers, --symbols, --relocations, " - "--dynamic-table, --notes, --version-info, --unwind, " - "--section-groups and --elf-hash-histogram.")); - cl::alias AllShort("a", cl::desc("Alias for --all"), cl::aliasopt(All)); - - // --dependent-libraries - cl::opt<bool> - DependentLibraries("dependent-libraries", - cl::desc("Display the dependent libraries section")); - - // --headers, -e - cl::opt<bool> - Headers("headers", - cl::desc("Equivalent to setting: --file-headers, --program-headers, " - "--section-headers")); - cl::alias HeadersShort("e", cl::desc("Alias for --headers"), - cl::aliasopt(Headers)); - - // --wide, -W - cl::opt<bool> - WideOutput("wide", cl::desc("Ignored for compatibility with GNU readelf"), - cl::Hidden); - cl::alias WideOutputShort("W", - cl::desc("Alias for --wide"), - cl::aliasopt(WideOutput)); - - // --file-headers, --file-header, -h - cl::opt<bool> FileHeaders("file-headers", - cl::desc("Display file headers ")); - cl::alias FileHeadersShort("h", cl::desc("Alias for --file-headers"), - cl::aliasopt(FileHeaders), cl::NotHidden); - cl::alias FileHeadersSingular("file-header", - cl::desc("Alias for --file-headers"), - cl::aliasopt(FileHeaders)); - - // --section-headers, --sections, -S - // Also -s in llvm-readobj mode. - cl::opt<bool> SectionHeaders("section-headers", - cl::desc("Display all section headers.")); - cl::alias SectionsShortUpper("S", cl::desc("Alias for --section-headers"), - cl::aliasopt(SectionHeaders), cl::NotHidden); - cl::alias SectionHeadersAlias("sections", - cl::desc("Alias for --section-headers"), - cl::aliasopt(SectionHeaders), cl::NotHidden); - - // --section-relocations - // Also --sr in llvm-readobj mode. - cl::opt<bool> SectionRelocations("section-relocations", - cl::desc("Display relocations for each section shown.")); - - // --section-symbols - // Also --st in llvm-readobj mode. - cl::opt<bool> SectionSymbols("section-symbols", - cl::desc("Display symbols for each section shown.")); - - // --section-data - // Also --sd in llvm-readobj mode. - cl::opt<bool> SectionData("section-data", - cl::desc("Display section data for each section shown.")); - - // --section-mapping - cl::opt<cl::boolOrDefault> - SectionMapping("section-mapping", - cl::desc("Display the section to segment mapping.")); - - // --relocations, --relocs, -r - cl::opt<bool> Relocations("relocations", - cl::desc("Display the relocation entries in the file")); - cl::alias RelocationsShort("r", cl::desc("Alias for --relocations"), - cl::aliasopt(Relocations), cl::NotHidden); - cl::alias RelocationsGNU("relocs", cl::desc("Alias for --relocations"), - cl::aliasopt(Relocations)); - - // --notes, -n - cl::opt<bool> Notes("notes", cl::desc("Display the ELF notes in the file")); - cl::alias NotesShort("n", cl::desc("Alias for --notes"), cl::aliasopt(Notes)); - - // --dyn-relocations - cl::opt<bool> DynRelocs("dyn-relocations", - cl::desc("Display the dynamic relocation entries in the file")); - - // --section-details - // Also -t in llvm-readelf mode. - cl::opt<bool> SectionDetails("section-details", - cl::desc("Display the section details")); - - // --symbols - // Also -s in llvm-readelf mode, or -t in llvm-readobj mode. - cl::opt<bool> - Symbols("symbols", - cl::desc("Display the symbol table. Also display the dynamic " - "symbol table when using GNU output style for ELF")); - cl::alias SymbolsGNU("syms", cl::desc("Alias for --symbols"), - cl::aliasopt(Symbols)); - - // --dyn-symbols, --dyn-syms - // Also --dt in llvm-readobj mode. - cl::opt<bool> DynamicSymbols("dyn-symbols", - cl::desc("Display the dynamic symbol table")); - cl::alias DynSymsGNU("dyn-syms", cl::desc("Alias for --dyn-symbols"), - cl::aliasopt(DynamicSymbols)); - - // --hash-symbols - cl::opt<bool> HashSymbols( - "hash-symbols", - cl::desc("Display the dynamic symbols derived from the hash section")); - - // --unwind, -u - cl::opt<bool> UnwindInfo("unwind", - cl::desc("Display unwind information")); - cl::alias UnwindInfoShort("u", - cl::desc("Alias for --unwind"), - cl::aliasopt(UnwindInfo)); - - // --dynamic-table, --dynamic, -d - cl::opt<bool> DynamicTable("dynamic-table", - cl::desc("Display the ELF .dynamic section table")); - cl::alias DynamicTableShort("d", cl::desc("Alias for --dynamic-table"), - cl::aliasopt(DynamicTable), cl::NotHidden); - cl::alias DynamicTableAlias("dynamic", cl::desc("Alias for --dynamic-table"), - cl::aliasopt(DynamicTable)); - - // --needed-libs - cl::opt<bool> NeededLibraries("needed-libs", - cl::desc("Display the needed libraries")); - - // --program-headers, --segments, -l - cl::opt<bool> ProgramHeaders("program-headers", - cl::desc("Display ELF program headers")); - cl::alias ProgramHeadersShort("l", cl::desc("Alias for --program-headers"), - cl::aliasopt(ProgramHeaders), cl::NotHidden); - cl::alias SegmentsAlias("segments", cl::desc("Alias for --program-headers"), - cl::aliasopt(ProgramHeaders)); - - // --string-dump, -p - cl::list<std::string> StringDump( - "string-dump", cl::value_desc("number|name"), - cl::desc("Display the specified section(s) as a list of strings"), - cl::ZeroOrMore); - cl::alias StringDumpShort("p", cl::desc("Alias for --string-dump"), - cl::aliasopt(StringDump), cl::Prefix); - - // --hex-dump, -x - cl::list<std::string> - HexDump("hex-dump", cl::value_desc("number|name"), - cl::desc("Display the specified section(s) as hexadecimal bytes"), - cl::ZeroOrMore); - cl::alias HexDumpShort("x", cl::desc("Alias for --hex-dump"), - cl::aliasopt(HexDump), cl::Prefix); - - // --demangle, -C - cl::opt<bool> Demangle("demangle", - cl::desc("Demangle symbol names in output")); - cl::alias DemangleShort("C", cl::desc("Alias for --demangle"), - cl::aliasopt(Demangle), cl::NotHidden); - - // --hash-table - cl::opt<bool> HashTable("hash-table", - cl::desc("Display ELF hash table")); - - // --gnu-hash-table - cl::opt<bool> GnuHashTable("gnu-hash-table", - cl::desc("Display ELF .gnu.hash section")); - - // --expand-relocs - cl::opt<bool> ExpandRelocs("expand-relocs", - cl::desc("Expand each shown relocation to multiple lines")); - - // --raw-relr - cl::opt<bool> RawRelr("raw-relr", - cl::desc("Do not decode relocations in SHT_RELR section, display raw contents")); - - // --codeview - cl::opt<bool> CodeView("codeview", - cl::desc("Display CodeView debug information")); - - // --codeview-merged-types - cl::opt<bool> - CodeViewMergedTypes("codeview-merged-types", - cl::desc("Display the merged CodeView type stream")); - - // --codeview-ghash - cl::opt<bool> CodeViewEnableGHash( - "codeview-ghash", - cl::desc( - "Enable global hashing for CodeView type stream de-duplication")); - - // --codeview-subsection-bytes - cl::opt<bool> CodeViewSubsectionBytes( - "codeview-subsection-bytes", - cl::desc("Dump raw contents of codeview debug sections and records")); - - // --arch-specific - cl::opt<bool> ArchSpecificInfo("arch-specific", - cl::desc("Displays architecture-specific information, if there is any.")); - cl::alias ArchSpecifcInfoShort("A", cl::desc("Alias for --arch-specific"), - cl::aliasopt(ArchSpecificInfo), cl::NotHidden); - - // --coff-imports - cl::opt<bool> - COFFImports("coff-imports", cl::desc("Display the PE/COFF import table")); - - // --coff-exports - cl::opt<bool> - COFFExports("coff-exports", cl::desc("Display the PE/COFF export table")); - - // --coff-directives - cl::opt<bool> - COFFDirectives("coff-directives", - cl::desc("Display the PE/COFF .drectve section")); - - // --coff-basereloc - cl::opt<bool> - COFFBaseRelocs("coff-basereloc", - cl::desc("Display the PE/COFF .reloc section")); - - // --coff-debug-directory - cl::opt<bool> - COFFDebugDirectory("coff-debug-directory", - cl::desc("Display the PE/COFF debug directory")); - - // --coff-tls-directory - cl::opt<bool> COFFTLSDirectory("coff-tls-directory", - cl::desc("Display the PE/COFF TLS directory")); - - // --coff-resources - cl::opt<bool> COFFResources("coff-resources", - cl::desc("Display the PE/COFF .rsrc section")); - - // --coff-load-config - cl::opt<bool> - COFFLoadConfig("coff-load-config", - cl::desc("Display the PE/COFF load config")); - - // --elf-linker-options - cl::opt<bool> - ELFLinkerOptions("elf-linker-options", - cl::desc("Display the ELF .linker-options section")); - - // --macho-data-in-code - cl::opt<bool> - MachODataInCode("macho-data-in-code", - cl::desc("Display MachO Data in Code command")); - - // --macho-indirect-symbols - cl::opt<bool> - MachOIndirectSymbols("macho-indirect-symbols", - cl::desc("Display MachO indirect symbols")); - - // --macho-linker-options - cl::opt<bool> - MachOLinkerOptions("macho-linker-options", - cl::desc("Display MachO linker options")); - - // --macho-segment - cl::opt<bool> - MachOSegment("macho-segment", - cl::desc("Display MachO Segment command")); - - // --macho-version-min - cl::opt<bool> - MachOVersionMin("macho-version-min", - cl::desc("Display MachO version min command")); - - // --macho-dysymtab - cl::opt<bool> - MachODysymtab("macho-dysymtab", - cl::desc("Display MachO Dysymtab command")); - - // --stackmap - cl::opt<bool> - PrintStackMap("stackmap", - cl::desc("Display contents of stackmap section")); - - // --stack-sizes - cl::opt<bool> - PrintStackSizes("stack-sizes", - cl::desc("Display contents of all stack sizes sections")); - - // --version-info, -V - cl::opt<bool> - VersionInfo("version-info", - cl::desc("Display ELF version sections (if present)")); - cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"), - cl::aliasopt(VersionInfo)); - - // --elf-section-groups, --section-groups, -g - cl::opt<bool> SectionGroups("elf-section-groups", - cl::desc("Display ELF section group contents")); - cl::alias SectionGroupsAlias("section-groups", - cl::desc("Alias for -elf-sections-groups"), - cl::aliasopt(SectionGroups)); - cl::alias SectionGroupsShort("g", cl::desc("Alias for -elf-sections-groups"), - cl::aliasopt(SectionGroups)); - - // --elf-hash-histogram, --histogram, -I - cl::opt<bool> HashHistogram( - "elf-hash-histogram", - cl::desc("Display bucket list histogram for hash sections")); - cl::alias HashHistogramShort("I", cl::desc("Alias for -elf-hash-histogram"), - cl::aliasopt(HashHistogram)); - cl::alias HistogramAlias("histogram", - cl::desc("Alias for --elf-hash-histogram"), - cl::aliasopt(HashHistogram)); - - // --cg-profile - cl::opt<bool> CGProfile("cg-profile", - cl::desc("Display callgraph profile section")); - cl::alias ELFCGProfile("elf-cg-profile", cl::desc("Alias for --cg-profile"), - cl::aliasopt(CGProfile)); - - // -addrsig - cl::opt<bool> Addrsig("addrsig", - cl::desc("Display address-significance table")); - - // -elf-output-style - cl::opt<OutputStyleTy> - Output("elf-output-style", cl::desc("Specify ELF dump style"), - cl::values(clEnumVal(LLVM, "LLVM default style"), - clEnumVal(GNU, "GNU readelf style")), - cl::init(LLVM)); - - cl::extrahelp - HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); +static bool Addrsig; +static bool All; +static bool ArchSpecificInfo; +static bool BBAddrMap; +bool ExpandRelocs; +static bool CGProfile; +bool Demangle; +static bool DependentLibraries; +static bool DynRelocs; +static bool DynamicSymbols; +static bool FileHeaders; +static bool Headers; +static std::vector<std::string> HexDump; +static bool PrintStackMap; +static bool PrintStackSizes; +static bool Relocations; +bool SectionData; +static bool SectionDetails; +static bool SectionHeaders; +bool SectionRelocations; +bool SectionSymbols; +static std::vector<std::string> StringDump; +static bool StringTable; +static bool Symbols; +static bool UnwindInfo; +static cl::boolOrDefault SectionMapping; + +// ELF specific options. +static bool DynamicTable; +static bool ELFLinkerOptions; +static bool GnuHashTable; +static bool HashSymbols; +static bool HashTable; +static bool HashHistogram; +static bool NeededLibraries; +static bool Notes; +static bool ProgramHeaders; +bool RawRelr; +static bool SectionGroups; +static bool VersionInfo; + +// Mach-O specific options. +static bool MachODataInCode; +static bool MachODysymtab; +static bool MachOIndirectSymbols; +static bool MachOLinkerOptions; +static bool MachOSegment; +static bool MachOVersionMin; + +// PE/COFF specific options. +static bool CodeView; +static bool CodeViewEnableGHash; +static bool CodeViewMergedTypes; +bool CodeViewSubsectionBytes; +static bool COFFBaseRelocs; +static bool COFFDebugDirectory; +static bool COFFDirectives; +static bool COFFExports; +static bool COFFImports; +static bool COFFLoadConfig; +static bool COFFResources; +static bool COFFTLSDirectory; + +OutputStyleTy Output = OutputStyleTy::LLVM; +static std::vector<std::string> InputFilenames; } // namespace opts static StringRef ToolName; @@ -419,6 +190,87 @@ void reportWarning(Error Err, StringRef Input) { } // namespace llvm +static void parseOptions(const opt::InputArgList &Args) { + opts::Addrsig = Args.hasArg(OPT_addrsig); + opts::All = Args.hasArg(OPT_all); + opts::ArchSpecificInfo = Args.hasArg(OPT_arch_specific); + opts::BBAddrMap = Args.hasArg(OPT_bb_addr_map); + opts::CGProfile = Args.hasArg(OPT_cg_profile); + opts::Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false); + opts::DependentLibraries = Args.hasArg(OPT_dependent_libraries); + opts::DynRelocs = Args.hasArg(OPT_dyn_relocations); + opts::DynamicSymbols = Args.hasArg(OPT_dyn_syms); + opts::ExpandRelocs = Args.hasArg(OPT_expand_relocs); + opts::FileHeaders = Args.hasArg(OPT_file_header); + opts::Headers = Args.hasArg(OPT_headers); + opts::HexDump = Args.getAllArgValues(OPT_hex_dump_EQ); + opts::Relocations = Args.hasArg(OPT_relocs); + opts::SectionData = Args.hasArg(OPT_section_data); + opts::SectionDetails = Args.hasArg(OPT_section_details); + opts::SectionHeaders = Args.hasArg(OPT_section_headers); + opts::SectionRelocations = Args.hasArg(OPT_section_relocations); + opts::SectionSymbols = Args.hasArg(OPT_section_symbols); + if (Args.hasArg(OPT_section_mapping)) + opts::SectionMapping = cl::BOU_TRUE; + else if (Args.hasArg(OPT_section_mapping_EQ_false)) + opts::SectionMapping = cl::BOU_FALSE; + else + opts::SectionMapping = cl::BOU_UNSET; + opts::PrintStackSizes = Args.hasArg(OPT_stack_sizes); + opts::PrintStackMap = Args.hasArg(OPT_stackmap); + opts::StringDump = Args.getAllArgValues(OPT_string_dump_EQ); + opts::StringTable = Args.hasArg(OPT_string_table); + opts::Symbols = Args.hasArg(OPT_symbols); + opts::UnwindInfo = Args.hasArg(OPT_unwind); + + // ELF specific options. + opts::DynamicTable = Args.hasArg(OPT_dynamic_table); + opts::ELFLinkerOptions = Args.hasArg(OPT_elf_linker_options); + if (Arg *A = Args.getLastArg(OPT_elf_output_style_EQ)) { + StringRef V(A->getValue()); + if (V == "LLVM") + opts::Output = opts::OutputStyleTy::LLVM; + else if (V == "GNU") + opts::Output = opts::OutputStyleTy::GNU; + else + error("--elf-output-style value should be either 'LLVM' or 'GNU'"); + } + opts::GnuHashTable = Args.hasArg(OPT_gnu_hash_table); + opts::HashSymbols = Args.hasArg(OPT_hash_symbols); + opts::HashTable = Args.hasArg(OPT_hash_table); + opts::HashHistogram = Args.hasArg(OPT_histogram); + opts::NeededLibraries = Args.hasArg(OPT_needed_libs); + opts::Notes = Args.hasArg(OPT_notes); + opts::ProgramHeaders = Args.hasArg(OPT_program_headers); + opts::RawRelr = Args.hasArg(OPT_raw_relr); + opts::SectionGroups = Args.hasArg(OPT_section_groups); + opts::VersionInfo = Args.hasArg(OPT_version_info); + + // Mach-O specific options. + opts::MachODataInCode = Args.hasArg(OPT_macho_data_in_code); + opts::MachODysymtab = Args.hasArg(OPT_macho_dysymtab); + opts::MachOIndirectSymbols = Args.hasArg(OPT_macho_indirect_symbols); + opts::MachOLinkerOptions = Args.hasArg(OPT_macho_linker_options); + opts::MachOSegment = Args.hasArg(OPT_macho_segment); + opts::MachOVersionMin = Args.hasArg(OPT_macho_version_min); + + // PE/COFF specific options. + opts::CodeView = Args.hasArg(OPT_codeview); + opts::CodeViewEnableGHash = Args.hasArg(OPT_codeview_ghash); + opts::CodeViewMergedTypes = Args.hasArg(OPT_codeview_merged_types); + opts::CodeViewSubsectionBytes = Args.hasArg(OPT_codeview_subsection_bytes); + opts::COFFBaseRelocs = Args.hasArg(OPT_coff_basereloc); + opts::COFFDebugDirectory = Args.hasArg(OPT_coff_debug_directory); + opts::COFFDirectives = Args.hasArg(OPT_coff_directives); + opts::COFFExports = Args.hasArg(OPT_coff_exports); + opts::COFFImports = Args.hasArg(OPT_coff_imports); + opts::COFFLoadConfig = Args.hasArg(OPT_coff_load_config); + opts::COFFResources = Args.hasArg(OPT_coff_resources); + opts::COFFTLSDirectory = Args.hasArg(OPT_coff_tls_directory); + + opts::InputFilenames = Args.getAllArgValues(OPT_INPUT); +} + namespace { struct ReadObjTypeTableBuilder { ReadObjTypeTableBuilder() @@ -529,6 +381,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, Dumper->printGnuHashTable(); if (opts::VersionInfo) Dumper->printVersionInfo(); + if (opts::StringTable) + Dumper->printStringTable(); if (Obj.isELF()) { if (opts::DependentLibraries) Dumper->printDependentLibs(); @@ -542,6 +396,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, Dumper->printHashHistograms(); if (opts::CGProfile) Dumper->printCGProfile(); + if (opts::BBAddrMap) + Dumper->printBBAddrMaps(); if (opts::Addrsig) Dumper->printAddrsig(); if (opts::Notes) @@ -646,93 +502,73 @@ static void dumpWindowsResourceFile(WindowsResource *WinRes, /// Opens \a File and dumps it. static void dumpInput(StringRef File, ScopedPrinter &Writer) { - // Attempt to open the binary. - Expected<OwningBinary<Binary>> BinaryOrErr = - createBinary(File, /*Context=*/nullptr, /*InitContent=*/false); + ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = + MemoryBuffer::getFileOrSTDIN(File, /*IsText=*/false, + /*RequiresNullTerminator=*/false); + if (std::error_code EC = FileOrErr.getError()) + return reportError(errorCodeToError(EC), File); + + std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get(); + file_magic Type = identify_magic(Buffer->getBuffer()); + if (Type == file_magic::bitcode) { + reportWarning(createStringError(errc::invalid_argument, + "bitcode files are not supported"), + File); + return; + } + + Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary( + Buffer->getMemBufferRef(), /*Context=*/nullptr, /*InitContent=*/false); if (!BinaryOrErr) reportError(BinaryOrErr.takeError(), File); - Binary &Binary = *BinaryOrErr.get().getBinary(); - if (Archive *Arc = dyn_cast<Archive>(&Binary)) + std::unique_ptr<Binary> Bin = std::move(*BinaryOrErr); + if (Archive *Arc = dyn_cast<Archive>(Bin.get())) dumpArchive(Arc, Writer); else if (MachOUniversalBinary *UBinary = - dyn_cast<MachOUniversalBinary>(&Binary)) + dyn_cast<MachOUniversalBinary>(Bin.get())) dumpMachOUniversalBinary(UBinary, Writer); - else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary)) + else if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin.get())) dumpObject(*Obj, Writer); - else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(&Binary)) + else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(Bin.get())) dumpCOFFImportFile(Import, Writer); - else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(&Binary)) + else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(Bin.get())) dumpWindowsResourceFile(WinRes, Writer); else llvm_unreachable("unrecognized file type"); - CVTypes.Binaries.push_back(std::move(*BinaryOrErr)); -} - -/// Registers aliases that should only be allowed by readobj. -static void registerReadobjAliases() { - // -s has meant --sections for a very long time in llvm-readobj despite - // meaning --symbols in readelf. - static cl::alias SectionsShort("s", cl::desc("Alias for --section-headers"), - cl::aliasopt(opts::SectionHeaders), - cl::NotHidden); - - // llvm-readelf reserves it for --section-details. - static cl::alias SymbolsShort("t", cl::desc("Alias for --symbols"), - cl::aliasopt(opts::Symbols), cl::NotHidden); - - // The following two-letter aliases are only provided for readobj, as readelf - // allows single-letter args to be grouped together. - static cl::alias SectionRelocationsShort( - "sr", cl::desc("Alias for --section-relocations"), - cl::aliasopt(opts::SectionRelocations)); - static cl::alias SectionDataShort("sd", cl::desc("Alias for --section-data"), - cl::aliasopt(opts::SectionData)); - static cl::alias SectionSymbolsShort("st", - cl::desc("Alias for --section-symbols"), - cl::aliasopt(opts::SectionSymbols)); - static cl::alias DynamicSymbolsShort("dt", - cl::desc("Alias for --dyn-symbols"), - cl::aliasopt(opts::DynamicSymbols)); -} - -/// Registers aliases that should only be allowed by readelf. -static void registerReadelfAliases() { - // -s is here because for readobj it means --sections. - static cl::alias SymbolsShort("s", cl::desc("Alias for --symbols"), - cl::aliasopt(opts::Symbols), cl::NotHidden, - cl::Grouping); - - // -t is here because for readobj it is an alias for --symbols. - static cl::alias SectionDetailsShort( - "t", cl::desc("Alias for --section-details"), - cl::aliasopt(opts::SectionDetails), cl::NotHidden); - - // Allow all single letter flags to be grouped together. - for (auto &OptEntry : cl::getRegisteredOptions()) { - StringRef ArgName = OptEntry.getKey(); - cl::Option *Option = OptEntry.getValue(); - if (ArgName.size() == 1) - apply(Option, cl::Grouping); - } + CVTypes.Binaries.push_back( + OwningBinary<Binary>(std::move(Bin), std::move(Buffer))); } -int main(int argc, const char *argv[]) { +int main(int argc, char *argv[]) { InitLLVM X(argc, argv); + BumpPtrAllocator A; + StringSaver Saver(A); + ReadobjOptTable Tbl; ToolName = argv[0]; - - // Register the target printer for --version. - cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); - - if (sys::path::stem(argv[0]).contains("readelf")) { - opts::Output = opts::GNU; - registerReadelfAliases(); - } else { - registerReadobjAliases(); + opt::InputArgList Args = + Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { + error(Msg); + exit(1); + }); + if (Args.hasArg(OPT_help)) { + Tbl.printHelp( + outs(), + (Twine(ToolName) + " [options] <input object files>").str().c_str(), + "LLVM Object Reader"); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + return 0; + } + if (Args.hasArg(OPT_version)) { + cl::PrintVersionMessage(); + return 0; } - cl::ParseCommandLineOptions(argc, argv, "LLVM Object Reader\n"); + if (sys::path::stem(argv[0]).contains("readelf")) + opts::Output = opts::GNU; + parseOptions(Args); // Default to print error if no filename is specified. if (opts::InputFilenames.empty()) { diff --git a/llvm/tools/llvm-readobj/llvm-readobj.h b/llvm/tools/llvm-readobj/llvm-readobj.h index d9813f5dea62..43d19b4d3f5c 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.h +++ b/llvm/tools/llvm-readobj/llvm-readobj.h @@ -32,15 +32,15 @@ namespace llvm { } // namespace llvm namespace opts { - extern llvm::cl::opt<bool> SectionRelocations; - extern llvm::cl::opt<bool> SectionSymbols; - extern llvm::cl::opt<bool> SectionData; - extern llvm::cl::opt<bool> ExpandRelocs; - extern llvm::cl::opt<bool> RawRelr; - extern llvm::cl::opt<bool> CodeViewSubsectionBytes; - extern llvm::cl::opt<bool> Demangle; - enum OutputStyleTy { LLVM, GNU }; - extern llvm::cl::opt<OutputStyleTy> Output; +extern bool SectionRelocations; +extern bool SectionSymbols; +extern bool SectionData; +extern bool ExpandRelocs; +extern bool RawRelr; +extern bool CodeViewSubsectionBytes; +extern bool Demangle; +enum OutputStyleTy { LLVM, GNU }; +extern OutputStyleTy Output; } // namespace opts #define LLVM_READOBJ_ENUM_ENT(ns, enum) \ diff --git a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp index 919943129311..f02d8981b30e 100644 --- a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -27,6 +27,7 @@ #include "llvm/Object/SymbolSize.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MSVCErrorWorkarounds.h" #include "llvm/Support/Memory.h" @@ -43,9 +44,11 @@ using namespace llvm; using namespace llvm::object; -static cl::list<std::string> -InputFileList(cl::Positional, cl::ZeroOrMore, - cl::desc("<input files>")); +static cl::OptionCategory RTDyldCategory("RTDyld Options"); + +static cl::list<std::string> InputFileList(cl::Positional, cl::ZeroOrMore, + cl::desc("<input files>"), + cl::cat(RTDyldCategory)); enum ActionType { AC_Execute, @@ -55,94 +58,93 @@ enum ActionType { AC_Verify }; -static cl::opt<ActionType> -Action(cl::desc("Action to perform:"), - cl::init(AC_Execute), - cl::values(clEnumValN(AC_Execute, "execute", - "Load, link, and execute the inputs."), - clEnumValN(AC_PrintLineInfo, "printline", - "Load, link, and print line information for each function."), - clEnumValN(AC_PrintDebugLineInfo, "printdebugline", - "Load, link, and print line information for each function using the debug object"), - clEnumValN(AC_PrintObjectLineInfo, "printobjline", - "Like -printlineinfo but does not load the object first"), - clEnumValN(AC_Verify, "verify", - "Load, link and verify the resulting memory image."))); +static cl::opt<ActionType> Action( + cl::desc("Action to perform:"), cl::init(AC_Execute), + cl::values( + clEnumValN(AC_Execute, "execute", + "Load, link, and execute the inputs."), + clEnumValN(AC_PrintLineInfo, "printline", + "Load, link, and print line information for each function."), + clEnumValN(AC_PrintDebugLineInfo, "printdebugline", + "Load, link, and print line information for each function " + "using the debug object"), + clEnumValN(AC_PrintObjectLineInfo, "printobjline", + "Like -printlineinfo but does not load the object first"), + clEnumValN(AC_Verify, "verify", + "Load, link and verify the resulting memory image.")), + cl::cat(RTDyldCategory)); static cl::opt<std::string> -EntryPoint("entry", - cl::desc("Function to call as entry point."), - cl::init("_main")); + EntryPoint("entry", cl::desc("Function to call as entry point."), + cl::init("_main"), cl::cat(RTDyldCategory)); -static cl::list<std::string> -Dylibs("dylib", - cl::desc("Add library."), - cl::ZeroOrMore); +static cl::list<std::string> Dylibs("dylib", cl::desc("Add library."), + cl::ZeroOrMore, cl::cat(RTDyldCategory)); static cl::list<std::string> InputArgv("args", cl::Positional, cl::desc("<program arguments>..."), - cl::ZeroOrMore, cl::PositionalEatsArgs); + cl::ZeroOrMore, cl::PositionalEatsArgs, + cl::cat(RTDyldCategory)); static cl::opt<std::string> -TripleName("triple", cl::desc("Target triple for disassembler")); + TripleName("triple", cl::desc("Target triple for disassembler"), + cl::cat(RTDyldCategory)); static cl::opt<std::string> -MCPU("mcpu", - cl::desc("Target a specific cpu type (-mcpu=help for details)"), - cl::value_desc("cpu-name"), - cl::init("")); + MCPU("mcpu", + cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), cl::init(""), cl::cat(RTDyldCategory)); static cl::list<std::string> -CheckFiles("check", - cl::desc("File containing RuntimeDyld verifier checks."), - cl::ZeroOrMore); + CheckFiles("check", + cl::desc("File containing RuntimeDyld verifier checks."), + cl::ZeroOrMore, cl::cat(RTDyldCategory)); static cl::opt<uint64_t> PreallocMemory("preallocate", cl::desc("Allocate memory upfront rather than on-demand"), - cl::init(0)); + cl::init(0), cl::cat(RTDyldCategory)); static cl::opt<uint64_t> TargetAddrStart( "target-addr-start", cl::desc("For -verify only: start of phony target address " "range."), cl::init(4096), // Start at "page 1" - no allocating at "null". - cl::Hidden); + cl::Hidden, cl::cat(RTDyldCategory)); static cl::opt<uint64_t> TargetAddrEnd( "target-addr-end", cl::desc("For -verify only: end of phony target address range."), - cl::init(~0ULL), cl::Hidden); + cl::init(~0ULL), cl::Hidden, cl::cat(RTDyldCategory)); static cl::opt<uint64_t> TargetSectionSep( "target-section-sep", cl::desc("For -verify only: Separation between sections in " "phony target address space."), - cl::init(0), cl::Hidden); - -static cl::list<std::string> -SpecificSectionMappings("map-section", - cl::desc("For -verify only: Map a section to a " - "specific address."), - cl::ZeroOrMore, - cl::Hidden); + cl::init(0), cl::Hidden, cl::cat(RTDyldCategory)); static cl::list<std::string> -DummySymbolMappings("dummy-extern", - cl::desc("For -verify only: Inject a symbol into the extern " - "symbol table."), - cl::ZeroOrMore, - cl::Hidden); - -static cl::opt<bool> -PrintAllocationRequests("print-alloc-requests", - cl::desc("Print allocation requests made to the memory " - "manager by RuntimeDyld"), - cl::Hidden); + SpecificSectionMappings("map-section", + cl::desc("For -verify only: Map a section to a " + "specific address."), + cl::ZeroOrMore, cl::Hidden, + cl::cat(RTDyldCategory)); + +static cl::list<std::string> DummySymbolMappings( + "dummy-extern", + cl::desc("For -verify only: Inject a symbol into the extern " + "symbol table."), + cl::ZeroOrMore, cl::Hidden, cl::cat(RTDyldCategory)); + +static cl::opt<bool> PrintAllocationRequests( + "print-alloc-requests", + cl::desc("Print allocation requests made to the memory " + "manager by RuntimeDyld"), + cl::Hidden, cl::cat(RTDyldCategory)); static cl::opt<bool> ShowTimes("show-times", cl::desc("Show times for llvm-rtdyld phases"), - cl::init(false)); + cl::init(false), cl::cat(RTDyldCategory)); ExitOnError ExitOnErr; @@ -756,7 +758,7 @@ static int linkAndVerify() { if (!MAI) ErrorAndExit("Unable to create target asm info!"); - MCContext Ctx(MAI.get(), MRI.get(), nullptr); + MCContext Ctx(Triple(TripleName), MAI.get(), MRI.get(), STI.get()); std::unique_ptr<MCDisassembler> Disassembler( TheTarget->createMCDisassembler(*STI, Ctx)); @@ -840,7 +842,7 @@ static int linkAndVerify() { char *CSymAddr = static_cast<char *>(SymAddr); StringRef SecContent = Dyld.getSectionContent(SectionID); uint64_t SymSize = SecContent.size() - (CSymAddr - SecContent.data()); - SymInfo.setContent(StringRef(CSymAddr, SymSize)); + SymInfo.setContent(ArrayRef<char>(CSymAddr, SymSize)); } } return SymInfo; @@ -867,7 +869,8 @@ static int linkAndVerify() { return SectionID.takeError(); RuntimeDyldChecker::MemoryRegionInfo SecInfo; SecInfo.setTargetAddress(Dyld.getSectionLoadAddress(*SectionID)); - SecInfo.setContent(Dyld.getSectionContent(*SectionID)); + StringRef SecContent = Dyld.getSectionContent(*SectionID); + SecInfo.setContent(ArrayRef<char>(SecContent.data(), SecContent.size())); return SecInfo; }; @@ -886,8 +889,10 @@ static int linkAndVerify() { RuntimeDyldChecker::MemoryRegionInfo StubMemInfo; StubMemInfo.setTargetAddress(Dyld.getSectionLoadAddress(SI.SectionID) + SI.Offset); + StringRef SecContent = + Dyld.getSectionContent(SI.SectionID).substr(SI.Offset); StubMemInfo.setContent( - Dyld.getSectionContent(SI.SectionID).substr(SI.Offset)); + ArrayRef<char>(SecContent.data(), SecContent.size())); return StubMemInfo; }; @@ -962,6 +967,7 @@ int main(int argc, char **argv) { llvm::InitializeAllTargetMCs(); llvm::InitializeAllDisassemblers(); + cl::HideUnrelatedOptions({&RTDyldCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm MC-JIT tool\n"); ExitOnErr.setBanner(std::string(argv[0]) + ": "); diff --git a/llvm/tools/llvm-sim/llvm-sim.cpp b/llvm/tools/llvm-sim/llvm-sim.cpp new file mode 100644 index 000000000000..26e370ff30f1 --- /dev/null +++ b/llvm/tools/llvm-sim/llvm-sim.cpp @@ -0,0 +1,149 @@ +//===-- llvm-sim.cpp - Find similar sections of programs -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This program finds similar sections of a Module, and exports them as a JSON +// file. +// +// To find similarities contained across multiple modules, please use llvm-link +// first to merge the modules. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/IRSimilarityIdentifier.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/ToolOutputFile.h" + +using namespace llvm; +using namespace IRSimilarity; + +static cl::opt<std::string> OutputFilename("o", cl::desc("Output Filename"), + cl::init("-"), + cl::value_desc("filename")); + +static cl::opt<std::string> InputSourceFile(cl::Positional, + cl::desc("<Source file>"), + cl::init("-"), + cl::value_desc("filename")); + +/// Retrieve the unique number \p I was mapped to in parseBitcodeFile. +/// +/// \param I - The Instruction to find the instruction number for. +/// \param LLVMInstNum - The mapping of Instructions to their location in the +/// module represented by an unsigned integer. +/// \returns The instruction number for \p I if it exists. +Optional<unsigned> +getPositionInModule(const Instruction *I, + const DenseMap<Instruction *, unsigned> &LLVMInstNum) { + assert(I && "Instruction is nullptr!"); + DenseMap<Instruction *, unsigned>::const_iterator It = LLVMInstNum.find(I); + if (It == LLVMInstNum.end()) + return None; + return It->second; +} + +/// Exports the given SimilarityGroups to a JSON file at \p FilePath. +/// +/// \param FilePath - The path to the output location. +/// \param SimSections - The similarity groups to process. +/// \param LLVMInstNum - The mapping of Instructions to their location in the +/// module represented by an unsigned integer. +/// \returns A nonzero error code if there was a failure creating the file. +std::error_code +exportToFile(const StringRef FilePath, + const SimilarityGroupList &SimSections, + const DenseMap<Instruction *, unsigned> &LLVMInstNum) { + std::error_code EC; + std::unique_ptr<ToolOutputFile> Out( + new ToolOutputFile(FilePath, EC, sys::fs::OF_None)); + if (EC) + return EC; + + json::OStream J(Out->os(), 1); + J.objectBegin(); + + unsigned SimOption = 1; + // Process each list of SimilarityGroups organized by the Module. + for (const SimilarityGroup &G : SimSections) { + std::string SimOptionStr = std::to_string(SimOption); + J.attributeBegin(SimOptionStr); + J.arrayBegin(); + // For each file there is a list of the range where the similarity + // exists. + for (const IRSimilarityCandidate &C : G) { + Optional<unsigned> Start = + getPositionInModule((*C.front()).Inst, LLVMInstNum); + Optional<unsigned> End = + getPositionInModule((*C.back()).Inst, LLVMInstNum); + + assert(Start.hasValue() && + "Could not find instruction number for first instruction"); + assert(End.hasValue() && + "Could not find instruction number for last instruction"); + + J.object([&] { + J.attribute("start", Start.getValue()); + J.attribute("end", End.getValue()); + }); + } + J.arrayEnd(); + J.attributeEnd(); + SimOption++; + } + J.objectEnd(); + + Out->keep(); + + return EC; +} + +int main(int argc, const char *argv[]) { + InitLLVM X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv, "LLVM IR Similarity Visualizer\n"); + + LLVMContext CurrContext; + SMDiagnostic Err; + std::unique_ptr<Module> ModuleToAnalyze = + parseIRFile(InputSourceFile, Err, CurrContext); + + if (!ModuleToAnalyze) { + Err.print(argv[0], errs()); + return 1; + } + + // Mapping from an Instruction pointer to its occurrence in a sequential + // list of all the Instructions in a Module. + DenseMap<Instruction *, unsigned> LLVMInstNum; + + // We give each instruction a number, which gives us a start and end value + // for the beginning and end of each IRSimilarityCandidate. + unsigned InstructionNumber = 1; + for (Function &F : *ModuleToAnalyze) + for (BasicBlock &BB : F) + for (Instruction &I : BB.instructionsWithoutDebug()) + LLVMInstNum[&I]= InstructionNumber++; + + // The similarity identifier we will use to find the similar sections. + IRSimilarityIdentifier SimIdent; + SimilarityGroupList SimilaritySections = + SimIdent.findSimilarity(*ModuleToAnalyze); + + std::error_code E = + exportToFile(OutputFilename, SimilaritySections, LLVMInstNum); + if (E) { + errs() << argv[0] << ": " << E.message() << '\n'; + return 2; + } + + return 0; +} diff --git a/llvm/tools/llvm-size/Opts.td b/llvm/tools/llvm-size/Opts.td new file mode 100644 index 000000000000..edae43f1abd2 --- /dev/null +++ b/llvm/tools/llvm-size/Opts.td @@ -0,0 +1,32 @@ +include "llvm/Option/OptParser.td" + +class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>; +class FF<string name, string help> : Flag<["--"], name>, HelpText<help>; + +multiclass Eq<string name, string help> { + def NAME #_EQ : Joined<["--"], name #"=">, + HelpText<help>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; +} + +def common : FF<"common", "Print common symbols in the ELF file. When using Berkeley format, this is added to bss">; +defm format : Eq<"format", "Specify output format">; +def help : FF<"help", "Display this help">; +defm radix : Eq<"radix", "Print size in radix">; +def totals : FF<"totals", "Print totals of all objects - Berkeley format only">; +def version : FF<"version", "Display the version">; + +// Mach-O specific options. +def grp_mach_o : OptionGroup<"kind">, HelpText<"OPTIONS (Mach-O specific)">; +def arch_EQ : Joined<["--"], "arch=">, HelpText<"architecture(s) from a Mach-O file to dump">, Group<grp_mach_o>; +def : Separate<["--", "-"], "arch">, Alias<arch_EQ>; +def l : F<"l", "When format is darwin, use long format to include addresses and offsets">, Group<grp_mach_o>; + +def : F<"A", "Alias for --format">, Alias<format_EQ>, AliasArgs<["sysv"]>; +def : F<"B", "Alias for --format">, Alias<format_EQ>, AliasArgs<["berkeley"]>; +def : F<"d", "Alias for --radix=10">, Alias<radix_EQ>, AliasArgs<["10"]>; +def : F<"h", "Alias for --help">, Alias<help>; +def : F<"m", "Alias for --format">, Alias<format_EQ>, AliasArgs<["darwin"]>; +def : F<"o", "Alias for --radix=8">, Alias<radix_EQ>, AliasArgs<["8"]>; +def : F<"t", "Alias for --totals">, Alias<totals>; +def : F<"x", "Alias for --radix=16">, Alias<radix_EQ>, AliasArgs<["16"]>; diff --git a/llvm/tools/llvm-size/llvm-size.cpp b/llvm/tools/llvm-size/llvm-size.cpp index 4f98a4c0ec10..ec9a4cde56b6 100644 --- a/llvm/tools/llvm-size/llvm-size.cpp +++ b/llvm/tools/llvm-size/llvm-size.cpp @@ -18,6 +18,9 @@ #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -33,23 +36,56 @@ using namespace llvm; using namespace object; -cl::OptionCategory SizeCat("llvm-size Options"); +namespace { +using namespace llvm::opt; // for HelpHidden in Opts.inc +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class SizeOptTable : public opt::OptTable { +public: + SizeOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } +}; enum OutputFormatTy { berkeley, sysv, darwin }; -static cl::opt<OutputFormatTy> - OutputFormat("format", cl::desc("Specify output format"), - cl::values(clEnumVal(sysv, "System V format"), - clEnumVal(berkeley, "Berkeley format"), - clEnumVal(darwin, "Darwin -m format")), - cl::init(berkeley), cl::cat(SizeCat)); - -static cl::opt<OutputFormatTy> - OutputFormatShort(cl::desc("Specify output format"), - cl::values(clEnumValN(sysv, "A", "System V format"), - clEnumValN(berkeley, "B", "Berkeley format"), - clEnumValN(darwin, "m", "Darwin -m format")), - cl::init(berkeley), cl::cat(SizeCat)); +enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 }; +} // namespace + +static bool ArchAll = false; +static std::vector<StringRef> ArchFlags; +static bool ELFCommons; +static OutputFormatTy OutputFormat; +static bool DarwinLongFormat; +static RadixTy Radix; +static bool TotalSizes; + +static std::vector<std::string> InputFilenames; + +static std::string ToolName; +// States +static bool HadError = false; static bool BerkeleyHeaderPrinted = false; static bool MoreThanOneFile = false; static uint64_t TotalObjectText = 0; @@ -57,59 +93,13 @@ static uint64_t TotalObjectData = 0; static uint64_t TotalObjectBss = 0; static uint64_t TotalObjectTotal = 0; -cl::opt<bool> - DarwinLongFormat("l", - cl::desc("When format is darwin, use long format " - "to include addresses and offsets."), - cl::cat(SizeCat)); - -cl::opt<bool> - ELFCommons("common", - cl::desc("Print common symbols in the ELF file. When using " - "Berkeley format, this is added to bss."), - cl::init(false), cl::cat(SizeCat)); - -static cl::list<std::string> - ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), - cl::ZeroOrMore, cl::cat(SizeCat)); -static bool ArchAll = false; - -enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 }; -static cl::opt<RadixTy> Radix( - "radix", cl::desc("Print size in radix"), cl::init(decimal), - cl::values(clEnumValN(octal, "8", "Print size in octal"), - clEnumValN(decimal, "10", "Print size in decimal"), - clEnumValN(hexadecimal, "16", "Print size in hexadecimal")), - cl::cat(SizeCat)); - -static cl::opt<RadixTy> RadixShort( - cl::desc("Print size in radix:"), - cl::values(clEnumValN(octal, "o", "Print size in octal"), - clEnumValN(decimal, "d", "Print size in decimal"), - clEnumValN(hexadecimal, "x", "Print size in hexadecimal")), - cl::init(decimal), cl::cat(SizeCat)); - -static cl::opt<bool> - TotalSizes("totals", - cl::desc("Print totals of all objects - Berkeley format only"), - cl::init(false), cl::cat(SizeCat)); - -static cl::alias TotalSizesShort("t", cl::desc("Short for --totals"), - cl::aliasopt(TotalSizes)); - -static cl::list<std::string> - InputFilenames(cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore); - -static cl::extrahelp - HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); - -static bool HadError = false; - -static std::string ToolName; - -static void error(const Twine &Message, StringRef File) { +static void error(const Twine &Message, StringRef File = "") { HadError = true; - WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n"; + if (File.empty()) + WithColor::error(errs(), ToolName) << Message << '\n'; + else + WithColor::error(errs(), ToolName) + << "'" << File << "': " << Message << '\n'; } // This version of error() prints the archive name and member name, for example: @@ -874,27 +864,66 @@ static void printBerkeleyTotals() { int main(int argc, char **argv) { InitLLVM X(argc, argv); - cl::HideUnrelatedOptions(SizeCat); - cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n"); - + BumpPtrAllocator A; + StringSaver Saver(A); + SizeOptTable Tbl; ToolName = argv[0]; - if (OutputFormatShort.getNumOccurrences()) - OutputFormat = static_cast<OutputFormatTy>(OutputFormatShort); - if (RadixShort.getNumOccurrences()) - Radix = RadixShort.getValue(); - - for (StringRef Arch : ArchFlags) { - if (Arch == "all") { - ArchAll = true; - } else { - if (!MachOObjectFile::isValidArch(Arch)) { + opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, + [&](StringRef Msg) { error(Msg); }); + if (Args.hasArg(OPT_help)) { + Tbl.printHelp( + outs(), + (Twine(ToolName) + " [options] <input object files>").str().c_str(), + "LLVM object size dumper"); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + return 0; + } + if (Args.hasArg(OPT_version)) { + outs() << ToolName << '\n'; + cl::PrintVersionMessage(); + return 0; + } + + ELFCommons = Args.hasArg(OPT_common); + DarwinLongFormat = Args.hasArg(OPT_l); + TotalSizes = Args.hasArg(OPT_totals); + StringRef V = Args.getLastArgValue(OPT_format_EQ, "berkeley"); + if (V == "berkeley") + OutputFormat = berkeley; + else if (V == "darwin") + OutputFormat = darwin; + else if (V == "sysv") + OutputFormat = sysv; + else + error("--format value should be one of: 'berkeley', 'darwin', 'sysv'"); + V = Args.getLastArgValue(OPT_radix_EQ, "10"); + if (V == "8") + Radix = RadixTy::octal; + else if (V == "10") + Radix = RadixTy::decimal; + else if (V == "16") + Radix = RadixTy::hexadecimal; + else + error("--radix value should be one of: 8, 10, 16 "); + + for (const auto *A : Args.filtered(OPT_arch_EQ)) { + SmallVector<StringRef, 2> Values; + llvm::SplitString(A->getValue(), Values, ","); + for (StringRef V : Values) { + if (V == "all") + ArchAll = true; + else if (MachOObjectFile::isValidArch(V)) + ArchFlags.push_back(V); + else { outs() << ToolName << ": for the -arch option: Unknown architecture " - << "named '" << Arch << "'"; + << "named '" << V << "'"; return 1; } } } + InputFilenames = Args.getAllArgValues(OPT_INPUT); if (InputFilenames.empty()) InputFilenames.push_back("a.out"); diff --git a/llvm/tools/llvm-stress/llvm-stress.cpp b/llvm/tools/llvm-stress/llvm-stress.cpp index 538240d65738..ece322999107 100644 --- a/llvm/tools/llvm-stress/llvm-stress.cpp +++ b/llvm/tools/llvm-stress/llvm-stress.cpp @@ -52,16 +52,20 @@ namespace llvm { -static cl::opt<unsigned> SeedCL("seed", - cl::desc("Seed used for randomness"), cl::init(0)); +static cl::OptionCategory StressCategory("Stress Options"); -static cl::opt<unsigned> SizeCL("size", - cl::desc("The estimated size of the generated function (# of instrs)"), - cl::init(100)); +static cl::opt<unsigned> SeedCL("seed", cl::desc("Seed used for randomness"), + cl::init(0), cl::cat(StressCategory)); -static cl::opt<std::string> -OutputFilename("o", cl::desc("Override output filename"), - cl::value_desc("filename")); +static cl::opt<unsigned> SizeCL( + "size", + cl::desc("The estimated size of the generated function (# of instrs)"), + cl::init(100), cl::cat(StressCategory)); + +static cl::opt<std::string> OutputFilename("o", + cl::desc("Override output filename"), + cl::value_desc("filename"), + cl::cat(StressCategory)); static LLVMContext Context; @@ -632,7 +636,7 @@ struct SelectModifier: public Modifier { // If the value type is a vector, and we allow vector select, then in 50% // of the cases generate a vector select. - if (isa<FixedVectorType>(Val0->getType()) && (getRandom() % 1)) { + if (isa<FixedVectorType>(Val0->getType()) && (getRandom() & 1)) { unsigned NumElem = cast<FixedVectorType>(Val0->getType())->getNumElements(); CondTy = FixedVectorType::get(CondTy, NumElem); @@ -718,7 +722,7 @@ static void IntroduceControlFlow(Function *F, Random &R) { BoolInst.push_back(&Instr); } - std::shuffle(BoolInst.begin(), BoolInst.end(), R); + llvm::shuffle(BoolInst.begin(), BoolInst.end(), R); for (auto *Instr : BoolInst) { BasicBlock *Curr = Instr->getParent(); @@ -738,6 +742,7 @@ int main(int argc, char **argv) { using namespace llvm; InitLLVM X(argc, argv); + cl::HideUnrelatedOptions({&StressCategory, &getColorCategory()}); cl::ParseCommandLineOptions(argc, argv, "llvm codegen stress-tester\n"); auto M = std::make_unique<Module>("/tmp/autogen.bc", Context); diff --git a/llvm/tools/llvm-strings/Opts.td b/llvm/tools/llvm-strings/Opts.td new file mode 100644 index 000000000000..2ad77fae6c15 --- /dev/null +++ b/llvm/tools/llvm-strings/Opts.td @@ -0,0 +1,23 @@ +include "llvm/Option/OptParser.td" + +class F<string letter, string help> : Flag<["-"], letter>, HelpText<help>; +class FF<string name, string help> : Flag<["--"], name>, HelpText<help>; + +multiclass Eq<string name, string help> { + def NAME #_EQ : Joined<["--"], name #"=">, + HelpText<help>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; +} + +def all : FF<"all", "Silently ignored. Present for GNU strings compatibility">; +defm bytes : Eq<"bytes", "Print sequences of the specified length">; +def help : FF<"help", "Display this help">; +def print_file_name : Flag<["--"], "print-file-name">, HelpText<"Print the name of the file before each string">; +defm radix : Eq<"radix", "Print the offset within the file with the specified radix: o (octal), d (decimal), x (hexadecimal)">, MetaVarName<"<radix>">; +def version : FF<"version", "Display the version">; + +def : F<"a", "Alias for --all">, Alias<all>; +def : F<"f", "Alias for --print-file-name">, Alias<print_file_name>; +def : F<"h", "Alias for --help">, Alias<help>; +def : JoinedOrSeparate<["-"], "n">, Alias<bytes_EQ>, HelpText<"Alias for --bytes">; +def : JoinedOrSeparate<["-"], "t">, Alias<radix_EQ>, HelpText<"Alias for --radix">, MetaVarName<"<radix>">; diff --git a/llvm/tools/llvm-strings/llvm-strings.cpp b/llvm/tools/llvm-strings/llvm-strings.cpp index 51313d73401e..0b068749917b 100644 --- a/llvm/tools/llvm-strings/llvm-strings.cpp +++ b/llvm/tools/llvm-strings/llvm-strings.cpp @@ -11,51 +11,81 @@ // //===----------------------------------------------------------------------===// +#include "Opts.inc" #include "llvm/Object/Binary.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Program.h" +#include "llvm/Support/WithColor.h" #include <cctype> #include <string> using namespace llvm; using namespace llvm::object; +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class StringsOptTable : public opt::OptTable { +public: + StringsOptTable() : OptTable(InfoTable) { setGroupedShortOptions(true); } +}; +} // namespace + +const char ToolName[] = "llvm-strings"; + static cl::list<std::string> InputFileNames(cl::Positional, cl::desc("<input object files>"), cl::ZeroOrMore); -static cl::opt<bool> - PrintFileName("print-file-name", - cl::desc("Print the name of the file before each string")); -static cl::alias PrintFileNameShort("f", cl::desc(""), - cl::aliasopt(PrintFileName)); - -static cl::opt<int> - MinLength("bytes", cl::desc("Print sequences of the specified length"), - cl::init(4)); -static cl::alias MinLengthShort("n", cl::desc(""), cl::aliasopt(MinLength)); - -static cl::opt<bool> - AllSections("all", - cl::desc("Check all sections, not just the data section")); -static cl::alias AllSectionsShort("a", cl::desc(""), - cl::aliasopt(AllSections)); +static int MinLength = 4; +static bool PrintFileName; enum radix { none, octal, hexadecimal, decimal }; -static cl::opt<radix> - Radix("radix", cl::desc("print the offset within the file"), - cl::values(clEnumValN(octal, "o", "octal"), - clEnumValN(hexadecimal, "x", "hexadecimal"), - clEnumValN(decimal, "d", "decimal")), - cl::init(none)); -static cl::alias RadixShort("t", cl::desc(""), cl::aliasopt(Radix)); +static radix Radix; -static cl::extrahelp - HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); +LLVM_ATTRIBUTE_NORETURN static void reportCmdLineError(const Twine &Message) { + WithColor::error(errs(), ToolName) << Message << "\n"; + exit(1); +} + +template <typename T> +static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) { + if (const opt::Arg *A = Args.getLastArg(ID)) { + StringRef V(A->getValue()); + if (!llvm::to_integer(V, Value, 0) || Value <= 0) + reportCmdLineError("expected a positive integer, but got '" + V + "'"); + } +} static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) { auto print = [&OS, FileName](unsigned Offset, StringRef L) { @@ -96,13 +126,48 @@ static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) { int main(int argc, char **argv) { InitLLVM X(argc, argv); + BumpPtrAllocator A; + StringSaver Saver(A); + StringsOptTable Tbl; + opt::InputArgList Args = + Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, + [&](StringRef Msg) { reportCmdLineError(Msg); }); + if (Args.hasArg(OPT_help)) { + Tbl.printHelp( + outs(), + (Twine(ToolName) + " [options] <input object files>").str().c_str(), + "llvm string dumper"); + // TODO Replace this with OptTable API once it adds extrahelp support. + outs() << "\nPass @FILE as argument to read options from FILE.\n"; + return 0; + } + if (Args.hasArg(OPT_version)) { + outs() << ToolName << '\n'; + cl::PrintVersionMessage(); + return 0; + } + + parseIntArg(Args, OPT_bytes_EQ, MinLength); + PrintFileName = Args.hasArg(OPT_print_file_name); + StringRef R = Args.getLastArgValue(OPT_radix_EQ); + if (R.empty()) + Radix = none; + else if (R == "o") + Radix = octal; + else if (R == "d") + Radix = decimal; + else if (R == "x") + Radix = hexadecimal; + else + reportCmdLineError("--radix value should be one of: '' (no offset), 'o' " + "(octal), 'd' (decimal), 'x' (hexadecimal)"); - cl::ParseCommandLineOptions(argc, argv, "llvm string dumper\n"); if (MinLength == 0) { errs() << "invalid minimum string length 0\n"; return EXIT_FAILURE; } + std::vector<std::string> InputFileNames = Args.getAllArgValues(OPT_INPUT); if (InputFileNames.empty()) InputFileNames.push_back("-"); diff --git a/llvm/tools/llvm-symbolizer/Opts.td b/llvm/tools/llvm-symbolizer/Opts.td index ac23639f130e..6026e24d6ffa 100644 --- a/llvm/tools/llvm-symbolizer/Opts.td +++ b/llvm/tools/llvm-symbolizer/Opts.td @@ -1,17 +1,20 @@ include "llvm/Option/OptParser.td" multiclass B<string name, string help1, string help2> { - def NAME: Flag<["--", "-"], name>, HelpText<help1>; - def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>; + def NAME: Flag<["--"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; } multiclass Eq<string name, string help> { - def NAME #_EQ : Joined<["--", "-"], name #"=">, + def NAME #_EQ : Joined<["--"], name #"=">, HelpText<help>; - def : Separate<["--", "-"], name>, Alias<!cast<Joined>(NAME #_EQ)>; + def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>; } -class F<string name, string help>: Flag<["--", "-"], name>, HelpText<help>; +class F<string name, string help>: Flag<["--"], name>, HelpText<help>; + +def grp_mach_o : OptionGroup<"kind">, + HelpText<"llvm-symbolizer Mach-O Specific Options">; def addresses : F<"addresses", "Show address before line information">; defm adjust_vma @@ -19,13 +22,19 @@ defm adjust_vma MetaVarName<"<offset>">; def basenames : Flag<["--"], "basenames">, HelpText<"Strip directory names from paths">; defm debug_file_directory : Eq<"debug-file-directory", "Path to directory where to look for debug files">, MetaVarName<"<dir>">; -defm default_arch : Eq<"default-arch", "Default architecture (for multi-arch objects)">; +defm default_arch + : Eq<"default-arch", "Default architecture (for multi-arch objects)">, + Group<grp_mach_o>; defm demangle : B<"demangle", "Demangle function names", "Don't demangle function names">; def functions : F<"functions", "Print function name for a given address">; def functions_EQ : Joined<["--"], "functions=">, HelpText<"Print function name for a given address">, Values<"none,short,linkage">; def help : F<"help", "Display this help">; defm dwp : Eq<"dwp", "Path to DWP file to be use for any split CUs">, MetaVarName<"<file>">; -defm dsym_hint : Eq<"dsym-hint", "Path to .dSYM bundles to search for debug info for the object files">, MetaVarName<"<dir>">; +defm dsym_hint + : Eq<"dsym-hint", + "Path to .dSYM bundles to search for debug info for the object files">, + MetaVarName<"<dir>">, + Group<grp_mach_o>; defm fallback_debug_path : Eq<"fallback-debug-path", "Fallback path for debug binaries">, MetaVarName<"<dir>">; defm inlines : B<"inlines", "Print all inlined frames for a given address", "Do not print inlined frames">; @@ -33,9 +42,9 @@ defm obj : Eq<"obj", "Path to object file to be symbolized (if not provided, " "object file should be specified for each input line)">, MetaVarName<"<file>">; defm output_style - : Eq<"output-style", "Specify print style. Supported styles: LLVM, GNU">, + : Eq<"output-style", "Specify print style. Supported styles: LLVM, GNU, JSON">, MetaVarName<"style">, - Values<"LLVM,GNU">; + Values<"LLVM,GNU,JSON">; def pretty_print : F<"pretty-print", "Make the output more human friendly">; defm print_source_context_lines : Eq<"print-source-context-lines", "Print N lines of source file context">; def relative_address : F<"relative-address", "Interpret addresses as addresses relative to the image base">; diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp index 9c68acee0ae2..227ce12a6d9a 100644 --- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -66,23 +66,35 @@ static const opt::OptTable::Info InfoTable[] = { class SymbolizerOptTable : public opt::OptTable { public: - SymbolizerOptTable() : OptTable(InfoTable, true) {} + SymbolizerOptTable() : OptTable(InfoTable) { + setGroupedShortOptions(true); + } }; } // namespace -static cl::list<std::string> ClInputAddresses(cl::Positional, - cl::desc("<input addresses>..."), - cl::ZeroOrMore); +template <typename T> +static void print(const Request &Request, Expected<T> &ResOrErr, + DIPrinter &Printer) { + if (ResOrErr) { + // No error, print the result. + Printer.print(Request, *ResOrErr); + return; + } + + // Handle the error. + bool PrintEmpty = true; + handleAllErrors(std::move(ResOrErr.takeError()), + [&](const ErrorInfoBase &EI) { + PrintEmpty = Printer.printError( + Request, EI, "LLVMSymbolizer: error reading file: "); + }); -template<typename T> -static bool error(Expected<T> &ResOrErr) { - if (ResOrErr) - return false; - logAllUnhandledErrors(ResOrErr.takeError(), errs(), - "LLVMSymbolizer: error reading file: "); - return true; + if (PrintEmpty) + Printer.print(Request, T()); } +enum class OutputStyle { LLVM, GNU, JSON }; + enum class Command { Code, Data, @@ -136,7 +148,7 @@ static bool parseCommand(StringRef BinaryName, bool IsAddr2Line, } static void symbolizeInput(const opt::InputArgList &Args, uint64_t AdjustVMA, - bool IsAddr2Line, DIPrinter::OutputStyle OutputStyle, + bool IsAddr2Line, OutputStyle Style, StringRef InputString, LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { Command Cmd; @@ -144,57 +156,49 @@ static void symbolizeInput(const opt::InputArgList &Args, uint64_t AdjustVMA, uint64_t Offset = 0; if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, StringRef(InputString), Cmd, ModuleName, Offset)) { - outs() << InputString << "\n"; + Printer.printInvalidCommand({ModuleName, None}, InputString); return; } - if (Args.hasArg(OPT_addresses)) { - outs() << "0x"; - outs().write_hex(Offset); - StringRef Delimiter = Args.hasArg(OPT_pretty_print) ? ": " : "\n"; - outs() << Delimiter; - } - Offset -= AdjustVMA; + uint64_t AdjustedOffset = Offset - AdjustVMA; if (Cmd == Command::Data) { - auto ResOrErr = Symbolizer.symbolizeData( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get()); + Expected<DIGlobal> ResOrErr = Symbolizer.symbolizeData( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print({ModuleName, Offset}, ResOrErr, Printer); } else if (Cmd == Command::Frame) { - auto ResOrErr = Symbolizer.symbolizeFrame( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - if (!error(ResOrErr)) { - for (DILocal Local : *ResOrErr) - Printer << Local; - if (ResOrErr->empty()) - outs() << "??\n"; - } + Expected<std::vector<DILocal>> ResOrErr = Symbolizer.symbolizeFrame( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print({ModuleName, Offset}, ResOrErr, Printer); } else if (Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line)) { - auto ResOrErr = Symbolizer.symbolizeInlinedCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DIInliningInfo() : ResOrErr.get()); - } else if (OutputStyle == DIPrinter::OutputStyle::GNU) { + Expected<DIInliningInfo> ResOrErr = Symbolizer.symbolizeInlinedCode( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print({ModuleName, Offset}, ResOrErr, Printer); + } else if (Style == OutputStyle::GNU) { // With PrintFunctions == FunctionNameKind::LinkageName (default) // and UseSymbolTable == true (also default), Symbolizer.symbolizeCode() // may override the name of an inlined function with the name of the topmost // caller function in the inlining chain. This contradicts the existing // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only // the topmost function, which suits our needs better. - auto ResOrErr = Symbolizer.symbolizeInlinedCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get().getFrame(0)); + Expected<DIInliningInfo> ResOrErr = Symbolizer.symbolizeInlinedCode( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + Expected<DILineInfo> Res0OrErr = + !ResOrErr + ? Expected<DILineInfo>(ResOrErr.takeError()) + : ((ResOrErr->getNumberOfFrames() == 0) ? DILineInfo() + : ResOrErr->getFrame(0)); + print({ModuleName, Offset}, Res0OrErr, Printer); } else { - auto ResOrErr = Symbolizer.symbolizeCode( - ModuleName, {Offset, object::SectionedAddress::UndefSection}); - Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get()); + Expected<DILineInfo> ResOrErr = Symbolizer.symbolizeCode( + ModuleName, {AdjustedOffset, object::SectionedAddress::UndefSection}); + print({ModuleName, Offset}, ResOrErr, Printer); } - if (OutputStyle == DIPrinter::OutputStyle::LLVM) - outs() << "\n"; } static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl, raw_ostream &OS) { const char HelpText[] = " [options] addresses..."; - Tbl.PrintHelp(OS, (ToolName + HelpText).str().c_str(), + Tbl.printHelp(OS, (ToolName + HelpText).str().c_str(), ToolName.str().c_str()); // TODO Replace this with OptTable API once it adds extrahelp support. OS << "\nPass @FILE as argument to read options from FILE.\n"; @@ -204,7 +208,6 @@ static opt::InputArgList parseOptions(int Argc, char *Argv[], bool IsAddr2Line, StringSaver &Saver, SymbolizerOptTable &Tbl) { StringRef ToolName = IsAddr2Line ? "llvm-addr2line" : "llvm-symbolizer"; - Tbl.setGroupedShortOptions(true); // The environment variable specifies initial options which can be overridden // by commnad line options. Tbl.setInitialOptionsFromEnvironment(IsAddr2Line ? "LLVM_ADDR2LINE_OPTS" @@ -268,7 +271,7 @@ int main(int argc, char **argv) { LLVMSymbolizer::Options Opts; uint64_t AdjustVMA; - unsigned SourceContextLines; + PrinterConfig Config; parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA); if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) { Opts.PathStyle = @@ -285,7 +288,8 @@ int main(int argc, char **argv) { Opts.FallbackDebugPath = Args.getLastArgValue(OPT_fallback_debug_path_EQ).str(); Opts.PrintFunctions = decideHowToPrintFunctions(Args, IsAddr2Line); - parseIntArg(Args, OPT_print_source_context_lines_EQ, SourceContextLines); + parseIntArg(Args, OPT_print_source_context_lines_EQ, + Config.SourceContextLines); Opts.RelativeAddresses = Args.hasArg(OPT_relative_address); Opts.UntagAddresses = Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line); @@ -297,6 +301,10 @@ int main(int argc, char **argv) { } #endif Opts.UseSymbolTable = true; + Config.PrintAddress = Args.hasArg(OPT_addresses); + Config.PrintFunctions = Opts.PrintFunctions != FunctionNameKind::None; + Config.Pretty = Args.hasArg(OPT_pretty_print); + Config.Verbose = Args.hasArg(OPT_verbose); for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) { StringRef Hint(A->getValue()); @@ -308,18 +316,24 @@ int main(int argc, char **argv) { } } - auto OutputStyle = - IsAddr2Line ? DIPrinter::OutputStyle::GNU : DIPrinter::OutputStyle::LLVM; + auto Style = IsAddr2Line ? OutputStyle::GNU : OutputStyle::LLVM; if (const opt::Arg *A = Args.getLastArg(OPT_output_style_EQ)) { - OutputStyle = strcmp(A->getValue(), "GNU") == 0 - ? DIPrinter::OutputStyle::GNU - : DIPrinter::OutputStyle::LLVM; + if (strcmp(A->getValue(), "GNU") == 0) + Style = OutputStyle::GNU; + else if (strcmp(A->getValue(), "JSON") == 0) + Style = OutputStyle::JSON; + else + Style = OutputStyle::LLVM; } LLVMSymbolizer Symbolizer(Opts); - DIPrinter Printer(outs(), Opts.PrintFunctions != FunctionNameKind::None, - Args.hasArg(OPT_pretty_print), SourceContextLines, - Args.hasArg(OPT_verbose), OutputStyle); + std::unique_ptr<DIPrinter> Printer; + if (Style == OutputStyle::GNU) + Printer = std::make_unique<GNUPrinter>(outs(), errs(), Config); + else if (Style == OutputStyle::JSON) + Printer = std::make_unique<JSONPrinter>(outs(), Config); + else + Printer = std::make_unique<LLVMPrinter>(outs(), errs(), Config); std::vector<std::string> InputAddresses = Args.getAllArgValues(OPT_INPUT); if (InputAddresses.empty()) { @@ -331,14 +345,16 @@ int main(int argc, char **argv) { std::string StrippedInputString(InputString); llvm::erase_if(StrippedInputString, [](char c) { return c == '\r' || c == '\n'; }); - symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, - StrippedInputString, Symbolizer, Printer); + symbolizeInput(Args, AdjustVMA, IsAddr2Line, Style, StrippedInputString, + Symbolizer, *Printer); outs().flush(); } } else { + Printer->listBegin(); for (StringRef Address : InputAddresses) - symbolizeInput(Args, AdjustVMA, IsAddr2Line, OutputStyle, Address, - Symbolizer, Printer); + symbolizeInput(Args, AdjustVMA, IsAddr2Line, Style, Address, Symbolizer, + *Printer); + Printer->listEnd(); } return 0; diff --git a/llvm/tools/llvm-tapi-diff/DiffEngine.cpp b/llvm/tools/llvm-tapi-diff/DiffEngine.cpp new file mode 100644 index 000000000000..ff60d52f5c65 --- /dev/null +++ b/llvm/tools/llvm-tapi-diff/DiffEngine.cpp @@ -0,0 +1,556 @@ +//===-- DiffEngine.cpp - Structural file comparison -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the implementation of the llvm-tapi difference +// engine, which structurally compares two tbd files. +// +//===----------------------------------------------------------------------===/ +#include "DiffEngine.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/Symbol.h" +#include "llvm/TextAPI/Target.h" + +using namespace llvm; +using namespace MachO; +using namespace object; + +StringRef setOrderIndicator(InterfaceInputOrder Order) { + return ((Order == lhs) ? "< " : "> "); +} + +// The following template specialization implementations +// need to be explicitly placed into the llvm namespace +// to work around a GCC 4.8 bug. +namespace llvm { + +template <typename T, DiffAttrKind U> +inline void DiffScalarVal<T, U>::print(raw_ostream &OS, std::string Indent) { + OS << Indent << "\t" << setOrderIndicator(Order) << Val << "\n"; +} + +template <> +inline void +DiffScalarVal<StringRef, AD_Diff_Scalar_Str>::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t\t" << setOrderIndicator(Order) << Val << "\n"; +} + +template <> +inline void +DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t" << setOrderIndicator(Order) << std::to_string(Val) + << "\n"; +} + +template <> +inline void +DiffScalarVal<bool, AD_Diff_Scalar_Bool>::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t" << setOrderIndicator(Order) + << ((Val == true) ? "true" : "false") << "\n"; +} + +} // end namespace llvm + +StringLiteral SymScalar::getSymbolNamePrefix(MachO::SymbolKind Kind) { + switch (Kind) { + case MachO::SymbolKind::GlobalSymbol: + return StringLiteral(""); + case MachO::SymbolKind::ObjectiveCClass: + return ObjC2MetaClassNamePrefix; + case MachO::SymbolKind ::ObjectiveCClassEHType: + return ObjC2EHTypePrefix; + case MachO::SymbolKind ::ObjectiveCInstanceVariable: + return ObjC2IVarPrefix; + } + llvm_unreachable("Unknown llvm::MachO::SymbolKind enum"); +} + +std::string SymScalar::stringifySymbolFlag(MachO::SymbolFlags Flag) { + switch (Flag) { + case MachO::SymbolFlags::None: + return ""; + case MachO::SymbolFlags::ThreadLocalValue: + return "Thread-Local"; + case MachO::SymbolFlags::WeakDefined: + return "Weak-Defined"; + case MachO::SymbolFlags::WeakReferenced: + return "Weak-Referenced"; + case MachO::SymbolFlags::Undefined: + return "Undefined"; + case MachO::SymbolFlags::Rexported: + return "Reexported"; + } + llvm_unreachable("Unknown llvm::MachO::SymbolFlags enum"); +} + +void SymScalar::print(raw_ostream &OS, std::string Indent, MachO::Target Targ) { + if (Val->getKind() == MachO::SymbolKind::ObjectiveCClass) { + if (Targ.Arch == MachO::AK_i386 && + Targ.Platform == MachO::PlatformKind::macOS) { + OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") + << ObjC1ClassNamePrefix << Val->getName() + << getFlagString(Val->getFlags()) << "\n"; + return; + } + OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") + << ObjC2ClassNamePrefix << Val->getName() + << getFlagString(Val->getFlags()) << "\n"; + } + OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") + << getSymbolNamePrefix(Val->getKind()) << Val->getName() + << getFlagString(Val->getFlags()) << "\n"; +} + +bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS, + llvm::MachO::InterfaceFile::const_symbol_range RHS) { + return std::equal(LHS.begin(), LHS.end(), RHS.begin(), + [&](auto LHS, auto RHS) { return *LHS == *RHS; }); +} + +template <typename TargetVecT, typename ValTypeT, typename V> +void addDiffForTargSlice(V Val, Target Targ, DiffOutput &Diff, + InterfaceInputOrder Order) { + auto TargetVector = llvm::find_if( + Diff.Values, [&](const std::unique_ptr<AttributeDiff> &RawTVec) { + if (TargetVecT *TVec = dyn_cast<TargetVecT>(RawTVec.get())) + return TVec->Targ == Targ; + return false; + }); + if (TargetVector != Diff.Values.end()) { + ValTypeT NewVal(Order, Val); + cast<TargetVecT>(TargetVector->get())->TargValues.push_back(NewVal); + } else { + auto NewTargetVec = std::make_unique<TargetVecT>(Targ); + ValTypeT NewVal(Order, Val); + NewTargetVec->TargValues.push_back(NewVal); + Diff.Values.push_back(std::move(NewTargetVec)); + } +} + +DiffOutput getSingleAttrDiff(const std::vector<InterfaceFileRef> &IRefVec, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Str_Vec; + for (const auto &IRef : IRefVec) + for (auto Targ : IRef.targets()) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + IRef.getInstallName(), Targ, Diff, Order); + return Diff; +} + +DiffOutput +getSingleAttrDiff(const std::vector<std::pair<Target, std::string>> &PairVec, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Str_Vec; + for (const auto &Pair : PairVec) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + StringRef(Pair.second), Pair.first, Diff, Order); + return Diff; +} + +DiffOutput getSingleAttrDiff(InterfaceFile::const_symbol_range SymRange, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Sym_Vec; + for (const auto *Sym : SymRange) + for (auto Targ : Sym->targets()) + addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Diff, Order); + return Diff; +} + +template <typename T> +DiffOutput getSingleAttrDiff(T SingleAttr, std::string Attribute) { + DiffOutput Diff(Attribute); + Diff.Kind = SingleAttr.getKind(); + Diff.Values.push_back(std::make_unique<T>(SingleAttr)); + return Diff; +} + +template <typename T, DiffAttrKind U> +void diffAttribute(std::string Name, std::vector<DiffOutput> &Output, + DiffScalarVal<T, U> Attr) { + Output.push_back(getSingleAttrDiff(Attr, Name)); +} + +template <typename T> +void diffAttribute(std::string Name, std::vector<DiffOutput> &Output, + const T &Val, InterfaceInputOrder Order) { + Output.push_back(getSingleAttrDiff(Val, Name, Order)); +} + +std::vector<DiffOutput> getSingleIF(InterfaceFile *Interface, + InterfaceInputOrder Order) { + std::vector<DiffOutput> Output; + diffAttribute("Install Name", Output, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>( + Order, Interface->getInstallName())); + diffAttribute("Current Version", Output, + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + Order, Interface->getCurrentVersion())); + diffAttribute("Compatibility Version", Output, + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + Order, Interface->getCompatibilityVersion())); + diffAttribute("Swift ABI Version", Output, + DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( + Order, Interface->getSwiftABIVersion())); + diffAttribute("InstallAPI", Output, + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + Order, Interface->isInstallAPI())); + diffAttribute("Two Level Namespace", Output, + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + Order, Interface->isTwoLevelNamespace())); + diffAttribute("Application Extension Safe", Output, + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + Order, Interface->isApplicationExtensionSafe())); + diffAttribute("Reexported Libraries", Output, + Interface->reexportedLibraries(), Order); + diffAttribute("Allowable Clients", Output, Interface->allowableClients(), + Order); + diffAttribute("Parent Umbrellas", Output, Interface->umbrellas(), Order); + diffAttribute("Symbols", Output, Interface->symbols(), Order); + for (auto Doc : Interface->documents()) { + DiffOutput Documents("Inlined Reexported Frameworks/Libraries"); + Documents.Kind = AD_Inline_Doc; + Documents.Values.push_back(std::make_unique<InlineDoc>( + InlineDoc(Doc->getInstallName(), getSingleIF(Doc.get(), Order)))); + Output.push_back(std::move(Documents)); + } + return Output; +} + +void findAndAddDiff(const std::vector<InterfaceFileRef> &CollectedIRefVec, + const std::vector<InterfaceFileRef> &LookupIRefVec, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Str_Vec; + for (const auto &IRef : CollectedIRefVec) + for (auto Targ : IRef.targets()) { + auto FoundIRef = llvm::find_if(LookupIRefVec, [&](const auto LIRef) { + auto FoundTarg = llvm::find(LIRef.targets(), Targ); + return (FoundTarg != LIRef.targets().end() && + IRef.getInstallName() == LIRef.getInstallName()); + }); + if (FoundIRef == LookupIRefVec.end()) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + IRef.getInstallName(), Targ, Result, Order); + } +} + +void findAndAddDiff( + const std::vector<std::pair<Target, std::string>> &CollectedPairs, + const std::vector<std::pair<Target, std::string>> &LookupPairs, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Str_Vec; + for (const auto &Pair : CollectedPairs) { + auto FoundPair = llvm::find(LookupPairs, Pair); + if (FoundPair == LookupPairs.end()) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + StringRef(Pair.second), Pair.first, Result, Order); + } +} + +void findAndAddDiff(InterfaceFile::const_symbol_range CollectedSyms, + InterfaceFile::const_symbol_range LookupSyms, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Sym_Vec; + for (const auto *Sym : CollectedSyms) + for (const auto Targ : Sym->targets()) { + auto FoundSym = llvm::find_if(LookupSyms, [&](const auto LSym) { + auto FoundTarg = llvm::find(LSym->targets(), Targ); + return (Sym->getName() == LSym->getName() && + Sym->getKind() == LSym->getKind() && + Sym->getFlags() == LSym->getFlags() && + FoundTarg != LSym->targets().end()); + }); + if (FoundSym == LookupSyms.end()) + addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Result, Order); + } +} + +template <typename T> +DiffOutput recordDifferences(T LHS, T RHS, std::string Attr) { + DiffOutput Diff(Attr); + if (LHS.getKind() == RHS.getKind()) { + Diff.Kind = LHS.getKind(); + Diff.Values.push_back(std::make_unique<T>(LHS)); + Diff.Values.push_back(std::make_unique<T>(RHS)); + } + return Diff; +} + +template <typename T> +DiffOutput recordDifferences(const std::vector<T> &LHS, + const std::vector<T> &RHS, std::string Attr) { + DiffOutput Diff(Attr); + Diff.Kind = AD_Str_Vec; + findAndAddDiff(LHS, RHS, Diff, lhs); + findAndAddDiff(RHS, LHS, Diff, rhs); + return Diff; +} + +DiffOutput recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS, + llvm::MachO::InterfaceFile::const_symbol_range RHS, + std::string Attr) { + DiffOutput Diff(Attr); + Diff.Kind = AD_Sym_Vec; + findAndAddDiff(LHS, RHS, Diff, lhs); + findAndAddDiff(RHS, LHS, Diff, rhs); + return Diff; +} + +std::vector<DiffOutput> +DiffEngine::findDifferences(const InterfaceFile *IFLHS, + const InterfaceFile *IFRHS) { + std::vector<DiffOutput> Output; + if (IFLHS->getInstallName() != IFRHS->getInstallName()) + Output.push_back(recordDifferences( + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(lhs, + IFLHS->getInstallName()), + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(rhs, + IFRHS->getInstallName()), + "Install Name")); + + if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion()) + Output.push_back(recordDifferences( + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + lhs, IFLHS->getCurrentVersion()), + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + rhs, IFRHS->getCurrentVersion()), + "Current Version")); + if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion()) + Output.push_back(recordDifferences( + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + lhs, IFLHS->getCompatibilityVersion()), + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + rhs, IFRHS->getCompatibilityVersion()), + "Compatibility Version")); + if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion()) + Output.push_back( + recordDifferences(DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( + lhs, IFLHS->getSwiftABIVersion()), + DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( + rhs, IFRHS->getSwiftABIVersion()), + "Swift ABI Version")); + if (IFLHS->isInstallAPI() != IFRHS->isInstallAPI()) + Output.push_back(recordDifferences( + DiffScalarVal<bool, AD_Diff_Scalar_Bool>(lhs, IFLHS->isInstallAPI()), + DiffScalarVal<bool, AD_Diff_Scalar_Bool>(rhs, IFRHS->isInstallAPI()), + "InstallAPI")); + + if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace()) + Output.push_back(recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + lhs, IFLHS->isTwoLevelNamespace()), + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + rhs, IFRHS->isTwoLevelNamespace()), + "Two Level Namespace")); + + if (IFLHS->isApplicationExtensionSafe() != + IFRHS->isApplicationExtensionSafe()) + Output.push_back( + recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + lhs, IFLHS->isApplicationExtensionSafe()), + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + rhs, IFRHS->isApplicationExtensionSafe()), + "Application Extension Safe")); + + if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries()) + Output.push_back(recordDifferences(IFLHS->reexportedLibraries(), + IFRHS->reexportedLibraries(), + "Reexported Libraries")); + + if (IFLHS->allowableClients() != IFRHS->allowableClients()) + Output.push_back(recordDifferences(IFLHS->allowableClients(), + IFRHS->allowableClients(), + "Allowable Clients")); + + if (IFLHS->umbrellas() != IFRHS->umbrellas()) + Output.push_back(recordDifferences(IFLHS->umbrellas(), IFRHS->umbrellas(), + "Parent Umbrellas")); + + if (!checkSymbolEquality(IFLHS->symbols(), IFRHS->symbols())) + Output.push_back( + recordDifferences(IFLHS->symbols(), IFRHS->symbols(), "Symbols")); + + if (IFLHS->documents() != IFRHS->documents()) { + DiffOutput Docs("Inlined Reexported Frameworks/Libraries"); + Docs.Kind = AD_Inline_Doc; + std::vector<StringRef> DocsInserted; + // Iterate through inline frameworks/libraries from interface file and find + // match based on install name. + for (auto DocLHS : IFLHS->documents()) { + auto Pair = llvm::find_if(IFRHS->documents(), [&](const auto &DocRHS) { + return (DocLHS->getInstallName() == DocRHS->getInstallName()); + }); + // If a match found, recursively get differences between the pair. + if (Pair != IFRHS->documents().end()) { + InlineDoc PairDiff = + InlineDoc(DocLHS->getInstallName(), + findDifferences(DocLHS.get(), Pair->get())); + if (!PairDiff.DocValues.empty()) + Docs.Values.push_back( + std::make_unique<InlineDoc>(std::move(PairDiff))); + } + // If a match is not found, get attributes from single item. + else + Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc( + DocLHS->getInstallName(), getSingleIF(DocLHS.get(), lhs)))); + DocsInserted.push_back(DocLHS->getInstallName()); + } + for (auto DocRHS : IFRHS->documents()) { + auto WasGathered = + llvm::find_if(DocsInserted, [&](const auto &GatheredDoc) { + return (GatheredDoc == DocRHS->getInstallName()); + }); + if (WasGathered == DocsInserted.end()) + Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc( + DocRHS->getInstallName(), getSingleIF(DocRHS.get(), rhs)))); + } + if (!Docs.Values.empty()) + Output.push_back(std::move(Docs)); + } + return Output; +} + +template <typename T> +void printSingleVal(std::string Indent, const DiffOutput &Attr, + raw_ostream &OS) { + if (Attr.Values.empty()) + return; + OS << Indent << Attr.Name << "\n"; + for (auto &RawItem : Attr.Values) + if (T *Item = dyn_cast<T>(RawItem.get())) + Item->print(OS, Indent); +} + +template <typename T> +T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) { + T *CastAttr = cast<T>(RawAttr.get()); + return CastAttr; +} + +template <typename T> void sortTargetValues(std::vector<T> &TargValues) { + llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) { + return ValA.getOrder() < ValB.getOrder(); + }); + llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) { + return ValA.getOrder() == ValB.getOrder() && ValA.getVal() < ValB.getVal(); + }); +} + +template <typename T> +void printVecVal(std::string Indent, const DiffOutput &Attr, raw_ostream &OS) { + if (Attr.Values.empty()) + return; + + OS << Indent << Attr.Name << "\n"; + + std::vector<T *> SortedAttrs; + + llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), castValues<T>); + + llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) { + return ValA->Targ < ValB->Targ; + }); + + for (auto *Vec : SortedAttrs) { + sortTargetValues<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + Vec->TargValues); + OS << Indent << "\t" << getTargetTripleName(Vec->Targ) << "\n"; + for (auto &Item : Vec->TargValues) + Item.print(OS, Indent); + } +} + +template <> +void printVecVal<DiffSymVec>(std::string Indent, const DiffOutput &Attr, + raw_ostream &OS) { + if (Attr.Values.empty()) + return; + + OS << Indent << Attr.Name << "\n"; + + std::vector<DiffSymVec *> SortedAttrs; + + llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), + castValues<DiffSymVec>); + + llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) { + return ValA->Targ < ValB->Targ; + }); + for (auto *SymVec : SortedAttrs) { + sortTargetValues<SymScalar>(SymVec->TargValues); + OS << Indent << "\t" << getTargetTripleName(SymVec->Targ) << "\n"; + for (auto &Item : SymVec->TargValues) + Item.print(OS, Indent, SymVec->Targ); + } +} + +void DiffEngine::printDifferences(raw_ostream &OS, + const std::vector<DiffOutput> &Diffs, + int IndentCounter) { + std::string Indent = std::string(IndentCounter, '\t'); + for (auto &Attr : Diffs) { + switch (Attr.Kind) { + case AD_Diff_Scalar_Str: + if (IndentCounter == 0) + printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_PackedVersion: + printSingleVal< + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_Unsigned: + printSingleVal<DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_Bool: + printSingleVal<DiffScalarVal<bool, AD_Diff_Scalar_Bool>>(Indent, Attr, + OS); + break; + case AD_Str_Vec: + printVecVal<DiffStrVec>(Indent, Attr, OS); + break; + case AD_Sym_Vec: + printVecVal<DiffSymVec>(Indent, Attr, OS); + break; + case AD_Inline_Doc: + if (!Attr.Values.empty()) { + OS << Indent << Attr.Name << "\n"; + for (auto &Item : Attr.Values) + if (InlineDoc *Doc = dyn_cast<InlineDoc>(Item.get())) + if (!Doc->DocValues.empty()) { + OS << Indent << "\t" << Doc->InstallName << "\n"; + printDifferences(OS, std::move(Doc->DocValues), 2); + } + } + break; + } + } +} + +bool DiffEngine::compareFiles(raw_ostream &OS) { + const auto *IFLHS = &(FileLHS->getInterfaceFile()); + const auto *IFRHS = &(FileRHS->getInterfaceFile()); + if (*IFLHS == *IFRHS) + return false; + OS << "< " << std::string(IFLHS->getPath().data()) << "\n> " + << std::string(IFRHS->getPath().data()) << "\n\n"; + std::vector<DiffOutput> Diffs = findDifferences(IFLHS, IFRHS); + printDifferences(OS, Diffs, 0); + return true; +} diff --git a/llvm/tools/llvm-tapi-diff/DiffEngine.h b/llvm/tools/llvm-tapi-diff/DiffEngine.h new file mode 100644 index 000000000000..252fbd87c637 --- /dev/null +++ b/llvm/tools/llvm-tapi-diff/DiffEngine.h @@ -0,0 +1,169 @@ +//===-- DiffEngine.h - File comparator --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header defines the interface to the llvm-tapi difference engine, +// which structurally compares two tbd files. +// +//===----------------------------------------------------------------------===/ +#ifndef LLVM_TOOLS_LLVM_TAPI_DIFF_DIFFENGINE_H +#define LLVM_TOOLS_LLVM_TAPI_DIFF_DIFFENGINE_H + +#include "llvm/ADT/Optional.h" +#include "llvm/Object/TapiUniversal.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TextAPI/Symbol.h" +#include "llvm/TextAPI/Target.h" + +namespace llvm { + +/// InterfaceInputOrder determines from which file the diff attribute belongs +/// to. +enum InterfaceInputOrder { lhs, rhs }; + +/// DiffAttrKind is the enum that holds the concrete bases for RTTI. +enum DiffAttrKind { + AD_Diff_Scalar_PackedVersion, + AD_Diff_Scalar_Unsigned, + AD_Diff_Scalar_Bool, + AD_Diff_Scalar_Str, + AD_Str_Vec, + AD_Sym_Vec, + AD_Inline_Doc, +}; + +/// AttributeDiff is the abstract class for RTTI. +class AttributeDiff { +public: + AttributeDiff(DiffAttrKind Kind) : Kind(Kind){}; + virtual ~AttributeDiff(){}; + DiffAttrKind getKind() const { return Kind; } + +private: + DiffAttrKind Kind; +}; + +/// DiffOutput is the representation of a diff for a single attribute. +struct DiffOutput { + /// The name of the attribute. + std::string Name; + /// The kind for RTTI + DiffAttrKind Kind; + /// Different values for the attribute + /// from each file where a diff is present. + std::vector<std::unique_ptr<AttributeDiff>> Values; + DiffOutput(std::string Name) : Name(Name){}; +}; + +/// DiffScalarVal is a template class for the different types of scalar values. +template <class T, DiffAttrKind U> class DiffScalarVal : public AttributeDiff { +public: + DiffScalarVal(InterfaceInputOrder Order, T Val) + : AttributeDiff(U), Order(Order), Val(Val){}; + + static bool classof(const AttributeDiff *A) { return A->getKind() == U; } + + void print(raw_ostream &, std::string); + + T getVal() const { return Val; } + InterfaceInputOrder getOrder() const { return Order; } + +private: + /// The order is the file from which the diff is found. + InterfaceInputOrder Order; + T Val; +}; + +/// SymScalar is the diff symbol and the order. +class SymScalar { +public: + SymScalar(InterfaceInputOrder Order, const MachO::Symbol *Sym) + : Order(Order), Val(Sym){}; + + std::string getFlagString(MachO::SymbolFlags Flags) { + return Flags != MachO::SymbolFlags::None + ? " - " + stringifySymbolFlag(Flags) + : stringifySymbolFlag(Flags); + } + + void print(raw_ostream &OS, std::string Indent, MachO::Target Targ); + + const MachO::Symbol *getVal() const { return Val; } + InterfaceInputOrder getOrder() const { return Order; } + +private: + /// The order is the file from which the diff is found. + InterfaceInputOrder Order; + const MachO::Symbol *Val; + StringLiteral getSymbolNamePrefix(MachO::SymbolKind Kind); + std::string stringifySymbolFlag(MachO::SymbolFlags Flag); +}; + +class DiffStrVec : public AttributeDiff { +public: + MachO::Target Targ; + /// Values is a vector of StringRef values associated with the target. + std::vector<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>> TargValues; + DiffStrVec(MachO::Target Targ) : AttributeDiff(AD_Str_Vec), Targ(Targ){}; + + static bool classof(const AttributeDiff *A) { + return A->getKind() == AD_Str_Vec; + } +}; + +class DiffSymVec : public AttributeDiff { +public: + MachO::Target Targ; + /// Values is a vector of symbol values associated with the target. + std::vector<SymScalar> TargValues; + DiffSymVec(MachO::Target Targ) : AttributeDiff(AD_Sym_Vec), Targ(Targ){}; + + static bool classof(const AttributeDiff *A) { + return A->getKind() == AD_Sym_Vec; + } +}; + +/// InlineDoc represents an inlined framework/library in a TBD File. +class InlineDoc : public AttributeDiff { +public: + /// Install name of the framework/library. + std::string InstallName; + /// Differences found from each file. + std::vector<DiffOutput> DocValues; + InlineDoc(StringRef InstName, std::vector<DiffOutput> Diff) + : AttributeDiff(AD_Inline_Doc), InstallName(InstName), + DocValues(std::move(Diff)){}; + + static bool classof(const AttributeDiff *A) { + return A->getKind() == AD_Inline_Doc; + } +}; + +/// DiffEngine contains the methods to compare the input files and print the +/// output of the differences found in the files. +class DiffEngine { +public: + DiffEngine(object::TapiUniversal *InputFileNameLHS, + object::TapiUniversal *InputFileNameRHS) + : FileLHS(InputFileNameLHS), FileRHS(InputFileNameRHS){}; + bool compareFiles(raw_ostream &); + +private: + object::TapiUniversal *FileLHS; + object::TapiUniversal *FileRHS; + + /// Function that prints the differences found in the files. + void printDifferences(raw_ostream &, const std::vector<DiffOutput> &, int); + /// Function that does the comparison of the TBD files and returns the + /// differences. + std::vector<DiffOutput> findDifferences(const MachO::InterfaceFile *, + const MachO::InterfaceFile *); +}; + +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp b/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp new file mode 100644 index 000000000000..40f1eec162be --- /dev/null +++ b/llvm/tools/llvm-tapi-diff/llvm-tapi-diff.cpp @@ -0,0 +1,89 @@ +//===-- llvm-tapi-diff.cpp - tbd comparator command-line driver ---*- +// C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the command-line driver for the llvm-tapi difference +// engine. +// +//===----------------------------------------------------------------------===// +#include "DiffEngine.h" +#include "llvm/Object/TapiUniversal.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdlib> + +using namespace llvm; +using namespace MachO; +using namespace object; + +namespace { +cl::OptionCategory NMCat("llvm-tapi-diff Options"); +cl::opt<std::string> InputFileNameLHS(cl::Positional, cl::desc("<first file>"), + cl::cat(NMCat)); +cl::opt<std::string> InputFileNameRHS(cl::Positional, cl::desc("<second file>"), + cl::cat(NMCat)); + +std::string ToolName; +} // anonymous namespace + +ExitOnError ExitOnErr; + +void setErrorBanner(ExitOnError &ExitOnErr, std::string InputFile) { + ExitOnErr.setBanner(ToolName + ": error: " + InputFile + ": "); +} + +Expected<std::unique_ptr<Binary>> convertFileToBinary(std::string &Filename) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = + MemoryBuffer::getFileOrSTDIN(Filename); + if (BufferOrErr.getError()) + return errorCodeToError(BufferOrErr.getError()); + return createBinary(BufferOrErr.get()->getMemBufferRef()); +} + +int main(int Argc, char **Argv) { + InitLLVM X(Argc, Argv); + cl::HideUnrelatedOptions(NMCat); + cl::ParseCommandLineOptions( + Argc, Argv, + "This tool will compare two tbd files and return the " + "differences in those files."); + if (InputFileNameLHS.empty() || InputFileNameRHS.empty()) { + cl::PrintHelpMessage(); + return EXIT_FAILURE; + } + + ToolName = Argv[0]; + + setErrorBanner(ExitOnErr, InputFileNameLHS); + auto BinLHS = ExitOnErr(convertFileToBinary(InputFileNameLHS)); + + TapiUniversal *FileLHS = dyn_cast<TapiUniversal>(BinLHS.get()); + if (!FileLHS) { + ExitOnErr( + createStringError(std::errc::executable_format_error, + "Error when parsing file, unsupported file format")); + } + + setErrorBanner(ExitOnErr, InputFileNameRHS); + auto BinRHS = ExitOnErr(convertFileToBinary(InputFileNameRHS)); + + TapiUniversal *FileRHS = dyn_cast<TapiUniversal>(BinRHS.get()); + if (!FileRHS) { + ExitOnErr( + createStringError(std::errc::executable_format_error, + "Error when parsing file, unsupported file format")); + } + + raw_ostream &OS = outs(); + + return DiffEngine(FileLHS, FileRHS).compareFiles(OS); +} diff --git a/llvm/tools/llvm-xray/xray-account.cpp b/llvm/tools/llvm-xray/xray-account.cpp index bde028a432fe..111704665c0b 100644 --- a/llvm/tools/llvm-xray/xray-account.cpp +++ b/llvm/tools/llvm-xray/xray-account.cpp @@ -459,7 +459,7 @@ static CommandRegistration Unused(&Account, []() -> Error { } std::error_code EC; - raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::OF_Text); + raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF); if (EC) return make_error<StringError>( Twine("Cannot open file '") + AccountOutput + "' for writing.", EC); diff --git a/llvm/tools/llvm-xray/xray-color-helper.cpp b/llvm/tools/llvm-xray/xray-color-helper.cpp index ea7ff357826b..e2cae21e162b 100644 --- a/llvm/tools/llvm-xray/xray-color-helper.cpp +++ b/llvm/tools/llvm-xray/xray-color-helper.cpp @@ -13,6 +13,7 @@ #include "xray-color-helper.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" +#include <cmath> using namespace llvm; using namespace xray; diff --git a/llvm/tools/llvm-xray/xray-color-helper.h b/llvm/tools/llvm-xray/xray-color-helper.h index 0940fc211343..3141e90cc893 100644 --- a/llvm/tools/llvm-xray/xray-color-helper.h +++ b/llvm/tools/llvm-xray/xray-color-helper.h @@ -13,9 +13,8 @@ #ifndef XRAY_COLOR_HELPER_H #define XRAY_COLOR_HELPER_H -#include <tuple> - #include "llvm/ADT/ArrayRef.h" +#include <tuple> namespace llvm { namespace xray { diff --git a/llvm/tools/llvm-xray/xray-converter.cpp b/llvm/tools/llvm-xray/xray-converter.cpp index c1a623f0f858..47cb645a5408 100644 --- a/llvm/tools/llvm-xray/xray-converter.cpp +++ b/llvm/tools/llvm-xray/xray-converter.cpp @@ -269,19 +269,14 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records, auto CycleFreq = FH.CycleFrequency; unsigned id_counter = 0; + int NumOutputRecords = 0; - OS << "{\n \"traceEvents\": ["; + OS << "{\n \"traceEvents\": [\n"; DenseMap<uint32_t, StackTrieNode *> StackCursorByThreadId{}; DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> StackRootsByThreadId{}; DenseMap<unsigned, StackTrieNode *> StacksByStackId{}; std::forward_list<StackTrieNode> NodeStore{}; - int loop_count = 0; for (const auto &R : Records) { - if (loop_count++ == 0) - OS << "\n"; - else - OS << ",\n"; - // Chrome trace event format always wants data in micros. // CyclesPerMicro = CycleHertz / 10^6 // TSC / CyclesPerMicro == TSC * 10^6 / CycleHertz == MicroTimestamp @@ -306,6 +301,9 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records, // type of B for begin or E for end, thread id, process id, // timestamp in microseconds, and a stack frame id. The ids are logged // in an id dictionary after the events. + if (NumOutputRecords++ > 0) { + OS << ",\n"; + } writeTraceViewerRecord(Version, OS, R.FuncId, R.TId, R.PId, Symbolize, FuncIdHelper, EventTimestampUs, *StackCursor, "B"); break; @@ -318,7 +316,7 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records, // (And/Or in loop termination below) StackTrieNode *PreviousCursor = nullptr; do { - if (PreviousCursor != nullptr) { + if (NumOutputRecords++ > 0) { OS << ",\n"; } writeTraceViewerRecord(Version, OS, StackCursor->FuncId, R.TId, R.PId, @@ -383,7 +381,7 @@ static CommandRegistration Unused(&Convert, []() -> Error { raw_fd_ostream OS(ConvertOutput, EC, ConvertOutputFormat == ConvertFormats::BINARY ? sys::fs::OpenFlags::OF_None - : sys::fs::OpenFlags::OF_Text); + : sys::fs::OpenFlags::OF_TextWithCRLF); if (EC) return make_error<StringError>( Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC); diff --git a/llvm/tools/llvm-xray/xray-extract.cpp b/llvm/tools/llvm-xray/xray-extract.cpp index 8304d2d27afa..a6ffacc6ab92 100644 --- a/llvm/tools/llvm-xray/xray-extract.cpp +++ b/llvm/tools/llvm-xray/xray-extract.cpp @@ -83,7 +83,7 @@ static CommandRegistration Unused(&Extract, []() -> Error { InstrumentationMapOrError.takeError()); std::error_code EC; - raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::OF_Text); + raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF); if (EC) return make_error<StringError>( Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC); diff --git a/llvm/tools/llvm-xray/xray-graph-diff.cpp b/llvm/tools/llvm-xray/xray-graph-diff.cpp index 11210e2004a7..f22ea06e0537 100644 --- a/llvm/tools/llvm-xray/xray-graph-diff.cpp +++ b/llvm/tools/llvm-xray/xray-graph-diff.cpp @@ -294,10 +294,7 @@ static Twine truncateString(const StringRef &S, size_t n) { } template <typename T> static bool containsNullptr(const T &Collection) { - for (const auto &E : Collection) - if (E == nullptr) - return true; - return false; + return llvm::is_contained(Collection, nullptr); } static std::string getLabel(const GraphDiffRenderer::GraphT::EdgeValueType &E, @@ -459,7 +456,7 @@ static CommandRegistration Unused(&GraphDiff, []() -> Error { auto &GDR = *GDROrErr; std::error_code EC; - raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::OF_Text); + raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF); if (EC) return make_error<StringError>( Twine("Cannot open file '") + GraphDiffOutput + "' for writing.", EC); diff --git a/llvm/tools/llvm-xray/xray-graph.cpp b/llvm/tools/llvm-xray/xray-graph.cpp index 00a1807c07c9..39d2c5c153ef 100644 --- a/llvm/tools/llvm-xray/xray-graph.cpp +++ b/llvm/tools/llvm-xray/xray-graph.cpp @@ -523,7 +523,7 @@ static CommandRegistration Unused(&GraphC, []() -> Error { auto &GR = *GROrError; std::error_code EC; - raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::OF_Text); + raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::OF_TextWithCRLF); if (EC) return make_error<StringError>( Twine("Cannot open file '") + GraphOutput + "' for writing.", EC); diff --git a/llvm/tools/opt/NewPMDriver.cpp b/llvm/tools/opt/NewPMDriver.cpp index 401a58fc154a..8b1fbd09e40b 100644 --- a/llvm/tools/opt/NewPMDriver.cpp +++ b/llvm/tools/opt/NewPMDriver.cpp @@ -52,9 +52,19 @@ cl::opt<std::string> cl::value_desc("filename")); } // namespace llvm -static cl::opt<bool> - DebugPM("debug-pass-manager", cl::Hidden, - cl::desc("Print pass management debugging information")); +enum class DebugLogging { None, Normal, Verbose, Quiet }; + +static cl::opt<DebugLogging> DebugPM( + "debug-pass-manager", cl::Hidden, cl::ValueOptional, + cl::desc("Print pass management debugging information"), + cl::init(DebugLogging::None), + cl::values( + clEnumValN(DebugLogging::Normal, "", ""), + clEnumValN(DebugLogging::Quiet, "quiet", + "Skip printing info about analyses"), + clEnumValN( + DebugLogging::Verbose, "verbose", + "Print extra information about adaptors and pass managers"))); static cl::list<std::string> PassPlugins("load-pass-plugin", @@ -121,11 +131,13 @@ static cl::opt<std::string> OptimizerLastEPPipeline( // Individual pipeline tuning options. extern cl::opt<bool> DisableLoopUnrolling; +namespace llvm { extern cl::opt<PGOKind> PGOKindFlag; extern cl::opt<std::string> ProfileFile; extern cl::opt<CSPGOKind> CSPGOKindFlag; extern cl::opt<std::string> CSProfileGenFile; extern cl::opt<bool> DisableBasicAA; +} // namespace llvm static cl::opt<std::string> ProfileRemappingFile("profile-remapping-file", @@ -137,10 +149,6 @@ static cl::opt<bool> DebugInfoForProfiling( static cl::opt<bool> PseudoProbeForProfiling( "new-pm-pseudo-probe-for-profiling", cl::init(false), cl::Hidden, cl::desc("Emit pseudo probes to enable PGO profile generation.")); -static cl::opt<bool> UniqueInternalLinkageNames( - "new-pm-unique-internal-linkage-names", cl::init(false), cl::Hidden, - cl::desc("Uniqueify Internal Linkage Symbol Names by appending the MD5 " - "hash of the module path.")); /// @}} template <typename PassManagerT> @@ -235,7 +243,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, bool ShouldPreserveAssemblyUseListOrder, bool ShouldPreserveBitcodeUseListOrder, bool EmitSummaryIndex, bool EmitModuleHash, - bool EnableDebugify, bool Coroutines) { + bool EnableDebugify) { bool VerifyEachPass = VK == VK_VerifyEachPass; Optional<PGOOptions> P; @@ -279,9 +287,18 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, P->CSAction = PGOOptions::CSIRUse; } } + LoopAnalysisManager LAM; + FunctionAnalysisManager FAM; + CGSCCAnalysisManager CGAM; + ModuleAnalysisManager MAM; + PassInstrumentationCallbacks PIC; - StandardInstrumentations SI(DebugPM, VerifyEachPass); - SI.registerCallbacks(PIC); + PrintPassOptions PrintPassOpts; + PrintPassOpts.Verbose = DebugPM == DebugLogging::Verbose; + PrintPassOpts.SkipAnalyses = DebugPM == DebugLogging::Quiet; + StandardInstrumentations SI(DebugPM != DebugLogging::None, VerifyEachPass, + PrintPassOpts); + SI.registerCallbacks(PIC, &FAM); DebugifyEachInstrumentation Debugify; if (DebugifyEach) Debugify.registerCallbacks(PIC); @@ -291,9 +308,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, // to false above so we shouldn't necessarily need to check whether or not the // option has been enabled. PTO.LoopUnrolling = !DisableLoopUnrolling; - PTO.Coroutines = Coroutines; - PTO.UniqueLinkageNames = UniqueInternalLinkageNames; - PassBuilder PB(DebugPM, TM, PTO, P, &PIC); + PassBuilder PB(TM, PTO, P, &PIC); registerEPCallbacks(PB); // Load requested pass plugins and let them register pass builder callbacks @@ -378,11 +393,6 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, } } - LoopAnalysisManager LAM(DebugPM); - FunctionAnalysisManager FAM(DebugPM); - CGSCCAnalysisManager CGAM(DebugPM); - ModuleAnalysisManager MAM(DebugPM); - // Register the AA manager first so that our version is the one used. FAM.registerPass([&] { return std::move(AA); }); // Register our TargetLibraryInfoImpl. @@ -395,7 +405,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); - ModulePassManager MPM(DebugPM); + ModulePassManager MPM; if (VK > VK_NoVerifier) MPM.addPass(VerifierPass()); if (EnableDebugify) @@ -463,3 +473,8 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, return true; } + +void llvm::printPasses(raw_ostream &OS) { + PassBuilder PB; + PB.printPassNames(OS); +} diff --git a/llvm/tools/opt/NewPMDriver.h b/llvm/tools/opt/NewPMDriver.h index 87a71cec4c53..056f7d6a9b80 100644 --- a/llvm/tools/opt/NewPMDriver.h +++ b/llvm/tools/opt/NewPMDriver.h @@ -54,6 +54,8 @@ enum PGOKind { enum CSPGOKind { NoCSPGO, CSInstrGen, CSInstrUse }; } +void printPasses(raw_ostream &OS); + /// Driver function to run the new pass manager over a module. /// /// This function only exists factored away from opt.cpp in order to prevent @@ -71,7 +73,7 @@ bool runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, bool ShouldPreserveAssemblyUseListOrder, bool ShouldPreserveBitcodeUseListOrder, bool EmitSummaryIndex, bool EmitModuleHash, - bool EnableDebugify, bool Coroutines); + bool EnableDebugify); } // namespace llvm #endif diff --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp index 5cb59f85ccf8..094f517fb703 100644 --- a/llvm/tools/opt/opt.cpp +++ b/llvm/tools/opt/opt.cpp @@ -66,14 +66,15 @@ static codegen::RegisterCodeGenFlags CFG; // The OptimizationList is automatically populated with registered Passes by the // PassNameParser. -// -static cl::list<const PassInfo*, bool, PassNameParser> -PassList(cl::desc("Optimizations available:")); +static cl::list<const PassInfo *, bool, PassNameParser> PassList(cl::desc( + "Optimizations available (use '-passes=' for the new pass manager)")); -static cl::opt<bool> - EnableNewPassManager("enable-new-pm", - cl::desc("Enable the new pass manager"), - cl::init(LLVM_ENABLE_NEW_PASS_MANAGER)); +static cl::opt<bool> EnableNewPassManager( + "enable-new-pm", + cl::desc("Enable the new pass manager, translating " + "'opt -foo' to 'opt -passes=foo'. This is strictly for the new PM " + "migration, use '-passes=' when possible."), + cl::init(LLVM_ENABLE_NEW_PASS_MANAGER)); // This flag specifies a textual description of the optimization pass pipeline // to run over the module. This flag switches opt to use the new pass manager @@ -81,11 +82,14 @@ static cl::opt<bool> // pass management. static cl::opt<std::string> PassPipeline( "passes", - cl::desc("A textual description of the pass pipeline for optimizing"), - cl::Hidden); + cl::desc( + "A textual description of the pass pipeline. To have analysis passes " + "available before a certain pass, add 'require<foo-analysis>'.")); + +static cl::opt<bool> PrintPasses("print-passes", + cl::desc("Print available passes that can be " + "specified in -passes=foo and exit")); -// Other command line options... -// static cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input bitcode file>"), cl::init("-"), cl::value_desc("filename")); @@ -142,44 +146,46 @@ static cl::opt<bool> StripNamedMetadata("strip-named-metadata", cl::desc("Strip module-level named metadata")); -static cl::opt<bool> DisableInline("disable-inlining", - cl::desc("Do not run the inliner pass")); +static cl::opt<bool> + DisableInline("disable-inlining", + cl::desc("Do not run the inliner pass (legacy PM only)")); static cl::opt<bool> DisableOptimizations("disable-opt", cl::desc("Do not run any optimization passes")); -static cl::opt<bool> -StandardLinkOpts("std-link-opts", - cl::desc("Include the standard link time optimizations")); +static cl::opt<bool> StandardLinkOpts( + "std-link-opts", + cl::desc("Include the standard link time optimizations (legacy PM only)")); static cl::opt<bool> -OptLevelO0("O0", - cl::desc("Optimization level 0. Similar to clang -O0")); + OptLevelO0("O0", cl::desc("Optimization level 0. Similar to clang -O0. " + "Use -passes='default<O0>' for the new PM")); static cl::opt<bool> -OptLevelO1("O1", - cl::desc("Optimization level 1. Similar to clang -O1")); + OptLevelO1("O1", cl::desc("Optimization level 1. Similar to clang -O1. " + "Use -passes='default<O1>' for the new PM")); static cl::opt<bool> -OptLevelO2("O2", - cl::desc("Optimization level 2. Similar to clang -O2")); + OptLevelO2("O2", cl::desc("Optimization level 2. Similar to clang -O2. " + "Use -passes='default<O2>' for the new PM")); static cl::opt<bool> -OptLevelOs("Os", - cl::desc("Like -O2 with extra optimizations for size. Similar to clang -Os")); + OptLevelOs("Os", cl::desc("Like -O2 but size-conscious. Similar to clang " + "-Os. Use -passes='default<Os>' for the new PM")); -static cl::opt<bool> -OptLevelOz("Oz", - cl::desc("Like -Os but reduces code size further. Similar to clang -Oz")); +static cl::opt<bool> OptLevelOz( + "Oz", + cl::desc("Like -O2 but optimize for code size above all else. Similar to " + "clang -Oz. Use -passes='default<Oz>' for the new PM")); static cl::opt<bool> -OptLevelO3("O3", - cl::desc("Optimization level 3. Similar to clang -O3")); + OptLevelO3("O3", cl::desc("Optimization level 3. Similar to clang -O3. " + "Use -passes='default<O3>' for the new PM")); -static cl::opt<unsigned> -CodeGenOptLevel("codegen-opt-level", - cl::desc("Override optimization level for codegen hooks")); +static cl::opt<unsigned> CodeGenOptLevel( + "codegen-opt-level", + cl::desc("Override optimization level for codegen hooks, legacy PM only")); static cl::opt<std::string> TargetTriple("mtriple", cl::desc("Override target triple for module")); @@ -205,13 +211,31 @@ DisableBuiltins("disable-builtin", cl::ZeroOrMore); static cl::opt<bool> -AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization")); + AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization. " + "Legacy pass manager only.")); static cl::opt<bool> EnableDebugify( "enable-debugify", cl::desc( "Start the pipeline with debugify and end it with check-debugify")); +static cl::opt<bool> VerifyDebugInfoPreserve( + "verify-debuginfo-preserve", + cl::desc("Start the pipeline with collecting and end it with checking of " + "debug info preservation.")); + +static cl::opt<bool> VerifyEachDebugInfoPreserve( + "verify-each-debuginfo-preserve", + cl::desc("Start each pass with collecting and end it with checking of " + "debug info preservation.")); + +static cl::opt<std::string> + VerifyDIPreserveExport("verify-di-preserve-export", + cl::desc("Export debug info preservation failures into " + "specified (JSON) file (should be abs path as we use" + " append mode to insert new JSON objects)"), + cl::value_desc("filename"), cl::init("")); + static cl::opt<bool> PrintBreakpoints("print-breakpoints-for-testing", cl::desc("Print select breakpoints location for testing")); @@ -231,10 +255,10 @@ static cl::opt<bool> PreserveAssemblyUseListOrder( cl::desc("Preserve use-list order when writing LLVM assembly."), cl::init(false), cl::Hidden); -static cl::opt<bool> - RunTwice("run-twice", - cl::desc("Run all passes twice, re-using the same pass manager."), - cl::init(false), cl::Hidden); +static cl::opt<bool> RunTwice("run-twice", + cl::desc("Run all passes twice, re-using the " + "same pass manager (legacy PM only)."), + cl::init(false), cl::Hidden); static cl::opt<bool> DiscardValueNames( "discard-value-names", @@ -289,6 +313,7 @@ static cl::opt<std::string> RemarksFormat( cl::desc("The format used for serializing remarks (default: YAML)"), cl::value_desc("format"), cl::init("yaml")); +namespace llvm { cl::opt<PGOKind> PGOKindFlag("pgo-kind", cl::init(NoPGO), cl::Hidden, cl::desc("The kind of profile guided optimization"), @@ -317,6 +342,7 @@ cl::opt<std::string> CSProfileGenFile( "cs-profilegen-file", cl::desc("Path to the instrumented context sensitive profile."), cl::Hidden); +} // namespace llvm static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { // Add the pass to the pass manager... @@ -477,9 +503,8 @@ static bool shouldPinPassToLegacyPM(StringRef Pass) { "amdgpu-unify-metadata", "amdgpu-printf-runtime-binding", "amdgpu-always-inline"}; - for (const auto &P : PassNameExactToIgnore) - if (Pass == P) - return false; + if (llvm::is_contained(PassNameExactToIgnore, Pass)) + return false; std::vector<StringRef> PassNamePrefix = { "x86-", "xcore-", "wasm-", "systemz-", "ppc-", "nvvm-", "nvptx-", @@ -490,24 +515,22 @@ static bool shouldPinPassToLegacyPM(StringRef Pass) { "safe-stack", "cost-model", "codegenprepare", "interleaved-load-combine", "unreachableblockelim", "verify-safepoint-ir", - "divergence", "atomic-expand", + "atomic-expand", "expandvp", "hardware-loops", "type-promotion", "mve-tail-predication", "interleaved-access", "global-merge", "pre-isel-intrinsic-lowering", "expand-reductions", "indirectbr-expand", "generic-to-nvvm", "expandmemcmp", "loop-reduce", "lower-amx-type", - "polyhedral-info"}; + "pre-amx-config", "lower-amx-intrinsics", + "polyhedral-info", "replace-with-veclib"}; for (const auto &P : PassNamePrefix) if (Pass.startswith(P)) return true; for (const auto &P : PassNameContain) if (Pass.contains(P)) return true; - for (const auto &P : PassNameExact) - if (Pass == P) - return true; - return false; + return llvm::is_contained(PassNameExact, Pass); } // For use in NPM transition. @@ -529,8 +552,6 @@ int main(int argc, char **argv) { // Enable debug stream buffering. EnableDebugBuffering = true; - LLVMContext Context; - InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmPrinters(); @@ -570,10 +591,12 @@ int main(int argc, char **argv) { initializePostInlineEntryExitInstrumenterPass(Registry); initializeUnreachableBlockElimLegacyPassPass(Registry); initializeExpandReductionsPass(Registry); + initializeExpandVectorPredicationPass(Registry); initializeWasmEHPreparePass(Registry); initializeWriteBitcodePassPass(Registry); initializeHardwareLoopsPass(Registry); initializeTypePromotionPass(Registry); + initializeReplaceWithVeclibLegacyPass(Registry); #ifdef BUILD_EXAMPLES initializeExampleIRTransforms(Registry); @@ -582,11 +605,21 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .bc modular optimizer and analysis printer\n"); + LLVMContext Context; + if (AnalyzeOnly && NoOutput) { errs() << argv[0] << ": analyze mode conflicts with no-output mode.\n"; return 1; } + // FIXME: once the legacy PM code is deleted, move runPassPipeline() here and + // construct the PassBuilder before parsing IR so we can reuse the same + // PassBuilder for print passes. + if (PrintPasses) { + printPasses(outs()); + return 0; + } + TimeTracerRAII TimeTracer(argv[0]); SMDiagnostic Err; @@ -654,7 +687,8 @@ int main(int argc, char **argv) { // specified by an internal option. This is normally done during LTO which is // not performed via opt. updateVCallVisibilityInModule(*M, - /* WholeProgramVisibilityEnabledInLTO */ false); + /* WholeProgramVisibilityEnabledInLTO */ false, + /* DynamicExportSymbols */ {}); // Figure out what stream we are supposed to write to... std::unique_ptr<ToolOutputFile> Out; @@ -669,8 +703,8 @@ int main(int argc, char **argv) { OutputFilename = "-"; std::error_code EC; - sys::fs::OpenFlags Flags = OutputAssembly ? sys::fs::OF_Text - : sys::fs::OF_None; + sys::fs::OpenFlags Flags = + OutputAssembly ? sys::fs::OF_TextWithCRLF : sys::fs::OF_None; Out.reset(new ToolOutputFile(OutputFilename, EC, Flags)); if (EC) { errs() << EC.message() << '\n'; @@ -746,7 +780,16 @@ int main(int argc, char **argv) { if ((EnableNewPassManager && !shouldForceLegacyPM()) || PassPipeline.getNumOccurrences() > 0) { if (AnalyzeOnly) { - errs() << "Cannot specify -analyze under new pass manager\n"; + errs() << "Cannot specify -analyze under new pass manager, either " + "specify '-enable-new-pm=0', or use the corresponding new pass " + "manager pass, e.g. '-passes=print<scalar-evolution>'. For a " + "full list of passes, see the '--print-passes' flag.\n"; + return 1; + } + if (legacy::debugPassSpecified()) { + errs() + << "-debug-pass does not work with the new PM, either use " + "-debug-pass-manager, or use the legacy PM (-enable-new-pm=0)\n"; return 1; } if (PassPipeline.getNumOccurrences() > 0 && PassList.size() > 0) { @@ -788,7 +831,7 @@ int main(int argc, char **argv) { ThinLinkOut.get(), RemarksFile.get(), PassPipeline, Passes, OK, VK, PreserveAssemblyUseListOrder, PreserveBitcodeUseListOrder, EmitSummaryIndex, - EmitModuleHash, EnableDebugify, Coroutines) + EmitModuleHash, EnableDebugify) ? 0 : 1; } @@ -797,10 +840,21 @@ int main(int argc, char **argv) { // about to build. If the -debugify-each option is set, wrap each pass with // the (-check)-debugify passes. DebugifyCustomPassManager Passes; - if (DebugifyEach) - Passes.enableDebugifyEach(); + DebugifyStatsMap DIStatsMap; + DebugInfoPerPassMap DIPreservationMap; + if (DebugifyEach) { + Passes.setDebugifyMode(DebugifyMode::SyntheticDebugInfo); + Passes.setDIStatsMap(DIStatsMap); + } else if (VerifyEachDebugInfoPreserve) { + Passes.setDebugifyMode(DebugifyMode::OriginalDebugInfo); + Passes.setDIPreservationMap(DIPreservationMap); + if (!VerifyDIPreserveExport.empty()) + Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport); + } - bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach; + bool AddOneTimeDebugifyPasses = + (EnableDebugify && !DebugifyEach) || + (VerifyDebugInfoPreserve && !VerifyEachDebugInfoPreserve); Passes.add(new TargetLibraryInfoWrapperPass(TLII)); @@ -808,8 +862,17 @@ int main(int argc, char **argv) { Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis() : TargetIRAnalysis())); - if (AddOneTimeDebugifyPasses) - Passes.add(createDebugifyModulePass()); + if (AddOneTimeDebugifyPasses) { + if (EnableDebugify) { + Passes.setDIStatsMap(DIStatsMap); + Passes.add(createDebugifyModulePass()); + } else if (VerifyDebugInfoPreserve) { + Passes.setDIPreservationMap(DIPreservationMap); + Passes.add(createDebugifyModulePass( + DebugifyMode::OriginalDebugInfo, "", + &(Passes.getDebugInfoPerPassMap()))); + } + } std::unique_ptr<legacy::FunctionPassManager> FPasses; if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz || @@ -953,8 +1016,17 @@ int main(int argc, char **argv) { if (!NoVerify && !VerifyEach) Passes.add(createVerifierPass()); - if (AddOneTimeDebugifyPasses) - Passes.add(createCheckDebugifyModulePass(false)); + if (AddOneTimeDebugifyPasses) { + if (EnableDebugify) + Passes.add(createCheckDebugifyModulePass(false)); + else if (VerifyDebugInfoPreserve) { + if (!VerifyDIPreserveExport.empty()) + Passes.setOrigDIVerifyBugsReportFilePath(VerifyDIPreserveExport); + Passes.add(createCheckDebugifyModulePass( + false, "", nullptr, DebugifyMode::OriginalDebugInfo, + &(Passes.getDebugInfoPerPassMap()), VerifyDIPreserveExport)); + } + } // In run twice mode, we want to make sure the output is bit-by-bit // equivalent if we run the pass manager again, so setup two buffers and |