diff options
Diffstat (limited to 'contrib/llvm/tools')
230 files changed, 12548 insertions, 6685 deletions
diff --git a/contrib/llvm/tools/bugpoint/BugDriver.cpp b/contrib/llvm/tools/bugpoint/BugDriver.cpp index 3832e075a693..942028cad80b 100644 --- a/contrib/llvm/tools/bugpoint/BugDriver.cpp +++ b/contrib/llvm/tools/bugpoint/BugDriver.cpp @@ -1,9 +1,8 @@ //===- BugDriver.cpp - Top-Level BugPoint class implementation ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/bugpoint/BugDriver.h b/contrib/llvm/tools/bugpoint/BugDriver.h index bc60ae753548..75f166b21b2c 100644 --- a/contrib/llvm/tools/bugpoint/BugDriver.h +++ b/contrib/llvm/tools/bugpoint/BugDriver.h @@ -1,9 +1,8 @@ //===- BugDriver.h - Top-Level BugPoint class -------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/bugpoint/CrashDebugger.cpp b/contrib/llvm/tools/bugpoint/CrashDebugger.cpp index ef6a214fde20..aab9debf9b59 100644 --- a/contrib/llvm/tools/bugpoint/CrashDebugger.cpp +++ b/contrib/llvm/tools/bugpoint/CrashDebugger.cpp @@ -1,9 +1,8 @@ //===- CrashDebugger.cpp - Debug compilation crashes ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/bugpoint/ExecutionDriver.cpp b/contrib/llvm/tools/bugpoint/ExecutionDriver.cpp index 1b86b103d835..40f198b88d1a 100644 --- a/contrib/llvm/tools/bugpoint/ExecutionDriver.cpp +++ b/contrib/llvm/tools/bugpoint/ExecutionDriver.cpp @@ -1,9 +1,8 @@ //===- ExecutionDriver.cpp - Allow execution of LLVM program --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/bugpoint/ExtractFunction.cpp b/contrib/llvm/tools/bugpoint/ExtractFunction.cpp index 48f1575c25eb..105702de3f1d 100644 --- a/contrib/llvm/tools/bugpoint/ExtractFunction.cpp +++ b/contrib/llvm/tools/bugpoint/ExtractFunction.cpp @@ -1,9 +1,8 @@ //===- ExtractFunction.cpp - Extract a function from Program --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/bugpoint/FindBugs.cpp b/contrib/llvm/tools/bugpoint/FindBugs.cpp index a695e875b787..2b1146da9680 100644 --- a/contrib/llvm/tools/bugpoint/FindBugs.cpp +++ b/contrib/llvm/tools/bugpoint/FindBugs.cpp @@ -1,9 +1,8 @@ //===-- FindBugs.cpp - Run Many Different Optimizations -------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/bugpoint/ListReducer.h b/contrib/llvm/tools/bugpoint/ListReducer.h index 0f9db022d555..04f2207a31ed 100644 --- a/contrib/llvm/tools/bugpoint/ListReducer.h +++ b/contrib/llvm/tools/bugpoint/ListReducer.h @@ -1,9 +1,8 @@ //===- ListReducer.h - Trim down list while retaining property --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/bugpoint/Miscompilation.cpp b/contrib/llvm/tools/bugpoint/Miscompilation.cpp index 375bee7a0d50..1621a51c91d6 100644 --- a/contrib/llvm/tools/bugpoint/Miscompilation.cpp +++ b/contrib/llvm/tools/bugpoint/Miscompilation.cpp @@ -1,9 +1,8 @@ //===- Miscompilation.cpp - Debug program miscompilations -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -592,9 +591,6 @@ ExtractBlocks(BugDriver &BD, if (Linker::linkModules(*ProgClone, std::move(Extracted))) exit(1); - // Set the new program and delete the old one. - BD.setNewProgram(std::move(ProgClone)); - // Update the list of miscompiled functions. MiscompiledFunctions.clear(); @@ -604,6 +600,9 @@ ExtractBlocks(BugDriver &BD, MiscompiledFunctions.push_back(NewF); } + // Set the new program and delete the old one. + BD.setNewProgram(std::move(ProgClone)); + return true; } @@ -706,8 +705,8 @@ static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test, if (!Optimized) { errs() << " Error running this sequence of passes" << " on the input program!\n"; - BD.setNewProgram(std::move(Test)); BD.EmitProgressBitcode(*Test, "pass-error", false); + BD.setNewProgram(std::move(Test)); if (Error E = BD.debugOptimizerCrash()) return std::move(E); return false; @@ -827,13 +826,14 @@ CleanupAndPrepareModules(BugDriver &BD, std::unique_ptr<Module> Test, // Add the resolver to the Safe module. // Prototype: void *getPointerToNamedFunction(const char* Name) - Constant *resolverFunc = Safe->getOrInsertFunction( + FunctionCallee resolverFunc = Safe->getOrInsertFunction( "getPointerToNamedFunction", Type::getInt8PtrTy(Safe->getContext()), Type::getInt8PtrTy(Safe->getContext())); // Use the function we just added to get addresses of functions we need. for (Module::iterator F = Safe->begin(), E = Safe->end(); F != E; ++F) { - if (F->isDeclaration() && !F->use_empty() && &*F != resolverFunc && + if (F->isDeclaration() && !F->use_empty() && + &*F != resolverFunc.getCallee() && !F->isIntrinsic() /* ignore intrinsics */) { Function *TestFn = Test->getFunction(F->getName()); @@ -879,7 +879,8 @@ CleanupAndPrepareModules(BugDriver &BD, std::unique_ptr<Module> Test, BasicBlock::Create(F->getContext(), "lookupfp", FuncWrapper); // Check to see if we already looked up the value. - Value *CachedVal = new LoadInst(Cache, "fpcache", EntryBB); + Value *CachedVal = + new LoadInst(F->getType(), Cache, "fpcache", EntryBB); Value *IsNull = new ICmpInst(*EntryBB, ICmpInst::ICMP_EQ, CachedVal, NullPtr, "isNull"); BranchInst::Create(LookupBB, DoCallBB, IsNull, EntryBB); @@ -911,11 +912,11 @@ CleanupAndPrepareModules(BugDriver &BD, std::unique_ptr<Module> Test, // Pass on the arguments to the real function, return its result if (F->getReturnType()->isVoidTy()) { - CallInst::Create(FuncPtr, Args, "", DoCallBB); + CallInst::Create(FuncTy, FuncPtr, Args, "", DoCallBB); ReturnInst::Create(F->getContext(), DoCallBB); } else { CallInst *Call = - CallInst::Create(FuncPtr, Args, "retval", DoCallBB); + CallInst::Create(FuncTy, FuncPtr, Args, "retval", DoCallBB); ReturnInst::Create(F->getContext(), Call, DoCallBB); } diff --git a/contrib/llvm/tools/bugpoint/OptimizerDriver.cpp b/contrib/llvm/tools/bugpoint/OptimizerDriver.cpp index 64fe675de20c..562de7952388 100644 --- a/contrib/llvm/tools/bugpoint/OptimizerDriver.cpp +++ b/contrib/llvm/tools/bugpoint/OptimizerDriver.cpp @@ -1,9 +1,8 @@ //===- OptimizerDriver.cpp - Allow BugPoint to run passes safely ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/bugpoint/ToolRunner.cpp b/contrib/llvm/tools/bugpoint/ToolRunner.cpp index 7ba8ea1f16c5..da4244345e3b 100644 --- a/contrib/llvm/tools/bugpoint/ToolRunner.cpp +++ b/contrib/llvm/tools/bugpoint/ToolRunner.cpp @@ -1,9 +1,8 @@ //===-- ToolRunner.cpp ----------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/bugpoint/ToolRunner.h b/contrib/llvm/tools/bugpoint/ToolRunner.h index ef8551cc669b..dde4ec539cfb 100644 --- a/contrib/llvm/tools/bugpoint/ToolRunner.h +++ b/contrib/llvm/tools/bugpoint/ToolRunner.h @@ -1,9 +1,8 @@ //===-- tools/bugpoint/ToolRunner.h -----------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/bugpoint/bugpoint.cpp b/contrib/llvm/tools/bugpoint/bugpoint.cpp index f6b7d08455d4..2d5322a351ad 100644 --- a/contrib/llvm/tools/bugpoint/bugpoint.cpp +++ b/contrib/llvm/tools/bugpoint/bugpoint.cpp @@ -1,9 +1,8 @@ //===- bugpoint.cpp - The LLVM Bugpoint utility ---------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llc/llc.cpp b/contrib/llvm/tools/llc/llc.cpp index 2329fb3e87c9..76da843f065e 100644 --- a/contrib/llvm/tools/llc/llc.cpp +++ b/contrib/llvm/tools/llc/llc.cpp @@ -1,9 +1,8 @@ //===-- llc.cpp - Implement the LLVM Native Code Generator ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -32,6 +31,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" +#include "llvm/IR/RemarkStreamer.h" #include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" #include "llvm/MC/SubtargetFeature.h" @@ -133,21 +133,33 @@ static cl::opt<bool> DiscardValueNames( static cl::list<std::string> IncludeDirs("I", cl::desc("include search path")); -static cl::opt<bool> PassRemarksWithHotness( +static cl::opt<bool> RemarksWithHotness( "pass-remarks-with-hotness", cl::desc("With PGO, include profile count in optimization remarks"), cl::Hidden); -static cl::opt<unsigned> PassRemarksHotnessThreshold( - "pass-remarks-hotness-threshold", - cl::desc("Minimum profile count required for an optimization remark to be output"), - cl::Hidden); +static cl::opt<unsigned> + RemarksHotnessThreshold("pass-remarks-hotness-threshold", + cl::desc("Minimum profile count required for " + "an optimization remark to be output"), + cl::Hidden); static cl::opt<std::string> RemarksFilename("pass-remarks-output", - cl::desc("YAML output filename for pass remarks"), + cl::desc("Output filename for pass remarks"), cl::value_desc("filename")); +static cl::opt<std::string> + RemarksPasses("pass-remarks-filter", + cl::desc("Only record optimization remarks from passes whose " + "names match the given regular expression"), + cl::value_desc("regex")); + +static cl::opt<std::string> RemarksFormat( + "pass-remarks-format", + cl::desc("The format used for serializing remarks (default: YAML)"), + cl::value_desc("format"), cl::init("yaml")); + namespace { static ManagedStatic<std::vector<std::string>> RunPassNames; @@ -302,6 +314,7 @@ int main(int argc, char **argv) { initializeVectorization(*Registry); initializeScalarizeMaskedMemIntrinPass(*Registry); initializeExpandReductionsPass(*Registry); + initializeHardwareLoopsPass(*Registry); // Initialize debugging passes. initializeScavengerTestPass(*Registry); @@ -319,24 +332,15 @@ int main(int argc, char **argv) { llvm::make_unique<LLCDiagnosticHandler>(&HasError)); Context.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, &HasError); - if (PassRemarksWithHotness) - Context.setDiagnosticsHotnessRequested(true); - - if (PassRemarksHotnessThreshold) - Context.setDiagnosticsHotnessThreshold(PassRemarksHotnessThreshold); - - std::unique_ptr<ToolOutputFile> YamlFile; - if (RemarksFilename != "") { - std::error_code EC; - YamlFile = - llvm::make_unique<ToolOutputFile>(RemarksFilename, EC, sys::fs::F_None); - if (EC) { - WithColor::error(errs(), argv[0]) << EC.message() << '\n'; - return 1; - } - Context.setDiagnosticsOutputFile( - llvm::make_unique<yaml::Output>(YamlFile->os())); + Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr = + setupOptimizationRemarks(Context, RemarksFilename, RemarksPasses, + RemarksFormat, RemarksWithHotness, + RemarksHotnessThreshold); + if (Error E = RemarksFileOrErr.takeError()) { + WithColor::error(errs(), argv[0]) << toString(std::move(E)) << '\n'; + return 1; } + std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr); if (InputLanguage != "" && InputLanguage != "ir" && InputLanguage != "mir") { @@ -351,8 +355,8 @@ int main(int argc, char **argv) { if (int RetVal = compileModule(argv, Context)) return RetVal; - if (YamlFile) - YamlFile->keep(); + if (RemarksFile) + RemarksFile->keep(); return 0; } diff --git a/contrib/llvm/tools/lli/RemoteJITUtils.h b/contrib/llvm/tools/lli/RemoteJITUtils.h index 944881070c70..8e80e73c8082 100644 --- a/contrib/llvm/tools/lli/RemoteJITUtils.h +++ b/contrib/llvm/tools/lli/RemoteJITUtils.h @@ -1,9 +1,8 @@ //===-- RemoteJITUtils.h - Utilities for remote-JITing with LLI -*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -76,7 +75,7 @@ std::unique_ptr<FDRawChannel> launchRemote(); namespace llvm { -// ForwardingMM - Adapter to connect MCJIT to Orc's Remote8 +// ForwardingMM - Adapter to connect MCJIT to Orc's Remote // memory manager. class ForwardingMemoryManager : public llvm::RTDyldMemoryManager { public: diff --git a/contrib/llvm/tools/lli/lli.cpp b/contrib/llvm/tools/lli/lli.cpp index 7e93d31361aa..8c8cd88c9711 100644 --- a/contrib/llvm/tools/lli/lli.cpp +++ b/contrib/llvm/tools/lli/lli.cpp @@ -1,9 +1,8 @@ //===- lli.cpp - LLVM Interpreter / Dynamic compiler ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -84,18 +83,15 @@ namespace { cl::desc("Force interpretation: disable JIT"), cl::init(false)); - cl::opt<JITKind> UseJITKind("jit-kind", - cl::desc("Choose underlying JIT kind."), - cl::init(JITKind::MCJIT), - cl::values( - clEnumValN(JITKind::MCJIT, "mcjit", - "MCJIT"), - clEnumValN(JITKind::OrcMCJITReplacement, - "orc-mcjit", - "Orc-based MCJIT replacement"), - clEnumValN(JITKind::OrcLazy, - "orc-lazy", - "Orc-based lazy JIT."))); + cl::opt<JITKind> UseJITKind( + "jit-kind", cl::desc("Choose underlying JIT kind."), + cl::init(JITKind::MCJIT), + cl::values(clEnumValN(JITKind::MCJIT, "mcjit", "MCJIT"), + clEnumValN(JITKind::OrcMCJITReplacement, "orc-mcjit", + "Orc-based MCJIT replacement " + "(deprecated)"), + clEnumValN(JITKind::OrcLazy, "orc-lazy", + "Orc-based lazy JIT."))); cl::opt<unsigned> LazyJITCompileThreads("compile-threads", @@ -173,7 +169,7 @@ namespace { cl::opt<bool> EnableCacheManager("enable-cache-manager", - cl::desc("Use cache manager to save/load mdoules"), + cl::desc("Use cache manager to save/load modules"), cl::init(false)); cl::opt<std::string> @@ -420,7 +416,8 @@ int main(int argc, char **argv, char * const *envp) { builder.setEngineKind(ForceInterpreter ? EngineKind::Interpreter : EngineKind::JIT); - builder.setUseOrcMCJITReplacement(UseJITKind == JITKind::OrcMCJITReplacement); + builder.setUseOrcMCJITReplacement(AcknowledgeORCv1Deprecation, + UseJITKind == JITKind::OrcMCJITReplacement); // If we are supposed to override the target triple, do so now. if (!TargetTriple.empty()) @@ -596,8 +593,8 @@ int main(int argc, char **argv, char * const *envp) { if (!RemoteMCJIT) { // If the program doesn't explicitly call exit, we will need the Exit // function later on to make an explicit call, so get the function now. - Constant *Exit = Mod->getOrInsertFunction("exit", Type::getVoidTy(Context), - Type::getInt32Ty(Context)); + FunctionCallee Exit = Mod->getOrInsertFunction( + "exit", Type::getVoidTy(Context), Type::getInt32Ty(Context)); // Run static constructors. if (!ForceInterpreter) { @@ -621,19 +618,21 @@ int main(int argc, char **argv, char * const *envp) { // If the program didn't call exit explicitly, we should call it now. // This ensures that any atexit handlers get called correctly. - if (Function *ExitF = dyn_cast<Function>(Exit)) { - std::vector<GenericValue> Args; - GenericValue ResultGV; - ResultGV.IntVal = APInt(32, Result); - Args.push_back(ResultGV); - EE->runFunction(ExitF, Args); - WithColor::error(errs(), argv[0]) << "exit(" << Result << ") returned!\n"; - abort(); - } else { - WithColor::error(errs(), argv[0]) - << "exit defined with wrong prototype!\n"; - abort(); + if (Function *ExitF = + dyn_cast<Function>(Exit.getCallee()->stripPointerCasts())) { + if (ExitF->getFunctionType() == Exit.getFunctionType()) { + std::vector<GenericValue> Args; + GenericValue ResultGV; + ResultGV.IntVal = APInt(32, Result); + Args.push_back(ResultGV); + EE->runFunction(ExitF, Args); + WithColor::error(errs(), argv[0]) + << "exit(" << Result << ") returned!\n"; + abort(); + } } + WithColor::error(errs(), argv[0]) << "exit defined with wrong prototype!\n"; + abort(); } else { // else == "if (RemoteMCJIT)" @@ -664,6 +663,7 @@ int main(int argc, char **argv, char * const *envp) { // Forward MCJIT's symbol resolution calls to the remote. static_cast<ForwardingMemoryManager *>(RTDyldMM)->setResolver( orc::createLambdaResolver( + AcknowledgeORCv1Deprecation, [](const std::string &Name) { return nullptr; }, [&](const std::string &Name) { if (auto Addr = ExitOnErr(R->getSymbolAddress(Name))) @@ -762,14 +762,17 @@ int runOrcLazyJIT(const char *ProgName) { reportError(Err, ProgName); const auto &TT = MainModule.getModule()->getTargetTriple(); - orc::JITTargetMachineBuilder JTMB = + orc::LLLazyJITBuilder Builder; + + Builder.setJITTargetMachineBuilder( TT.empty() ? ExitOnErr(orc::JITTargetMachineBuilder::detectHost()) - : orc::JITTargetMachineBuilder(Triple(TT)); + : orc::JITTargetMachineBuilder(Triple(TT))); if (!MArch.empty()) - JTMB.getTargetTriple().setArchName(MArch); + Builder.getJITTargetMachineBuilder()->getTargetTriple().setArchName(MArch); - JTMB.setCPU(getCPUStr()) + Builder.getJITTargetMachineBuilder() + ->setCPU(getCPUStr()) .addFeatures(getFeatureList()) .setRelocationModel(RelocModel.getNumOccurrences() ? Optional<Reloc::Model>(RelocModel) @@ -778,12 +781,11 @@ int runOrcLazyJIT(const char *ProgName) { ? Optional<CodeModel::Model>(CMModel) : None); - DataLayout DL = ExitOnErr(JTMB.getDefaultDataLayoutForTarget()); + Builder.setLazyCompileFailureAddr( + pointerToJITTargetAddress(exitOnLazyCallThroughFailure)); + Builder.setNumCompileThreads(LazyJITCompileThreads); - auto J = ExitOnErr(orc::LLLazyJIT::Create( - std::move(JTMB), DL, - pointerToJITTargetAddress(exitOnLazyCallThroughFailure), - LazyJITCompileThreads)); + auto J = ExitOnErr(Builder.create()); if (PerModuleLazy) J->setPartitionFunction(orc::CompileOnDemandLayer::compileWholeModule); @@ -799,9 +801,10 @@ int runOrcLazyJIT(const char *ProgName) { return Dump(std::move(TSM), R); }); J->getMainJITDylib().setGenerator( - ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(DL))); + ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( + J->getDataLayout().getGlobalPrefix()))); - orc::MangleAndInterner Mangle(J->getExecutionSession(), DL); + orc::MangleAndInterner Mangle(J->getExecutionSession(), J->getDataLayout()); orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides; ExitOnErr(CXXRuntimeOverrides.enable(J->getMainJITDylib(), Mangle)); @@ -817,8 +820,10 @@ int runOrcLazyJIT(const char *ProgName) { IdxToDylib[0] = &J->getMainJITDylib(); for (auto JDItr = JITDylibs.begin(), JDEnd = JITDylibs.end(); JDItr != JDEnd; ++JDItr) { - IdxToDylib[JITDylibs.getPosition(JDItr - JITDylibs.begin())] = - &J->createJITDylib(*JDItr); + orc::JITDylib *JD = J->getJITDylibByName(*JDItr); + if (!JD) + JD = &J->createJITDylib(*JDItr); + IdxToDylib[JITDylibs.getPosition(JDItr - JITDylibs.begin())] = JD; } for (auto EMItr = ExtraModules.begin(), EMEnd = ExtraModules.end(); @@ -861,8 +866,6 @@ int runOrcLazyJIT(const char *ProgName) { AltEntryThreads.push_back(std::thread([EntryPoint]() { EntryPoint(); })); } - J->getExecutionSession().dump(llvm::dbgs()); - // Run main. auto MainSym = ExitOnErr(J->lookup("main")); typedef int (*MainFnPtr)(int, const char *[]); diff --git a/contrib/llvm/tools/llvm-ar/llvm-ar.cpp b/contrib/llvm/tools/llvm-ar/llvm-ar.cpp index 1c453ee0b569..91746d0fab37 100644 --- a/contrib/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/contrib/llvm/tools/llvm-ar/llvm-ar.cpp @@ -1,9 +1,8 @@ //===-- llvm-ar.cpp - LLVM archive librarian utility ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -67,7 +66,7 @@ OPTIONS: const char ArHelp[] = R"( OVERVIEW: LLVM Archiver -USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] <archive> [files] +USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] [count] <archive> [files] llvm-ar -M [<mri-script] OPTIONS: @@ -79,6 +78,7 @@ OPTIONS: --plugin=<string> - Ignored for compatibility --help - Display available options --version - Display the version of this program + @<file> - read options from <file> OPERATIONS: d - delete [files] from the archive @@ -98,7 +98,9 @@ MODIFIERS: [i] - put [files] before [relpos] (same as [b]) [l] - ignored for compatibility [L] - add archive's contents + [N] - use instance [count] of name [o] - preserve original dates + [P] - use full names when matching (implied for thin archives) [s] - create an archive index (cf. ranlib) [S] - do not build a symbol table [T] - create a thin archive @@ -169,16 +171,17 @@ enum ArchiveOperation { }; // Modifiers to follow operation to vary behavior -static bool AddAfter = false; ///< 'a' modifier -static bool AddBefore = false; ///< 'b' modifier -static bool Create = false; ///< 'c' modifier -static bool OriginalDates = false; ///< 'o' modifier -static bool OnlyUpdate = false; ///< 'u' modifier -static bool Verbose = false; ///< 'v' modifier -static bool Symtab = true; ///< 's' modifier -static bool Deterministic = true; ///< 'D' and 'U' modifiers -static bool Thin = false; ///< 'T' modifier -static bool AddLibrary = false; ///< 'L' modifier +static bool AddAfter = false; ///< 'a' modifier +static bool AddBefore = false; ///< 'b' modifier +static bool Create = false; ///< 'c' modifier +static bool OriginalDates = false; ///< 'o' modifier +static bool CompareFullPath = false; ///< 'P' modifier +static bool OnlyUpdate = false; ///< 'u' modifier +static bool Verbose = false; ///< 'v' modifier +static bool Symtab = true; ///< 's' modifier +static bool Deterministic = true; ///< 'D' and 'U' modifiers +static bool Thin = false; ///< 'T' modifier +static bool AddLibrary = false; ///< 'L' modifier // Relative Positional Argument (for insert/move). This variable holds // the name of the archive member to which the 'a', 'b' or 'i' modifier @@ -186,6 +189,11 @@ static bool AddLibrary = false; ///< 'L' modifier // one variable. static std::string RelPos; +// Count parameter for 'N' modifier. This variable specifies which file should +// match for extract/delete operations when there are multiple matches. This is +// 1-indexed. A value of 0 is invalid, and implies 'N' is not used. +static int CountParam = 0; + // This variable holds the name of the archive file as given on the // command line. static std::string ArchiveName; @@ -194,6 +202,9 @@ static std::string ArchiveName; // on the command line. static std::vector<StringRef> Members; +// Static buffer to hold StringRefs. +static BumpPtrAllocator Alloc; + // Extract the member filename from the command line for the [relpos] argument // associated with a, b, and i modifiers static void getRelPos() { @@ -203,6 +214,19 @@ static void getRelPos() { PositionalArgs.erase(PositionalArgs.begin()); } +// Extract the parameter from the command line for the [count] argument +// associated with the N modifier +static void getCountParam() { + if (PositionalArgs.empty()) + fail("Expected [count] for N modifier"); + auto CountParamArg = StringRef(PositionalArgs[0]); + if (CountParamArg.getAsInteger(10, CountParam)) + fail("Value for [count] must be numeric, got: " + CountParamArg); + if (CountParam < 1) + fail("Value for [count] must be positive, got: " + CountParamArg); + PositionalArgs.erase(PositionalArgs.begin()); +} + // Get the archive file name from the command line static void getArchive() { if (PositionalArgs.empty()) @@ -295,6 +319,9 @@ static ArchiveOperation parseCommandLine() { case 'o': OriginalDates = true; break; + case 'P': + CompareFullPath = true; + break; case 's': Symtab = true; MaybeJustCreateSymTab = true; @@ -329,8 +356,13 @@ static ArchiveOperation parseCommandLine() { case 'U': Deterministic = false; break; + case 'N': + getCountParam(); + break; case 'T': Thin = true; + // Thin archives store path names, so P should be forced. + CompareFullPath = true; break; case 'L': AddLibrary = true; @@ -362,11 +394,14 @@ static ArchiveOperation parseCommandLine() { fail("Only one operation may be specified"); if (NumPositional > 1) fail("You may only specify one of a, b, and i modifiers"); - if (AddAfter || AddBefore) { + if (AddAfter || AddBefore) if (Operation != Move && Operation != ReplaceOrInsert) fail("The 'a', 'b' and 'i' modifiers can only be specified with " "the 'm' or 'r' operations"); - } + if (CountParam) + if (Operation != Extract && Operation != Delete) + fail("The 'N' modifier can only be specified with the 'x' or 'd' " + "operations"); if (OriginalDates && Operation != Extract) fail("The 'o' modifier is only applicable to the 'x' operation"); if (OnlyUpdate && Operation != ReplaceOrInsert) @@ -430,12 +465,19 @@ static void doDisplayTable(StringRef Name, const object::Archive::Child &C) { } if (C.getParent()->isThin()) { - outs() << sys::path::parent_path(ArchiveName); - outs() << '/'; + if (!sys::path::is_absolute(Name)) { + StringRef ParentDir = sys::path::parent_path(ArchiveName); + if (!ParentDir.empty()) + outs() << sys::path::convert_to_slash(ParentDir) << '/'; + } } outs() << Name << "\n"; } +static StringRef normalizePath(StringRef Path) { + return CompareFullPath ? Path : sys::path::filename(Path); +} + // Implement the 'x' operation. This function extracts files back to the file // system. static void doExtract(StringRef Name, const object::Archive::Child &C) { @@ -499,6 +541,7 @@ static void performReadOperation(ArchiveOperation Operation, fail("extracting from a thin archive is not supported"); bool Filter = !Members.empty(); + StringMap<int> MemberCount; { Error Err = Error::success(); for (auto &C : OldArchive->children(Err)) { @@ -507,9 +550,13 @@ static void performReadOperation(ArchiveOperation Operation, StringRef Name = NameOrErr.get(); if (Filter) { - auto I = find(Members, Name); + auto I = find_if(Members, [Name](StringRef Path) { + return Name == normalizePath(Path); + }); if (I == Members.end()) continue; + if (CountParam && ++MemberCount[Name] != CountParam) + continue; Members.erase(I); } @@ -545,6 +592,23 @@ static void addChildMember(std::vector<NewArchiveMember> &Members, Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getOldMember(M, Deterministic); failIfError(NMOrErr.takeError()); + // If the child member we're trying to add is thin, use the path relative to + // the archive it's in, so the file resolves correctly. + if (Thin && FlattenArchive) { + StringSaver Saver(Alloc); + Expected<std::string> FileNameOrErr = M.getName(); + failIfError(FileNameOrErr.takeError()); + if (sys::path::is_absolute(*FileNameOrErr)) { + NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(*FileNameOrErr)); + } else { + FileNameOrErr = M.getFullName(); + failIfError(FileNameOrErr.takeError()); + Expected<std::string> PathOrErr = + computeArchiveRelativePath(ArchiveName, *FileNameOrErr); + NMOrErr->MemberName = Saver.save( + PathOrErr ? *PathOrErr : sys::path::convert_to_slash(*FileNameOrErr)); + } + } if (FlattenArchive && identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { Expected<std::string> FileNameOrErr = M.getFullName(); @@ -568,6 +632,23 @@ static void addMember(std::vector<NewArchiveMember> &Members, Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getFile(FileName, Deterministic); failIfError(NMOrErr.takeError(), FileName); + StringSaver Saver(Alloc); + // For regular archives, use the basename of the object path for the member + // name. For thin archives, use the full relative paths so the file resolves + // correctly. + if (!Thin) { + NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); + } else { + if (sys::path::is_absolute(FileName)) + NMOrErr->MemberName = Saver.save(sys::path::convert_to_slash(FileName)); + else { + Expected<std::string> PathOrErr = + computeArchiveRelativePath(ArchiveName, FileName); + NMOrErr->MemberName = Saver.save( + PathOrErr ? *PathOrErr : sys::path::convert_to_slash(FileName)); + } + } + if (FlattenArchive && identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { object::Archive &Lib = readLibrary(FileName); @@ -581,8 +662,6 @@ static void addMember(std::vector<NewArchiveMember> &Members, return; } } - // Use the basename of the object path for the member name. - NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); Members.push_back(std::move(*NMOrErr)); } @@ -597,27 +676,29 @@ enum InsertAction { static InsertAction computeInsertAction(ArchiveOperation Operation, const object::Archive::Child &Member, StringRef Name, - std::vector<StringRef>::iterator &Pos) { + std::vector<StringRef>::iterator &Pos, + StringMap<int> &MemberCount) { if (Operation == QuickAppend || Members.empty()) return IA_AddOldMember; - - auto MI = find_if(Members, [Name](StringRef Path) { - return Name == sys::path::filename(Path); - }); + auto MI = find_if( + Members, [Name](StringRef Path) { return Name == normalizePath(Path); }); if (MI == Members.end()) return IA_AddOldMember; Pos = MI; - if (Operation == Delete) + if (Operation == Delete) { + if (CountParam && ++MemberCount[Name] != CountParam) + return IA_AddOldMember; return IA_Delete; + } if (Operation == Move) return IA_MoveOldMember; if (Operation == ReplaceOrInsert) { - StringRef PosName = sys::path::filename(RelPos); + StringRef PosName = normalizePath(RelPos); if (!OnlyUpdate) { if (PosName.empty()) return IA_AddNewMember; @@ -651,9 +732,10 @@ computeNewArchiveMembers(ArchiveOperation Operation, std::vector<NewArchiveMember> Ret; std::vector<NewArchiveMember> Moved; int InsertPos = -1; - StringRef PosName = sys::path::filename(RelPos); + StringRef PosName = normalizePath(RelPos); if (OldArchive) { Error Err = Error::success(); + StringMap<int> MemberCount; for (auto &Child : OldArchive->children(Err)) { int Pos = Ret.size(); Expected<StringRef> NameOrErr = Child.getName(); @@ -669,10 +751,10 @@ computeNewArchiveMembers(ArchiveOperation Operation, std::vector<StringRef>::iterator MemberI = Members.end(); InsertAction Action = - computeInsertAction(Operation, Child, Name, MemberI); + computeInsertAction(Operation, Child, Name, MemberI, MemberCount); switch (Action) { case IA_AddOldMember: - addChildMember(Ret, Child); + addChildMember(Ret, Child, /*FlattenArchive=*/Thin); break; case IA_AddNewMember: addMember(Ret, *MemberI); @@ -680,13 +762,18 @@ computeNewArchiveMembers(ArchiveOperation Operation, case IA_Delete: break; case IA_MoveOldMember: - addChildMember(Moved, Child); + addChildMember(Moved, Child, /*FlattenArchive=*/Thin); break; case IA_MoveNewMember: addMember(Moved, *MemberI); break; } - if (MemberI != Members.end()) + // When processing elements with the count param, we need to preserve the + // full members list when iterating over all archive members. For + // instance, "llvm-ar dN 2 archive.a member.o" should delete the second + // file named member.o it sees; we are not done with member.o the first + // time we see it in the archive. + if (MemberI != Members.end() && !CountParam) Members.erase(MemberI); } failIfError(std::move(Err)); @@ -843,6 +930,8 @@ static int performOperation(ArchiveOperation Operation, EC = errorToErrorCode(std::move(Err)); failIfError(EC, "error loading '" + ArchiveName + "': " + EC.message() + "!"); + if (Archive.isThin()) + CompareFullPath = true; performOperation(Operation, &Archive, std::move(Buf.get()), NewMembers); return 0; } @@ -864,7 +953,7 @@ static int performOperation(ArchiveOperation Operation, } static void runMRIScript() { - enum class MRICommand { AddLib, AddMod, Create, Delete, Save, End, Invalid }; + enum class MRICommand { AddLib, AddMod, Create, CreateThin, Delete, Save, End, Invalid }; ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getSTDIN(); failIfError(Buf.getError()); @@ -888,6 +977,7 @@ static void runMRIScript() { .Case("addlib", MRICommand::AddLib) .Case("addmod", MRICommand::AddMod) .Case("create", MRICommand::Create) + .Case("createthin", MRICommand::CreateThin) .Case("delete", MRICommand::Delete) .Case("save", MRICommand::Save) .Case("end", MRICommand::End) @@ -899,7 +989,7 @@ static void runMRIScript() { { Error Err = Error::success(); for (auto &Member : Lib.children(Err)) - addChildMember(NewMembers, Member); + addChildMember(NewMembers, Member, /*FlattenArchive=*/Thin); failIfError(std::move(Err)); } break; @@ -907,6 +997,9 @@ static void runMRIScript() { case MRICommand::AddMod: addMember(NewMembers, Rest); break; + case MRICommand::CreateThin: + Thin = true; + LLVM_FALLTHROUGH; case MRICommand::Create: Create = true; if (!ArchiveName.empty()) @@ -916,7 +1009,7 @@ static void runMRIScript() { ArchiveName = Rest; break; case MRICommand::Delete: { - StringRef Name = sys::path::filename(Rest); + StringRef Name = normalizePath(Rest); llvm::erase_if(NewMembers, [=](NewArchiveMember &M) { return M.MemberName == Name; }); break; @@ -951,7 +1044,6 @@ static bool handleGenericOption(StringRef arg) { static int ar_main(int argc, char **argv) { SmallVector<const char *, 0> Argv(argv, argv + argc); - BumpPtrAllocator Alloc; StringSaver Saver(Alloc); cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv); for (size_t i = 1; i < Argv.size(); ++i) { diff --git a/contrib/llvm/tools/llvm-as/llvm-as.cpp b/contrib/llvm/tools/llvm-as/llvm-as.cpp index bb4233aa9ba0..234fef907a38 100644 --- a/contrib/llvm/tools/llvm-as/llvm-as.cpp +++ b/contrib/llvm/tools/llvm-as/llvm-as.cpp @@ -1,9 +1,8 @@ //===--- llvm-as.cpp - The low-level LLVM assembler -----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -31,38 +30,43 @@ #include <memory> using namespace llvm; +cl::OptionCategory AsCat("llvm-as Options"); + static cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input .llvm file>"), cl::init("-")); static cl::opt<std::string> OutputFilename("o", cl::desc("Override output filename"), - cl::value_desc("filename")); + cl::value_desc("filename"), + cl::cat(AsCat)); -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(AsCat)); static cl::opt<bool> DisableOutput("disable-output", cl::desc("Disable output"), - cl::init(false)); + cl::init(false), cl::cat(AsCat)); static cl::opt<bool> EmitModuleHash("module-hash", cl::desc("Emit module hash"), - cl::init(false)); + cl::init(false), cl::cat(AsCat)); static cl::opt<bool> DumpAsm("d", cl::desc("Print assembly as parsed"), - cl::Hidden); + cl::Hidden, cl::cat(AsCat)); static cl::opt<bool> DisableVerify("disable-verify", cl::Hidden, - cl::desc("Do not run verifier on input LLVM (dangerous!)")); + cl::desc("Do not run verifier on input LLVM (dangerous!)"), + cl::cat(AsCat)); 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(AsCat)); static cl::opt<std::string> ClDataLayout("data-layout", cl::desc("data layout string to use"), cl::value_desc("layout-string"), - cl::init("")); + cl::init(""), cl::cat(AsCat)); static void WriteOutputFile(const Module *M, const ModuleSummaryIndex *Index) { // Infer the output filename if needed. @@ -110,6 +114,7 @@ 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"); // Parse the file now... diff --git a/contrib/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/contrib/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index 789a666cb41a..01cba1f6e3c9 100644 --- a/contrib/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/contrib/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -1,9 +1,8 @@ //===-- llvm-bcanalyzer.cpp - Bitcode Analyzer --------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -27,22 +26,18 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/StringExtras.h" -#include "llvm/Bitcode/BitcodeReader.h" -#include "llvm/Bitcode/BitstreamReader.h" -#include "llvm/Bitcode/LLVMBitCodes.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Bitcode/BitcodeAnalyzer.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Support/Format.h" +#include "llvm/Support/Error.h" #include "llvm/Support/InitLLVM.h" -#include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/SHA1.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("-")); + InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace")); @@ -53,951 +48,66 @@ static cl::opt<bool> Dump("dump", cl::desc("Dump low level bitcode trace")); static cl::opt<bool> NoHistogram("disable-histogram", cl::desc("Do not print per-code histogram")); -static cl::opt<bool> -NonSymbolic("non-symbolic", - cl::desc("Emit numeric info in dump even if" - " symbolic info is available")); +static cl::opt<bool> NonSymbolic("non-symbolic", + cl::desc("Emit numeric info in dump even if" + " symbolic info is available")); static cl::opt<std::string> - BlockInfoFilename("block-info", - cl::desc("Use the BLOCK_INFO from the given file")); + BlockInfoFilename("block-info", + cl::desc("Use the BLOCK_INFO from the given file")); static cl::opt<bool> - ShowBinaryBlobs("show-binary-blobs", - cl::desc("Print binary blobs using hex escapes")); + ShowBinaryBlobs("show-binary-blobs", + cl::desc("Print binary blobs using hex escapes")); static cl::opt<std::string> CheckHash( "check-hash", cl::desc("Check module hash using the argument as a string table")); -namespace { - -/// CurStreamTypeType - A type for CurStreamType -enum CurStreamTypeType { - UnknownBitstream, - LLVMIRBitstream, - ClangSerializedASTBitstream, - ClangSerializedDiagnosticsBitstream, -}; - -} - -/// GetBlockName - Return a symbolic block name if known, otherwise return -/// null. -static const char *GetBlockName(unsigned BlockID, - const BitstreamBlockInfo &BlockInfo, - CurStreamTypeType CurStreamType) { - // Standard blocks for all bitcode files. - if (BlockID < bitc::FIRST_APPLICATION_BLOCKID) { - if (BlockID == bitc::BLOCKINFO_BLOCK_ID) - return "BLOCKINFO_BLOCK"; - return nullptr; - } - - // Check to see if we have a blockinfo record for this block, with a name. - if (const BitstreamBlockInfo::BlockInfo *Info = - BlockInfo.getBlockInfo(BlockID)) { - if (!Info->Name.empty()) - return Info->Name.c_str(); - } - - - if (CurStreamType != LLVMIRBitstream) return nullptr; - - switch (BlockID) { - default: return nullptr; - case bitc::OPERAND_BUNDLE_TAGS_BLOCK_ID: return "OPERAND_BUNDLE_TAGS_BLOCK"; - case bitc::MODULE_BLOCK_ID: return "MODULE_BLOCK"; - case bitc::PARAMATTR_BLOCK_ID: return "PARAMATTR_BLOCK"; - case bitc::PARAMATTR_GROUP_BLOCK_ID: return "PARAMATTR_GROUP_BLOCK_ID"; - case bitc::TYPE_BLOCK_ID_NEW: return "TYPE_BLOCK_ID"; - case bitc::CONSTANTS_BLOCK_ID: return "CONSTANTS_BLOCK"; - case bitc::FUNCTION_BLOCK_ID: return "FUNCTION_BLOCK"; - case bitc::IDENTIFICATION_BLOCK_ID: - return "IDENTIFICATION_BLOCK_ID"; - case bitc::VALUE_SYMTAB_BLOCK_ID: return "VALUE_SYMTAB"; - case bitc::METADATA_BLOCK_ID: return "METADATA_BLOCK"; - case bitc::METADATA_KIND_BLOCK_ID: return "METADATA_KIND_BLOCK"; - case bitc::METADATA_ATTACHMENT_ID: return "METADATA_ATTACHMENT_BLOCK"; - case bitc::USELIST_BLOCK_ID: return "USELIST_BLOCK_ID"; - case bitc::GLOBALVAL_SUMMARY_BLOCK_ID: - return "GLOBALVAL_SUMMARY_BLOCK"; - case bitc::FULL_LTO_GLOBALVAL_SUMMARY_BLOCK_ID: - return "FULL_LTO_GLOBALVAL_SUMMARY_BLOCK"; - case bitc::MODULE_STRTAB_BLOCK_ID: return "MODULE_STRTAB_BLOCK"; - case bitc::STRTAB_BLOCK_ID: return "STRTAB_BLOCK"; - case bitc::SYMTAB_BLOCK_ID: return "SYMTAB_BLOCK"; - } -} - -/// GetCodeName - Return a symbolic code name if known, otherwise return -/// null. -static const char *GetCodeName(unsigned CodeID, unsigned BlockID, - const BitstreamBlockInfo &BlockInfo, - CurStreamTypeType CurStreamType) { - // Standard blocks for all bitcode files. - if (BlockID < bitc::FIRST_APPLICATION_BLOCKID) { - if (BlockID == bitc::BLOCKINFO_BLOCK_ID) { - switch (CodeID) { - default: return nullptr; - case bitc::BLOCKINFO_CODE_SETBID: return "SETBID"; - case bitc::BLOCKINFO_CODE_BLOCKNAME: return "BLOCKNAME"; - case bitc::BLOCKINFO_CODE_SETRECORDNAME: return "SETRECORDNAME"; - } - } - return nullptr; - } - - // Check to see if we have a blockinfo record for this record, with a name. - if (const BitstreamBlockInfo::BlockInfo *Info = - BlockInfo.getBlockInfo(BlockID)) { - for (unsigned i = 0, e = Info->RecordNames.size(); i != e; ++i) - if (Info->RecordNames[i].first == CodeID) - return Info->RecordNames[i].second.c_str(); - } - - - if (CurStreamType != LLVMIRBitstream) return nullptr; - -#define STRINGIFY_CODE(PREFIX, CODE) \ - case bitc::PREFIX##_##CODE: \ - return #CODE; - switch (BlockID) { - default: return nullptr; - case bitc::MODULE_BLOCK_ID: - switch (CodeID) { - default: return nullptr; - STRINGIFY_CODE(MODULE_CODE, VERSION) - STRINGIFY_CODE(MODULE_CODE, TRIPLE) - STRINGIFY_CODE(MODULE_CODE, DATALAYOUT) - STRINGIFY_CODE(MODULE_CODE, ASM) - STRINGIFY_CODE(MODULE_CODE, SECTIONNAME) - STRINGIFY_CODE(MODULE_CODE, DEPLIB) // FIXME: Remove in 4.0 - STRINGIFY_CODE(MODULE_CODE, GLOBALVAR) - STRINGIFY_CODE(MODULE_CODE, FUNCTION) - STRINGIFY_CODE(MODULE_CODE, ALIAS) - STRINGIFY_CODE(MODULE_CODE, GCNAME) - STRINGIFY_CODE(MODULE_CODE, VSTOFFSET) - STRINGIFY_CODE(MODULE_CODE, METADATA_VALUES_UNUSED) - STRINGIFY_CODE(MODULE_CODE, SOURCE_FILENAME) - STRINGIFY_CODE(MODULE_CODE, HASH) - } - case bitc::IDENTIFICATION_BLOCK_ID: - switch (CodeID) { - default: - return nullptr; - STRINGIFY_CODE(IDENTIFICATION_CODE, STRING) - STRINGIFY_CODE(IDENTIFICATION_CODE, EPOCH) - } - case bitc::PARAMATTR_BLOCK_ID: - switch (CodeID) { - default: return nullptr; - // FIXME: Should these be different? - case bitc::PARAMATTR_CODE_ENTRY_OLD: return "ENTRY"; - case bitc::PARAMATTR_CODE_ENTRY: return "ENTRY"; - } - case bitc::PARAMATTR_GROUP_BLOCK_ID: - switch (CodeID) { - default: return nullptr; - case bitc::PARAMATTR_GRP_CODE_ENTRY: return "ENTRY"; - } - case bitc::TYPE_BLOCK_ID_NEW: - switch (CodeID) { - default: return nullptr; - STRINGIFY_CODE(TYPE_CODE, NUMENTRY) - STRINGIFY_CODE(TYPE_CODE, VOID) - STRINGIFY_CODE(TYPE_CODE, FLOAT) - STRINGIFY_CODE(TYPE_CODE, DOUBLE) - STRINGIFY_CODE(TYPE_CODE, LABEL) - STRINGIFY_CODE(TYPE_CODE, OPAQUE) - STRINGIFY_CODE(TYPE_CODE, INTEGER) - STRINGIFY_CODE(TYPE_CODE, POINTER) - STRINGIFY_CODE(TYPE_CODE, ARRAY) - STRINGIFY_CODE(TYPE_CODE, VECTOR) - STRINGIFY_CODE(TYPE_CODE, X86_FP80) - STRINGIFY_CODE(TYPE_CODE, FP128) - STRINGIFY_CODE(TYPE_CODE, PPC_FP128) - STRINGIFY_CODE(TYPE_CODE, METADATA) - STRINGIFY_CODE(TYPE_CODE, STRUCT_ANON) - STRINGIFY_CODE(TYPE_CODE, STRUCT_NAME) - STRINGIFY_CODE(TYPE_CODE, STRUCT_NAMED) - STRINGIFY_CODE(TYPE_CODE, FUNCTION) - } - - case bitc::CONSTANTS_BLOCK_ID: - switch (CodeID) { - default: return nullptr; - STRINGIFY_CODE(CST_CODE, SETTYPE) - STRINGIFY_CODE(CST_CODE, NULL) - STRINGIFY_CODE(CST_CODE, UNDEF) - STRINGIFY_CODE(CST_CODE, INTEGER) - STRINGIFY_CODE(CST_CODE, WIDE_INTEGER) - STRINGIFY_CODE(CST_CODE, FLOAT) - STRINGIFY_CODE(CST_CODE, AGGREGATE) - STRINGIFY_CODE(CST_CODE, STRING) - STRINGIFY_CODE(CST_CODE, CSTRING) - STRINGIFY_CODE(CST_CODE, CE_BINOP) - STRINGIFY_CODE(CST_CODE, CE_CAST) - STRINGIFY_CODE(CST_CODE, CE_GEP) - STRINGIFY_CODE(CST_CODE, CE_INBOUNDS_GEP) - STRINGIFY_CODE(CST_CODE, CE_SELECT) - STRINGIFY_CODE(CST_CODE, CE_EXTRACTELT) - STRINGIFY_CODE(CST_CODE, CE_INSERTELT) - STRINGIFY_CODE(CST_CODE, CE_SHUFFLEVEC) - STRINGIFY_CODE(CST_CODE, CE_CMP) - STRINGIFY_CODE(CST_CODE, INLINEASM) - STRINGIFY_CODE(CST_CODE, CE_SHUFVEC_EX) - STRINGIFY_CODE(CST_CODE, CE_UNOP) - case bitc::CST_CODE_BLOCKADDRESS: return "CST_CODE_BLOCKADDRESS"; - STRINGIFY_CODE(CST_CODE, DATA) - } - case bitc::FUNCTION_BLOCK_ID: - switch (CodeID) { - default: return nullptr; - STRINGIFY_CODE(FUNC_CODE, DECLAREBLOCKS) - STRINGIFY_CODE(FUNC_CODE, INST_BINOP) - STRINGIFY_CODE(FUNC_CODE, INST_CAST) - STRINGIFY_CODE(FUNC_CODE, INST_GEP_OLD) - STRINGIFY_CODE(FUNC_CODE, INST_INBOUNDS_GEP_OLD) - STRINGIFY_CODE(FUNC_CODE, INST_SELECT) - STRINGIFY_CODE(FUNC_CODE, INST_EXTRACTELT) - STRINGIFY_CODE(FUNC_CODE, INST_INSERTELT) - STRINGIFY_CODE(FUNC_CODE, INST_SHUFFLEVEC) - STRINGIFY_CODE(FUNC_CODE, INST_CMP) - STRINGIFY_CODE(FUNC_CODE, INST_RET) - STRINGIFY_CODE(FUNC_CODE, INST_BR) - STRINGIFY_CODE(FUNC_CODE, INST_SWITCH) - STRINGIFY_CODE(FUNC_CODE, INST_INVOKE) - STRINGIFY_CODE(FUNC_CODE, INST_UNOP) - STRINGIFY_CODE(FUNC_CODE, INST_UNREACHABLE) - STRINGIFY_CODE(FUNC_CODE, INST_CLEANUPRET) - STRINGIFY_CODE(FUNC_CODE, INST_CATCHRET) - STRINGIFY_CODE(FUNC_CODE, INST_CATCHPAD) - STRINGIFY_CODE(FUNC_CODE, INST_PHI) - STRINGIFY_CODE(FUNC_CODE, INST_ALLOCA) - STRINGIFY_CODE(FUNC_CODE, INST_LOAD) - STRINGIFY_CODE(FUNC_CODE, INST_VAARG) - STRINGIFY_CODE(FUNC_CODE, INST_STORE) - STRINGIFY_CODE(FUNC_CODE, INST_EXTRACTVAL) - STRINGIFY_CODE(FUNC_CODE, INST_INSERTVAL) - STRINGIFY_CODE(FUNC_CODE, INST_CMP2) - STRINGIFY_CODE(FUNC_CODE, INST_VSELECT) - STRINGIFY_CODE(FUNC_CODE, DEBUG_LOC_AGAIN) - STRINGIFY_CODE(FUNC_CODE, INST_CALL) - STRINGIFY_CODE(FUNC_CODE, DEBUG_LOC) - STRINGIFY_CODE(FUNC_CODE, INST_GEP) - STRINGIFY_CODE(FUNC_CODE, OPERAND_BUNDLE) - STRINGIFY_CODE(FUNC_CODE, INST_FENCE) - STRINGIFY_CODE(FUNC_CODE, INST_ATOMICRMW) - STRINGIFY_CODE(FUNC_CODE, INST_LOADATOMIC) - STRINGIFY_CODE(FUNC_CODE, INST_STOREATOMIC) - STRINGIFY_CODE(FUNC_CODE, INST_CMPXCHG) - } - case bitc::VALUE_SYMTAB_BLOCK_ID: - switch (CodeID) { - default: return nullptr; - STRINGIFY_CODE(VST_CODE, ENTRY) - STRINGIFY_CODE(VST_CODE, BBENTRY) - STRINGIFY_CODE(VST_CODE, FNENTRY) - STRINGIFY_CODE(VST_CODE, COMBINED_ENTRY) - } - case bitc::MODULE_STRTAB_BLOCK_ID: - switch (CodeID) { - default: - return nullptr; - STRINGIFY_CODE(MST_CODE, ENTRY) - STRINGIFY_CODE(MST_CODE, HASH) - } - case bitc::GLOBALVAL_SUMMARY_BLOCK_ID: - case bitc::FULL_LTO_GLOBALVAL_SUMMARY_BLOCK_ID: - switch (CodeID) { - default: - return nullptr; - STRINGIFY_CODE(FS, PERMODULE) - STRINGIFY_CODE(FS, PERMODULE_PROFILE) - STRINGIFY_CODE(FS, PERMODULE_RELBF) - STRINGIFY_CODE(FS, PERMODULE_GLOBALVAR_INIT_REFS) - STRINGIFY_CODE(FS, COMBINED) - STRINGIFY_CODE(FS, COMBINED_PROFILE) - STRINGIFY_CODE(FS, COMBINED_GLOBALVAR_INIT_REFS) - STRINGIFY_CODE(FS, ALIAS) - STRINGIFY_CODE(FS, COMBINED_ALIAS) - STRINGIFY_CODE(FS, COMBINED_ORIGINAL_NAME) - STRINGIFY_CODE(FS, VERSION) - STRINGIFY_CODE(FS, FLAGS) - STRINGIFY_CODE(FS, TYPE_TESTS) - STRINGIFY_CODE(FS, TYPE_TEST_ASSUME_VCALLS) - STRINGIFY_CODE(FS, TYPE_CHECKED_LOAD_VCALLS) - STRINGIFY_CODE(FS, TYPE_TEST_ASSUME_CONST_VCALL) - STRINGIFY_CODE(FS, TYPE_CHECKED_LOAD_CONST_VCALL) - STRINGIFY_CODE(FS, VALUE_GUID) - STRINGIFY_CODE(FS, CFI_FUNCTION_DEFS) - STRINGIFY_CODE(FS, CFI_FUNCTION_DECLS) - STRINGIFY_CODE(FS, TYPE_ID) - } - case bitc::METADATA_ATTACHMENT_ID: - switch(CodeID) { - default:return nullptr; - STRINGIFY_CODE(METADATA, ATTACHMENT) - } - case bitc::METADATA_BLOCK_ID: - switch(CodeID) { - default:return nullptr; - STRINGIFY_CODE(METADATA, STRING_OLD) - STRINGIFY_CODE(METADATA, VALUE) - STRINGIFY_CODE(METADATA, NODE) - STRINGIFY_CODE(METADATA, NAME) - STRINGIFY_CODE(METADATA, DISTINCT_NODE) - STRINGIFY_CODE(METADATA, KIND) // Older bitcode has it in a MODULE_BLOCK - STRINGIFY_CODE(METADATA, LOCATION) - STRINGIFY_CODE(METADATA, OLD_NODE) - STRINGIFY_CODE(METADATA, OLD_FN_NODE) - STRINGIFY_CODE(METADATA, NAMED_NODE) - STRINGIFY_CODE(METADATA, GENERIC_DEBUG) - STRINGIFY_CODE(METADATA, SUBRANGE) - STRINGIFY_CODE(METADATA, ENUMERATOR) - STRINGIFY_CODE(METADATA, BASIC_TYPE) - STRINGIFY_CODE(METADATA, FILE) - STRINGIFY_CODE(METADATA, DERIVED_TYPE) - STRINGIFY_CODE(METADATA, COMPOSITE_TYPE) - STRINGIFY_CODE(METADATA, SUBROUTINE_TYPE) - STRINGIFY_CODE(METADATA, COMPILE_UNIT) - STRINGIFY_CODE(METADATA, SUBPROGRAM) - STRINGIFY_CODE(METADATA, LEXICAL_BLOCK) - STRINGIFY_CODE(METADATA, LEXICAL_BLOCK_FILE) - STRINGIFY_CODE(METADATA, NAMESPACE) - STRINGIFY_CODE(METADATA, TEMPLATE_TYPE) - STRINGIFY_CODE(METADATA, TEMPLATE_VALUE) - STRINGIFY_CODE(METADATA, GLOBAL_VAR) - STRINGIFY_CODE(METADATA, LOCAL_VAR) - STRINGIFY_CODE(METADATA, EXPRESSION) - STRINGIFY_CODE(METADATA, OBJC_PROPERTY) - STRINGIFY_CODE(METADATA, IMPORTED_ENTITY) - STRINGIFY_CODE(METADATA, MODULE) - STRINGIFY_CODE(METADATA, MACRO) - STRINGIFY_CODE(METADATA, MACRO_FILE) - STRINGIFY_CODE(METADATA, STRINGS) - STRINGIFY_CODE(METADATA, GLOBAL_DECL_ATTACHMENT) - STRINGIFY_CODE(METADATA, GLOBAL_VAR_EXPR) - STRINGIFY_CODE(METADATA, INDEX_OFFSET) - STRINGIFY_CODE(METADATA, INDEX) - } - case bitc::METADATA_KIND_BLOCK_ID: - switch (CodeID) { - default: - return nullptr; - STRINGIFY_CODE(METADATA, KIND) - } - case bitc::USELIST_BLOCK_ID: - switch(CodeID) { - default:return nullptr; - case bitc::USELIST_CODE_DEFAULT: return "USELIST_CODE_DEFAULT"; - case bitc::USELIST_CODE_BB: return "USELIST_CODE_BB"; - } - - case bitc::OPERAND_BUNDLE_TAGS_BLOCK_ID: - switch(CodeID) { - default: return nullptr; - case bitc::OPERAND_BUNDLE_TAG: return "OPERAND_BUNDLE_TAG"; - } - case bitc::STRTAB_BLOCK_ID: - switch(CodeID) { - default: return nullptr; - case bitc::STRTAB_BLOB: return "BLOB"; - } - case bitc::SYMTAB_BLOCK_ID: - switch(CodeID) { - default: return nullptr; - case bitc::SYMTAB_BLOB: return "BLOB"; - } - } -#undef STRINGIFY_CODE -} - -struct PerRecordStats { - unsigned NumInstances; - unsigned NumAbbrev; - uint64_t TotalBits; - - PerRecordStats() : NumInstances(0), NumAbbrev(0), TotalBits(0) {} -}; - -struct PerBlockIDStats { - /// NumInstances - This the number of times this block ID has been seen. - unsigned NumInstances; - - /// NumBits - The total size in bits of all of these blocks. - uint64_t NumBits; - - /// NumSubBlocks - The total number of blocks these blocks contain. - unsigned NumSubBlocks; - - /// NumAbbrevs - The total number of abbreviations. - unsigned NumAbbrevs; - - /// NumRecords - The total number of records these blocks contain, and the - /// number that are abbreviated. - unsigned NumRecords, NumAbbreviatedRecords; - - /// CodeFreq - Keep track of the number of times we see each code. - std::vector<PerRecordStats> CodeFreq; - - PerBlockIDStats() - : NumInstances(0), NumBits(0), - NumSubBlocks(0), NumAbbrevs(0), NumRecords(0), NumAbbreviatedRecords(0) {} -}; - -static std::map<unsigned, PerBlockIDStats> BlockIDStats; - - - -/// ReportError - All bitcode analysis errors go through this function, making this a -/// good place to breakpoint if debugging. -static bool ReportError(const Twine &Err) { - WithColor::error() << Err << "\n"; - return true; -} - -static bool decodeMetadataStringsBlob(StringRef Indent, - ArrayRef<uint64_t> Record, - StringRef Blob) { - if (Blob.empty()) - return true; - - if (Record.size() != 2) - return true; - - unsigned NumStrings = Record[0]; - unsigned StringsOffset = Record[1]; - outs() << " num-strings = " << NumStrings << " {\n"; - - StringRef Lengths = Blob.slice(0, StringsOffset); - SimpleBitstreamCursor R(Lengths); - StringRef Strings = Blob.drop_front(StringsOffset); - do { - if (R.AtEndOfStream()) - return ReportError("bad length"); - - unsigned Size = R.ReadVBR(6); - if (Strings.size() < Size) - return ReportError("truncated chars"); - - outs() << Indent << " '"; - outs().write_escaped(Strings.slice(0, Size), /*hex=*/true); - outs() << "'\n"; - Strings = Strings.drop_front(Size); - } while (--NumStrings); - - outs() << Indent << " }"; - return false; -} - -static bool decodeBlob(unsigned Code, unsigned BlockID, StringRef Indent, - ArrayRef<uint64_t> Record, StringRef Blob) { - if (BlockID != bitc::METADATA_BLOCK_ID) - return true; - if (Code != bitc::METADATA_STRINGS) - return true; - - return decodeMetadataStringsBlob(Indent, Record, Blob); -} - -/// ParseBlock - Read a block, updating statistics, etc. -static bool ParseBlock(BitstreamCursor &Stream, BitstreamBlockInfo &BlockInfo, - unsigned BlockID, unsigned IndentLevel, - CurStreamTypeType CurStreamType) { - std::string Indent(IndentLevel*2, ' '); - uint64_t BlockBitStart = Stream.GetCurrentBitNo(); - - // Get the statistics for this BlockID. - PerBlockIDStats &BlockStats = BlockIDStats[BlockID]; - - BlockStats.NumInstances++; - - // BLOCKINFO is a special part of the stream. - bool DumpRecords = Dump; - if (BlockID == bitc::BLOCKINFO_BLOCK_ID) { - if (Dump) outs() << Indent << "<BLOCKINFO_BLOCK/>\n"; - Optional<BitstreamBlockInfo> NewBlockInfo = - Stream.ReadBlockInfoBlock(/*ReadBlockInfoNames=*/true); - if (!NewBlockInfo) - return ReportError("Malformed BlockInfoBlock"); - BlockInfo = std::move(*NewBlockInfo); - Stream.JumpToBit(BlockBitStart); - // It's not really interesting to dump the contents of the blockinfo block. - DumpRecords = false; - } - - unsigned NumWords = 0; - if (Stream.EnterSubBlock(BlockID, &NumWords)) - return ReportError("Malformed block record"); - - // Keep it for later, when we see a MODULE_HASH record - uint64_t BlockEntryPos = Stream.getCurrentByteNo(); - - const char *BlockName = nullptr; - if (DumpRecords) { - outs() << Indent << "<"; - if ((BlockName = GetBlockName(BlockID, BlockInfo, CurStreamType))) - outs() << BlockName; - else - outs() << "UnknownBlock" << BlockID; - - if (NonSymbolic && BlockName) - outs() << " BlockID=" << BlockID; - - outs() << " NumWords=" << NumWords - << " BlockCodeSize=" << Stream.getAbbrevIDWidth() << ">\n"; - } - - SmallVector<uint64_t, 64> Record; - - // Keep the offset to the metadata index if seen. - uint64_t MetadataIndexOffset = 0; - - // Read all the records for this block. - while (1) { - if (Stream.AtEndOfStream()) - return ReportError("Premature end of bitstream"); - - uint64_t RecordStartBit = Stream.GetCurrentBitNo(); - - BitstreamEntry Entry = - Stream.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); - - switch (Entry.Kind) { - case BitstreamEntry::Error: - return ReportError("malformed bitcode file"); - case BitstreamEntry::EndBlock: { - uint64_t BlockBitEnd = Stream.GetCurrentBitNo(); - BlockStats.NumBits += BlockBitEnd-BlockBitStart; - if (DumpRecords) { - outs() << Indent << "</"; - if (BlockName) - outs() << BlockName << ">\n"; - else - outs() << "UnknownBlock" << BlockID << ">\n"; - } - return false; - } - - case BitstreamEntry::SubBlock: { - uint64_t SubBlockBitStart = Stream.GetCurrentBitNo(); - if (ParseBlock(Stream, BlockInfo, Entry.ID, IndentLevel + 1, - CurStreamType)) - return true; - ++BlockStats.NumSubBlocks; - uint64_t SubBlockBitEnd = Stream.GetCurrentBitNo(); - - // Don't include subblock sizes in the size of this block. - BlockBitStart += SubBlockBitEnd-SubBlockBitStart; - continue; - } - case BitstreamEntry::Record: - // The interesting case. - break; - } - - if (Entry.ID == bitc::DEFINE_ABBREV) { - Stream.ReadAbbrevRecord(); - ++BlockStats.NumAbbrevs; - continue; - } - - Record.clear(); - - ++BlockStats.NumRecords; - - StringRef Blob; - uint64_t CurrentRecordPos = Stream.GetCurrentBitNo(); - unsigned Code = Stream.readRecord(Entry.ID, Record, &Blob); - - // Increment the # occurrences of this code. - if (BlockStats.CodeFreq.size() <= Code) - BlockStats.CodeFreq.resize(Code+1); - BlockStats.CodeFreq[Code].NumInstances++; - BlockStats.CodeFreq[Code].TotalBits += - Stream.GetCurrentBitNo()-RecordStartBit; - if (Entry.ID != bitc::UNABBREV_RECORD) { - BlockStats.CodeFreq[Code].NumAbbrev++; - ++BlockStats.NumAbbreviatedRecords; - } - - if (DumpRecords) { - outs() << Indent << " <"; - if (const char *CodeName = - GetCodeName(Code, BlockID, BlockInfo, CurStreamType)) - outs() << CodeName; - else - outs() << "UnknownCode" << Code; - if (NonSymbolic && GetCodeName(Code, BlockID, BlockInfo, CurStreamType)) - outs() << " codeid=" << Code; - const BitCodeAbbrev *Abbv = nullptr; - if (Entry.ID != bitc::UNABBREV_RECORD) { - Abbv = Stream.getAbbrev(Entry.ID); - outs() << " abbrevid=" << Entry.ID; - } - - for (unsigned i = 0, e = Record.size(); i != e; ++i) - outs() << " op" << i << "=" << (int64_t)Record[i]; - - // If we found a metadata index, let's verify that we had an offset before - // and validate its forward reference offset was correct! - if (BlockID == bitc::METADATA_BLOCK_ID) { - if (Code == bitc::METADATA_INDEX_OFFSET) { - if (Record.size() != 2) - outs() << "(Invalid record)"; - else { - auto Offset = Record[0] + (Record[1] << 32); - MetadataIndexOffset = Stream.GetCurrentBitNo() + Offset; - } - } - if (Code == bitc::METADATA_INDEX) { - outs() << " (offset "; - if (MetadataIndexOffset == RecordStartBit) - outs() << "match)"; - else - outs() << "mismatch: " << MetadataIndexOffset << " vs " - << RecordStartBit << ")"; - } - } - - // If we found a module hash, let's verify that it matches! - if (BlockID == bitc::MODULE_BLOCK_ID && Code == bitc::MODULE_CODE_HASH && - !CheckHash.empty()) { - if (Record.size() != 5) - outs() << " (invalid)"; - else { - // Recompute the hash and compare it to the one in the bitcode - SHA1 Hasher; - StringRef Hash; - Hasher.update(CheckHash); - { - int BlockSize = (CurrentRecordPos / 8) - BlockEntryPos; - auto Ptr = Stream.getPointerToByte(BlockEntryPos, BlockSize); - Hasher.update(ArrayRef<uint8_t>(Ptr, BlockSize)); - Hash = Hasher.result(); - } - SmallString<20> RecordedHash; - RecordedHash.resize(20); - int Pos = 0; - for (auto &Val : Record) { - assert(!(Val >> 32) && "Unexpected high bits set"); - RecordedHash[Pos++] = (Val >> 24) & 0xFF; - RecordedHash[Pos++] = (Val >> 16) & 0xFF; - RecordedHash[Pos++] = (Val >> 8) & 0xFF; - RecordedHash[Pos++] = (Val >> 0) & 0xFF; - } - if (Hash == RecordedHash) - outs() << " (match)"; - else - outs() << " (!mismatch!)"; - } - } - - outs() << "/>"; - - if (Abbv) { - for (unsigned i = 1, e = Abbv->getNumOperandInfos(); i != e; ++i) { - const BitCodeAbbrevOp &Op = Abbv->getOperandInfo(i); - if (!Op.isEncoding() || Op.getEncoding() != BitCodeAbbrevOp::Array) - continue; - assert(i + 2 == e && "Array op not second to last"); - std::string Str; - bool ArrayIsPrintable = true; - for (unsigned j = i - 1, je = Record.size(); j != je; ++j) { - if (!isPrint(static_cast<unsigned char>(Record[j]))) { - ArrayIsPrintable = false; - break; - } - Str += (char)Record[j]; - } - if (ArrayIsPrintable) - outs() << " record string = '" << Str << "'"; - break; - } - } - - if (Blob.data() && decodeBlob(Code, BlockID, Indent, Record, Blob)) { - outs() << " blob data = "; - if (ShowBinaryBlobs) { - outs() << "'"; - outs().write_escaped(Blob, /*hex=*/true) << "'"; - } else { - bool BlobIsPrintable = true; - for (unsigned i = 0, e = Blob.size(); i != e; ++i) - if (!isPrint(static_cast<unsigned char>(Blob[i]))) { - BlobIsPrintable = false; - break; - } - - if (BlobIsPrintable) - outs() << "'" << Blob << "'"; - else - outs() << "unprintable, " << Blob.size() << " bytes."; - } - } - - outs() << "\n"; - } - - // Make sure that we can skip the current record. - Stream.JumpToBit(CurrentRecordPos); - Stream.skipRecord(Entry.ID); - } -} - -static void PrintSize(double Bits) { - outs() << format("%.2f/%.2fB/%luW", Bits, Bits/8,(unsigned long)(Bits/32)); -} -static void PrintSize(uint64_t Bits) { - outs() << format("%lub/%.2fB/%luW", (unsigned long)Bits, - (double)Bits/8, (unsigned long)(Bits/32)); +static Error reportError(StringRef Message) { + return createStringError(std::errc::illegal_byte_sequence, Message.data()); } -static CurStreamTypeType ReadSignature(BitstreamCursor &Stream) { - char Signature[6]; - Signature[0] = Stream.Read(8); - Signature[1] = Stream.Read(8); - - // Autodetect the file contents, if it is one we know. - if (Signature[0] == 'C' && Signature[1] == 'P') { - Signature[2] = Stream.Read(8); - Signature[3] = Stream.Read(8); - if (Signature[2] == 'C' && Signature[3] == 'H') - return ClangSerializedASTBitstream; - } else if (Signature[0] == 'D' && Signature[1] == 'I') { - Signature[2] = Stream.Read(8); - Signature[3] = Stream.Read(8); - if (Signature[2] == 'A' && Signature[3] == 'G') - return ClangSerializedDiagnosticsBitstream; - } else { - Signature[2] = Stream.Read(4); - Signature[3] = Stream.Read(4); - Signature[4] = Stream.Read(4); - Signature[5] = Stream.Read(4); - if (Signature[0] == 'B' && Signature[1] == 'C' && - Signature[2] == 0x0 && Signature[3] == 0xC && - Signature[4] == 0xE && Signature[5] == 0xD) - return LLVMIRBitstream; - } - return UnknownBitstream; -} - -static bool openBitcodeFile(StringRef Path, - std::unique_ptr<MemoryBuffer> &MemBuf, - BitstreamCursor &Stream, - CurStreamTypeType &CurStreamType) { +static Expected<std::unique_ptr<MemoryBuffer>> openBitcodeFile(StringRef Path) { // Read the input file. - ErrorOr<std::unique_ptr<MemoryBuffer>> MemBufOrErr = - MemoryBuffer::getFileOrSTDIN(Path); - if (std::error_code EC = MemBufOrErr.getError()) - return ReportError(Twine("ReportError reading '") + Path + "': " + EC.message()); - MemBuf = std::move(MemBufOrErr.get()); - - if (MemBuf->getBufferSize() & 3) - return ReportError("Bitcode stream should be a multiple of 4 bytes in length"); - - const unsigned char *BufPtr = (const unsigned char *)MemBuf->getBufferStart(); - const unsigned char *EndBufPtr = BufPtr + MemBuf->getBufferSize(); + Expected<std::unique_ptr<MemoryBuffer>> MemBufOrErr = + errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Path)); + if (Error E = MemBufOrErr.takeError()) + return std::move(E); - // If we have a wrapper header, parse it and ignore the non-bc file contents. - // The magic number is 0x0B17C0DE stored in little endian. - if (isBitcodeWrapper(BufPtr, EndBufPtr)) { - if (MemBuf->getBufferSize() < BWH_HeaderSize) - return ReportError("Invalid bitcode wrapper header"); + std::unique_ptr<MemoryBuffer> MemBuf = std::move(*MemBufOrErr); - if (Dump) { - unsigned Magic = support::endian::read32le(&BufPtr[BWH_MagicField]); - unsigned Version = support::endian::read32le(&BufPtr[BWH_VersionField]); - unsigned Offset = support::endian::read32le(&BufPtr[BWH_OffsetField]); - unsigned Size = support::endian::read32le(&BufPtr[BWH_SizeField]); - unsigned CPUType = support::endian::read32le(&BufPtr[BWH_CPUTypeField]); - - outs() << "<BITCODE_WRAPPER_HEADER" - << " Magic=" << format_hex(Magic, 10) - << " Version=" << format_hex(Version, 10) - << " Offset=" << format_hex(Offset, 10) - << " Size=" << format_hex(Size, 10) - << " CPUType=" << format_hex(CPUType, 10) << "/>\n"; - } - - if (SkipBitcodeWrapperHeader(BufPtr, EndBufPtr, true)) - return ReportError("Invalid bitcode wrapper header"); - } - - Stream = BitstreamCursor(ArrayRef<uint8_t>(BufPtr, EndBufPtr)); - CurStreamType = ReadSignature(Stream); - - return false; + if (MemBuf->getBufferSize() & 3) + return reportError( + "Bitcode stream should be a multiple of 4 bytes in length"); + return std::move(MemBuf); } -/// AnalyzeBitcode - Analyze the bitcode file specified by InputFilename. -static int AnalyzeBitcode() { - std::unique_ptr<MemoryBuffer> StreamBuffer; - BitstreamCursor Stream; - BitstreamBlockInfo BlockInfo; - CurStreamTypeType CurStreamType; - if (openBitcodeFile(InputFilename, StreamBuffer, Stream, CurStreamType)) - return true; - Stream.setBlockInfo(&BlockInfo); - - // Read block info from BlockInfoFilename, if specified. - // The block info must be a top-level block. - if (!BlockInfoFilename.empty()) { - std::unique_ptr<MemoryBuffer> BlockInfoBuffer; - BitstreamCursor BlockInfoCursor; - CurStreamTypeType BlockInfoStreamType; - if (openBitcodeFile(BlockInfoFilename, BlockInfoBuffer, BlockInfoCursor, - BlockInfoStreamType)) - return true; - - while (!BlockInfoCursor.AtEndOfStream()) { - unsigned Code = BlockInfoCursor.ReadCode(); - if (Code != bitc::ENTER_SUBBLOCK) - return ReportError("Invalid record at top-level in block info file"); - - unsigned BlockID = BlockInfoCursor.ReadSubBlockID(); - if (BlockID == bitc::BLOCKINFO_BLOCK_ID) { - Optional<BitstreamBlockInfo> NewBlockInfo = - BlockInfoCursor.ReadBlockInfoBlock(/*ReadBlockInfoNames=*/true); - if (!NewBlockInfo) - return ReportError("Malformed BlockInfoBlock in block info file"); - BlockInfo = std::move(*NewBlockInfo); - break; - } - - BlockInfoCursor.SkipBlock(); - } - } - - unsigned NumTopBlocks = 0; - - // Parse the top-level structure. We only allow blocks at the top-level. - while (!Stream.AtEndOfStream()) { - unsigned Code = Stream.ReadCode(); - if (Code != bitc::ENTER_SUBBLOCK) - return ReportError("Invalid record at top-level"); - - unsigned BlockID = Stream.ReadSubBlockID(); - - if (ParseBlock(Stream, BlockInfo, BlockID, 0, CurStreamType)) - return true; - ++NumTopBlocks; - } - - if (Dump) outs() << "\n\n"; - - uint64_t BufferSizeBits = Stream.getBitcodeBytes().size() * CHAR_BIT; - // Print a summary of the read file. - outs() << "Summary of " << InputFilename << ":\n"; - outs() << " Total size: "; - PrintSize(BufferSizeBits); - outs() << "\n"; - outs() << " Stream type: "; - switch (CurStreamType) { - case UnknownBitstream: - outs() << "unknown\n"; - break; - case LLVMIRBitstream: - outs() << "LLVM IR\n"; - break; - case ClangSerializedASTBitstream: - outs() << "Clang Serialized AST\n"; - break; - case ClangSerializedDiagnosticsBitstream: - outs() << "Clang Serialized Diagnostics\n"; - break; - } - outs() << " # Toplevel Blocks: " << NumTopBlocks << "\n"; - outs() << "\n"; - - // Emit per-block stats. - outs() << "Per-block Summary:\n"; - for (std::map<unsigned, PerBlockIDStats>::iterator I = BlockIDStats.begin(), - E = BlockIDStats.end(); I != E; ++I) { - outs() << " Block ID #" << I->first; - if (const char *BlockName = - GetBlockName(I->first, BlockInfo, CurStreamType)) - outs() << " (" << BlockName << ")"; - outs() << ":\n"; - - const PerBlockIDStats &Stats = I->second; - outs() << " Num Instances: " << Stats.NumInstances << "\n"; - outs() << " Total Size: "; - PrintSize(Stats.NumBits); - outs() << "\n"; - double pct = (Stats.NumBits * 100.0) / BufferSizeBits; - outs() << " Percent of file: " << format("%2.4f%%", pct) << "\n"; - if (Stats.NumInstances > 1) { - outs() << " Average Size: "; - PrintSize(Stats.NumBits/(double)Stats.NumInstances); - outs() << "\n"; - outs() << " Tot/Avg SubBlocks: " << Stats.NumSubBlocks << "/" - << Stats.NumSubBlocks/(double)Stats.NumInstances << "\n"; - outs() << " Tot/Avg Abbrevs: " << Stats.NumAbbrevs << "/" - << Stats.NumAbbrevs/(double)Stats.NumInstances << "\n"; - outs() << " Tot/Avg Records: " << Stats.NumRecords << "/" - << Stats.NumRecords/(double)Stats.NumInstances << "\n"; - } else { - outs() << " Num SubBlocks: " << Stats.NumSubBlocks << "\n"; - outs() << " Num Abbrevs: " << Stats.NumAbbrevs << "\n"; - outs() << " Num Records: " << Stats.NumRecords << "\n"; - } - if (Stats.NumRecords) { - double pct = (Stats.NumAbbreviatedRecords * 100.0) / Stats.NumRecords; - outs() << " Percent Abbrevs: " << format("%2.4f%%", pct) << "\n"; - } - outs() << "\n"; - - // Print a histogram of the codes we see. - if (!NoHistogram && !Stats.CodeFreq.empty()) { - std::vector<std::pair<unsigned, unsigned> > FreqPairs; // <freq,code> - for (unsigned i = 0, e = Stats.CodeFreq.size(); i != e; ++i) - if (unsigned Freq = Stats.CodeFreq[i].NumInstances) - FreqPairs.push_back(std::make_pair(Freq, i)); - std::stable_sort(FreqPairs.begin(), FreqPairs.end()); - std::reverse(FreqPairs.begin(), FreqPairs.end()); +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + cl::ParseCommandLineOptions(argc, argv, "llvm-bcanalyzer file analyzer\n"); + ExitOnError ExitOnErr("llvm-bcanalyzer: "); - outs() << "\tRecord Histogram:\n"; - outs() << "\t\t Count # Bits b/Rec % Abv Record Kind\n"; - for (unsigned i = 0, e = FreqPairs.size(); i != e; ++i) { - const PerRecordStats &RecStats = Stats.CodeFreq[FreqPairs[i].second]; + std::unique_ptr<MemoryBuffer> MB = ExitOnErr(openBitcodeFile(InputFilename)); + std::unique_ptr<MemoryBuffer> BlockInfoMB = nullptr; + if (!BlockInfoFilename.empty()) + BlockInfoMB = ExitOnErr(openBitcodeFile(BlockInfoFilename)); - outs() << format("\t\t%7d %9lu", - RecStats.NumInstances, - (unsigned long)RecStats.TotalBits); + BitcodeAnalyzer BA(MB->getBuffer(), + BlockInfoMB ? Optional<StringRef>(BlockInfoMB->getBuffer()) + : None); - if (RecStats.NumInstances > 1) - outs() << format(" %9.1f", - (double)RecStats.TotalBits/RecStats.NumInstances); - else - outs() << " "; + BCDumpOptions O(outs()); + O.Histogram = !NoHistogram; + O.Symbolic = !NonSymbolic; + O.ShowBinaryBlobs = ShowBinaryBlobs; - if (RecStats.NumAbbrev) - outs() << - format(" %7.2f", - (double)RecStats.NumAbbrev/RecStats.NumInstances*100); - else - outs() << " "; + ExitOnErr( + BA.analyze(O, CheckHash.empty() ? None : Optional<StringRef>(CheckHash))); - outs() << " "; - if (const char *CodeName = GetCodeName(FreqPairs[i].second, I->first, - BlockInfo, CurStreamType)) - outs() << CodeName << "\n"; - else - outs() << "UnknownCode" << FreqPairs[i].second << "\n"; - } - outs() << "\n"; + if (Dump) + outs() << "\n\n"; - } - } + BA.printStats(O, StringRef(InputFilename.getValue())); return 0; } - - -int main(int argc, char **argv) { - InitLLVM X(argc, argv); - cl::ParseCommandLineOptions(argc, argv, "llvm-bcanalyzer file analyzer\n"); - return AnalyzeBitcode(); -} diff --git a/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp b/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp index 728e00e7c3c2..f707e3c7ab53 100644 --- a/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -1,9 +1,8 @@ //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -1007,10 +1006,23 @@ int CodeCoverageTool::doReport(int argc, const char **argv, int CodeCoverageTool::doExport(int argc, const char **argv, CommandLineParserType commandLineParser) { + cl::OptionCategory ExportCategory("Exporting options"); + + cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional, + cl::desc("Don't export expanded source regions"), + cl::cat(ExportCategory)); + + cl::opt<bool> SkipFunctions("skip-functions", cl::Optional, + cl::desc("Don't export per-function data"), + cl::cat(ExportCategory)); + auto Err = commandLineParser(argc, argv); if (Err) return Err; + ViewOpts.SkipExpansions = SkipExpansions; + ViewOpts.SkipFunctions = SkipFunctions; + if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text && ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) { error("Coverage data can only be exported as textual JSON or an " diff --git a/contrib/llvm/tools/llvm-cov/CoverageExporter.h b/contrib/llvm/tools/llvm-cov/CoverageExporter.h index b226d68813d9..751e55dc0916 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageExporter.h +++ b/contrib/llvm/tools/llvm-cov/CoverageExporter.h @@ -1,9 +1,8 @@ //===- CoverageExporter.h - Code coverage exporter ------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp index 22243f8e2c3e..181d428ed9d8 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ b/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -1,9 +1,8 @@ //===- CoverageExporterJson.cpp - Code coverage export --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -43,7 +42,14 @@ #include "CoverageExporterJson.h" #include "CoverageReport.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Support/Threading.h" +#include <algorithm> +#include <mutex> +#include <utility> /// The semantic version combined as a string. #define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.0" @@ -128,13 +134,15 @@ json::Array renderFileSegments(const coverage::CoverageData &FileCoverage, json::Object renderFile(const coverage::CoverageMapping &Coverage, const std::string &Filename, const FileCoverageSummary &FileReport, - bool ExportSummaryOnly) { + const CoverageViewOptions &Options) { json::Object File({{"filename", Filename}}); - if (!ExportSummaryOnly) { + if (!Options.ExportSummaryOnly) { // Calculate and render detailed coverage information for given file. auto FileCoverage = Coverage.getCoverageForFile(Filename); File["segments"] = renderFileSegments(FileCoverage, FileReport); - File["expansions"] = renderFileExpansions(FileCoverage, FileReport); + if (!Options.SkipExpansions) { + File["expansions"] = renderFileExpansions(FileCoverage, FileReport); + } } File["summary"] = renderSummary(FileReport); return File; @@ -143,11 +151,28 @@ json::Object renderFile(const coverage::CoverageMapping &Coverage, json::Array renderFiles(const coverage::CoverageMapping &Coverage, ArrayRef<std::string> SourceFiles, ArrayRef<FileCoverageSummary> FileReports, - bool ExportSummaryOnly) { + const CoverageViewOptions &Options) { + auto NumThreads = Options.NumThreads; + if (NumThreads == 0) { + NumThreads = std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), + unsigned(SourceFiles.size()))); + } + ThreadPool Pool(NumThreads); json::Array FileArray; - for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) - FileArray.push_back(renderFile(Coverage, SourceFiles[I], FileReports[I], - ExportSummaryOnly)); + std::mutex FileArrayMutex; + + for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) { + auto &SourceFile = SourceFiles[I]; + auto &FileReport = FileReports[I]; + Pool.async([&] { + auto File = renderFile(Coverage, SourceFile, FileReport, Options); + { + std::lock_guard<std::mutex> Lock(FileArrayMutex); + FileArray.push_back(std::move(File)); + } + }); + } + Pool.wait(); return FileArray; } @@ -178,12 +203,22 @@ void CoverageExporterJson::renderRoot(ArrayRef<std::string> SourceFiles) { FileCoverageSummary Totals = FileCoverageSummary("Totals"); auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals, SourceFiles, Options); - auto Export = - json::Object({{"files", renderFiles(Coverage, SourceFiles, FileReports, - Options.ExportSummaryOnly)}, - {"totals", renderSummary(Totals)}}); - // Skip functions-level information for summary-only export mode. - if (!Options.ExportSummaryOnly) + auto Files = renderFiles(Coverage, SourceFiles, FileReports, Options); + // Sort files in order of their names. + std::sort(Files.begin(), Files.end(), + [](const json::Value &A, const json::Value &B) { + const json::Object *ObjA = A.getAsObject(); + const json::Object *ObjB = B.getAsObject(); + assert(ObjA != nullptr && "Value A was not an Object"); + assert(ObjB != nullptr && "Value B was not an Object"); + const StringRef FilenameA = ObjA->getString("filename").getValue(); + const StringRef FilenameB = ObjB->getString("filename").getValue(); + return FilenameA.compare(FilenameB) < 0; + }); + auto Export = json::Object( + {{"files", std::move(Files)}, {"totals", renderSummary(Totals)}}); + // Skip functions-level information if necessary. + if (!Options.ExportSummaryOnly && !Options.SkipFunctions) Export["functions"] = renderFunctions(Coverage.getCoveredFunctions()); auto ExportArray = json::Array({std::move(Export)}); diff --git a/contrib/llvm/tools/llvm-cov/CoverageExporterJson.h b/contrib/llvm/tools/llvm-cov/CoverageExporterJson.h index c37c86b42be9..c19475005552 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageExporterJson.h +++ b/contrib/llvm/tools/llvm-cov/CoverageExporterJson.h @@ -1,9 +1,8 @@ //===- CoverageExporterJson.h - Code coverage JSON exporter ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/contrib/llvm/tools/llvm-cov/CoverageExporterLcov.cpp index d149ba1a4c87..d9b0c3b0d7a8 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageExporterLcov.cpp +++ b/contrib/llvm/tools/llvm-cov/CoverageExporterLcov.cpp @@ -1,9 +1,8 @@ //===- CoverageExporterLcov.cpp - Code coverage export --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -83,7 +82,7 @@ void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, OS << "SF:" << Filename << '\n'; if (!ExportSummaryOnly) { - renderFunctions(OS, Coverage.getCoveredFunctions()); + renderFunctions(OS, Coverage.getCoveredFunctions(Filename)); } renderFunctionSummary(OS, FileReport); diff --git a/contrib/llvm/tools/llvm-cov/CoverageExporterLcov.h b/contrib/llvm/tools/llvm-cov/CoverageExporterLcov.h index 539b2dacd384..e8a260bf4937 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageExporterLcov.h +++ b/contrib/llvm/tools/llvm-cov/CoverageExporterLcov.h @@ -1,9 +1,8 @@ //===- CoverageExporterLcov.h - Code coverage lcov exporter ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp b/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp index 4dd0f552c7e0..ca241e386e87 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp +++ b/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp @@ -1,9 +1,8 @@ //===- CoverageFilters.cpp - Function coverage mapping filters ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cov/CoverageFilters.h b/contrib/llvm/tools/llvm-cov/CoverageFilters.h index 6424ca5a8081..ce56e1607111 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageFilters.h +++ b/contrib/llvm/tools/llvm-cov/CoverageFilters.h @@ -1,9 +1,8 @@ //===- CoverageFilters.h - Function coverage mapping filters --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cov/CoverageReport.cpp b/contrib/llvm/tools/llvm-cov/CoverageReport.cpp index 607a3ceb30cb..82259542c597 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageReport.cpp +++ b/contrib/llvm/tools/llvm-cov/CoverageReport.cpp @@ -1,9 +1,8 @@ //===- CoverageReport.cpp - Code coverage report -------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cov/CoverageReport.h b/contrib/llvm/tools/llvm-cov/CoverageReport.h index 4a6527e9fe5d..f9a092f510b5 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageReport.h +++ b/contrib/llvm/tools/llvm-cov/CoverageReport.h @@ -1,9 +1,8 @@ //===- CoverageReport.h - Code coverage report ----------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp index 7847a2abf48c..1029f7784040 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp +++ b/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp @@ -1,9 +1,8 @@ //===- CoverageSummaryInfo.cpp - Coverage summary for function/file -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h b/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h index 0845e2ce2e77..97beacb26d07 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h +++ b/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h @@ -1,9 +1,8 @@ //===- CoverageSummaryInfo.h - Coverage summary for function/file ---------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h b/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h index c8a472860027..dde0c692ab05 100644 --- a/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h +++ b/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h @@ -1,9 +1,8 @@ //===- CoverageViewOptions.h - Code coverage display options -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -35,6 +34,8 @@ struct CoverageViewOptions { bool ShowRegionSummary; bool ShowInstantiationSummary; bool ExportSummaryOnly; + bool SkipExpansions; + bool SkipFunctions; OutputFormat Format; std::string ShowOutputDirectory; std::vector<std::string> DemanglerOpts; diff --git a/contrib/llvm/tools/llvm-cov/RenderingSupport.h b/contrib/llvm/tools/llvm-cov/RenderingSupport.h index 2cfe24919142..0674fbac9a3c 100644 --- a/contrib/llvm/tools/llvm-cov/RenderingSupport.h +++ b/contrib/llvm/tools/llvm-cov/RenderingSupport.h @@ -1,9 +1,8 @@ //===- RenderingSupport.h - output stream rendering support functions ----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp b/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp index cebaf63adb12..616f667e2c84 100644 --- a/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp +++ b/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp @@ -1,9 +1,8 @@ //===- SourceCoverageView.cpp - Code coverage view for source code --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -190,8 +189,8 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile, // We need the expansions and instantiations sorted so we can go through them // while we iterate lines. - std::stable_sort(ExpansionSubViews.begin(), ExpansionSubViews.end()); - std::stable_sort(InstantiationSubViews.begin(), InstantiationSubViews.end()); + llvm::stable_sort(ExpansionSubViews); + llvm::stable_sort(InstantiationSubViews); auto NextESV = ExpansionSubViews.begin(); auto EndESV = ExpansionSubViews.end(); auto NextISV = InstantiationSubViews.begin(); diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageView.h b/contrib/llvm/tools/llvm-cov/SourceCoverageView.h index e3a2f9e5c0b4..9ae928443651 100644 --- a/contrib/llvm/tools/llvm-cov/SourceCoverageView.h +++ b/contrib/llvm/tools/llvm-cov/SourceCoverageView.h @@ -1,9 +1,8 @@ //===- SourceCoverageView.h - Code coverage view for source code ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp index 3f730bb7bc82..e3332245f9c8 100644 --- a/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -1,9 +1,8 @@ //===- SourceCoverageViewHTML.cpp - A html code coverage view -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.h b/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.h index cb41fcaf37b9..9834040008a6 100644 --- a/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.h +++ b/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.h @@ -1,9 +1,8 @@ //===- SourceCoverageViewHTML.h - A html code coverage view ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.cpp index aac70baed613..fcabee2ee69d 100644 --- a/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.cpp +++ b/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.cpp @@ -1,9 +1,8 @@ //===- SourceCoverageViewText.cpp - A text-based code coverage view -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.h b/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.h index a46f35cc6495..c8c4632c3b9d 100644 --- a/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.h +++ b/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.h @@ -1,9 +1,8 @@ //===- SourceCoverageViewText.h - A text-based code coverage view ---------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/llvm-cov/TestingSupport.cpp b/contrib/llvm/tools/llvm-cov/TestingSupport.cpp index 16a1c2665299..3ee318c9c640 100644 --- a/contrib/llvm/tools/llvm-cov/TestingSupport.cpp +++ b/contrib/llvm/tools/llvm-cov/TestingSupport.cpp @@ -1,9 +1,8 @@ //===- TestingSupport.cpp - Convert objects files into test files --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -70,9 +69,18 @@ int convertForTestingMain(int argc, const char *argv[]) { uint64_t ProfileNamesAddress = ProfileNames.getAddress(); StringRef CoverageMappingData; StringRef ProfileNamesData; - if (CoverageMapping.getContents(CoverageMappingData) || - ProfileNames.getContents(ProfileNamesData)) + if (Expected<StringRef> E = CoverageMapping.getContents()) + CoverageMappingData = *E; + else { + consumeError(E.takeError()); + return 1; + } + if (Expected<StringRef> E = ProfileNames.getContents()) + ProfileNamesData = *E; + else { + consumeError(E.takeError()); return 1; + } int FD; if (auto Err = sys::fs::openFileForWrite(OutputFilename, FD)) { diff --git a/contrib/llvm/tools/llvm-cov/gcov.cpp b/contrib/llvm/tools/llvm-cov/gcov.cpp index 7776f2aa9a68..8a00ff64711f 100644 --- a/contrib/llvm/tools/llvm-cov/gcov.cpp +++ b/contrib/llvm/tools/llvm-cov/gcov.cpp @@ -1,9 +1,8 @@ //===- gcov.cpp - GCOV compatible LLVM coverage tool ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -125,6 +124,11 @@ int gcovMain(int argc, const char *argv[]) { "(requires -b)")); cl::alias UncondBranchA("unconditional-branches", cl::aliasopt(UncondBranch)); + cl::opt<bool> HashFilenames("x", cl::Grouping, cl::init(false), + cl::desc("Hash long pathnames")); + cl::alias HashFilenamesA("hash-filenames", cl::aliasopt(HashFilenames)); + + cl::OptionCategory DebugCat("Internal and debugging options"); cl::opt<bool> DumpGCOV("dump", cl::init(false), cl::cat(DebugCat), cl::desc("Dump the gcov file to stderr")); @@ -136,7 +140,8 @@ int gcovMain(int argc, const char *argv[]) { cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); GCOV::Options Options(AllBlocks, BranchProb, BranchCount, FuncSummary, - PreservePaths, UncondBranch, LongNames, NoOutput); + PreservePaths, UncondBranch, LongNames, NoOutput, + HashFilenames); for (const auto &SourceFile : SourceFiles) reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV, diff --git a/contrib/llvm/tools/llvm-cov/llvm-cov.cpp b/contrib/llvm/tools/llvm-cov/llvm-cov.cpp index 4c3b574451c3..172ec9f3cedf 100644 --- a/contrib/llvm/tools/llvm-cov/llvm-cov.cpp +++ b/contrib/llvm/tools/llvm-cov/llvm-cov.cpp @@ -1,9 +1,8 @@ //===- llvm-cov.cpp - LLVM coverage tool ----------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cxxdump/Error.cpp b/contrib/llvm/tools/llvm-cxxdump/Error.cpp index 54207fad32af..25317820409c 100644 --- a/contrib/llvm/tools/llvm-cxxdump/Error.cpp +++ b/contrib/llvm/tools/llvm-cxxdump/Error.cpp @@ -1,9 +1,8 @@ //===- Error.cpp - system_error extensions for llvm-cxxdump -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cxxdump/Error.h b/contrib/llvm/tools/llvm-cxxdump/Error.h index 7caf6d6447c9..439902fa3803 100644 --- a/contrib/llvm/tools/llvm-cxxdump/Error.h +++ b/contrib/llvm/tools/llvm-cxxdump/Error.h @@ -1,9 +1,8 @@ //===- Error.h - system_error extensions for llvm-cxxdump -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp b/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp index 7594066a395d..833312655788 100644 --- a/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp +++ b/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp @@ -1,9 +1,8 @@ //===- llvm-cxxdump.cpp - Dump C++ data in an Object File -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -49,15 +48,20 @@ static void error(std::error_code EC) { exit(1); } -static void error(Error Err) { - if (!Err) - return; +LLVM_ATTRIBUTE_NORETURN static void error(Error Err) { logAllUnhandledErrors(std::move(Err), WithColor::error(outs()), "reading file: "); outs().flush(); exit(1); } +template <typename T> +T unwrapOrError(Expected<T> EO) { + if (!EO) + error(EO.takeError()); + return std::move(*EO); +} + } // namespace llvm static void reportError(StringRef Input, StringRef Message) { @@ -196,8 +200,7 @@ static void dumpCXXData(const ObjectFile *Obj) { // Skip virtual or BSS sections. if (Sec.isBSS() || Sec.isVirtual()) continue; - StringRef SecContents; - error(Sec.getContents(SecContents)); + StringRef SecContents = unwrapOrError(Sec.getContents()); Expected<uint64_t> SymAddressOrErr = Sym.getAddress(); error(errorToErrorCode(SymAddressOrErr.takeError())); uint64_t SymAddress = *SymAddressOrErr; @@ -511,7 +514,8 @@ static void dumpArchive(const Archive *Arc) { else reportError(Arc->getFileName(), cxxdump_error::unrecognized_file_format); } - error(std::move(Err)); + if (Err) + error(std::move(Err)); } static void dumpInput(StringRef File) { diff --git a/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.h b/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.h index daa05cb2ca0a..739cfe481a4b 100644 --- a/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.h +++ b/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.h @@ -1,9 +1,8 @@ //===-- llvm-cxxdump.h ------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/contrib/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp index afc1e4a8d128..9ac8bcf0ff01 100644 --- a/contrib/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp +++ b/contrib/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp @@ -1,12 +1,12 @@ //===-- llvm-c++filt.cpp --------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 "llvm/ADT/StringExtras.h" #include "llvm/Demangle/Demangle.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" @@ -25,7 +25,7 @@ enum Style { EDG, ///< EDG compiler GNUv3, ///< GNU C++ v3 ABI Java, ///< Java (gcj) - GNAT ///< ADA copiler (gnat) + GNAT ///< ADA compiler (gnat) }; static cl::opt<Style> Format("format", cl::desc("decoration style"), @@ -52,31 +52,84 @@ static cl::alias TypesShort("t", cl::desc("alias for --types"), static cl::list<std::string> Decorated(cl::Positional, cl::desc("<mangled>"), cl::ZeroOrMore); -static void demangle(llvm::raw_ostream &OS, const std::string &Mangled) { +static cl::extrahelp + HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); + +static std::string demangle(llvm::raw_ostream &OS, const std::string &Mangled) { int Status; - const char *Decorated = Mangled.c_str(); + const char *DecoratedStr = Mangled.c_str(); if (StripUnderscore) - if (Decorated[0] == '_') - ++Decorated; - size_t DecoratedLength = strlen(Decorated); + if (DecoratedStr[0] == '_') + ++DecoratedStr; + size_t DecoratedLength = strlen(DecoratedStr); char *Undecorated = nullptr; - if (Types || ((DecoratedLength >= 2 && strncmp(Decorated, "_Z", 2) == 0) || - (DecoratedLength >= 4 && strncmp(Decorated, "___Z", 4) == 0))) - Undecorated = itaniumDemangle(Decorated, nullptr, nullptr, &Status); + if (Types || + ((DecoratedLength >= 2 && strncmp(DecoratedStr, "_Z", 2) == 0) || + (DecoratedLength >= 4 && strncmp(DecoratedStr, "___Z", 4) == 0))) + Undecorated = itaniumDemangle(DecoratedStr, nullptr, nullptr, &Status); if (!Undecorated && - (DecoratedLength > 6 && strncmp(Decorated, "__imp_", 6) == 0)) { + (DecoratedLength > 6 && strncmp(DecoratedStr, "__imp_", 6) == 0)) { OS << "import thunk for "; - Undecorated = itaniumDemangle(Decorated + 6, nullptr, nullptr, &Status); + Undecorated = itaniumDemangle(DecoratedStr + 6, nullptr, nullptr, &Status); } - OS << (Undecorated ? Undecorated : Mangled) << '\n'; - OS.flush(); - + std::string Result(Undecorated ? Undecorated : Mangled); free(Undecorated); + return Result; +} + +// Split 'Source' on any character that fails to pass 'IsLegalChar'. The +// returned vector consists of pairs where 'first' is the delimited word, and +// 'second' are the delimiters following that word. +static void SplitStringDelims( + StringRef Source, + SmallVectorImpl<std::pair<StringRef, StringRef>> &OutFragments, + function_ref<bool(char)> IsLegalChar) { + // The beginning of the input string. + const auto Head = Source.begin(); + + // Obtain any leading delimiters. + auto Start = std::find_if(Head, Source.end(), IsLegalChar); + if (Start != Head) + OutFragments.push_back({"", Source.slice(0, Start - Head)}); + + // Capture each word and the delimiters following that word. + while (Start != Source.end()) { + Start = std::find_if(Start, Source.end(), IsLegalChar); + auto End = std::find_if_not(Start, Source.end(), IsLegalChar); + auto DEnd = std::find_if(End, Source.end(), IsLegalChar); + OutFragments.push_back({Source.slice(Start - Head, End - Head), + Source.slice(End - Head, DEnd - Head)}); + Start = DEnd; + } +} + +// This returns true if 'C' is a character that can show up in an +// Itanium-mangled string. +static bool IsLegalItaniumChar(char C) { + // Itanium CXX ABI [External Names]p5.1.1: + // '$' and '.' in mangled names are reserved for private implementations. + return isalnum(C) || C == '.' || C == '$' || C == '_'; +} + +// If 'Split' is true, then 'Mangled' is broken into individual words and each +// word is demangled. Otherwise, the entire string is treated as a single +// mangled item. The result is output to 'OS'. +static void demangleLine(llvm::raw_ostream &OS, StringRef Mangled, bool Split) { + std::string Result; + if (Split) { + SmallVector<std::pair<StringRef, StringRef>, 16> Words; + SplitStringDelims(Mangled, Words, IsLegalItaniumChar); + for (const auto &Word : Words) + Result += demangle(OS, Word.first) + Word.second.str(); + } else + Result = demangle(OS, Mangled); + OS << Result << '\n'; + OS.flush(); } int main(int argc, char **argv) { @@ -86,10 +139,10 @@ int main(int argc, char **argv) { if (Decorated.empty()) for (std::string Mangled; std::getline(std::cin, Mangled);) - demangle(llvm::outs(), Mangled); + demangleLine(llvm::outs(), Mangled, true); else for (const auto &Symbol : Decorated) - demangle(llvm::outs(), Symbol); + demangleLine(llvm::outs(), Symbol, false); return EXIT_SUCCESS; } diff --git a/contrib/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp b/contrib/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp index 39028cc86723..87d4d06bbc96 100644 --- a/contrib/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp +++ b/contrib/llvm/tools/llvm-cxxmap/llvm-cxxmap.cpp @@ -1,9 +1,8 @@ //===- llvm-cxxmap.cpp ----------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-diff/DiffConsumer.cpp b/contrib/llvm/tools/llvm-diff/DiffConsumer.cpp index ec189df27521..b797143bde1b 100644 --- a/contrib/llvm/tools/llvm-diff/DiffConsumer.cpp +++ b/contrib/llvm/tools/llvm-diff/DiffConsumer.cpp @@ -1,9 +1,8 @@ //===-- DiffConsumer.cpp - Difference Consumer ------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -13,8 +12,8 @@ #include "DiffConsumer.h" #include "llvm/IR/Instructions.h" -#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" using namespace llvm; diff --git a/contrib/llvm/tools/llvm-diff/DiffConsumer.h b/contrib/llvm/tools/llvm-diff/DiffConsumer.h index 82f5ce598b44..6cb8f2eb7eeb 100644 --- a/contrib/llvm/tools/llvm-diff/DiffConsumer.h +++ b/contrib/llvm/tools/llvm-diff/DiffConsumer.h @@ -1,9 +1,8 @@ //===-- DiffConsumer.h - Difference Consumer --------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-diff/DiffLog.cpp b/contrib/llvm/tools/llvm-diff/DiffLog.cpp index 50c0c4cff2fc..6484197521f2 100644 --- a/contrib/llvm/tools/llvm-diff/DiffLog.cpp +++ b/contrib/llvm/tools/llvm-diff/DiffLog.cpp @@ -1,9 +1,8 @@ //===-- DiffLog.h - Difference Log Builder and accessories ------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-diff/DiffLog.h b/contrib/llvm/tools/llvm-diff/DiffLog.h index 8f28461afdde..0c8952496155 100644 --- a/contrib/llvm/tools/llvm-diff/DiffLog.h +++ b/contrib/llvm/tools/llvm-diff/DiffLog.h @@ -1,9 +1,8 @@ //===-- DiffLog.h - Difference Log Builder and accessories ------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-diff/DifferenceEngine.cpp b/contrib/llvm/tools/llvm-diff/DifferenceEngine.cpp index acff8bb3e89b..bc93ece86490 100644 --- a/contrib/llvm/tools/llvm-diff/DifferenceEngine.cpp +++ b/contrib/llvm/tools/llvm-diff/DifferenceEngine.cpp @@ -1,9 +1,8 @@ //===-- DifferenceEngine.cpp - Structural function/module comparison ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -68,7 +67,7 @@ public: unsigned NewSize = Storage.size() - 1; if (NewSize) { // Move the slot at the end to the beginning. - if (isPodLike<T>::value) + if (is_trivially_copyable<T>::value) Storage[0] = Storage[NewSize]; else std::swap(Storage[0], Storage[NewSize]); diff --git a/contrib/llvm/tools/llvm-diff/DifferenceEngine.h b/contrib/llvm/tools/llvm-diff/DifferenceEngine.h index 7f084a377f0c..da1b6526a6e2 100644 --- a/contrib/llvm/tools/llvm-diff/DifferenceEngine.h +++ b/contrib/llvm/tools/llvm-diff/DifferenceEngine.h @@ -1,9 +1,8 @@ //===-- DifferenceEngine.h - Module comparator ------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-diff/llvm-diff.cpp b/contrib/llvm/tools/llvm-diff/llvm-diff.cpp index e449d6994784..aaf7989e2e3d 100644 --- a/contrib/llvm/tools/llvm-diff/llvm-diff.cpp +++ b/contrib/llvm/tools/llvm-diff/llvm-diff.cpp @@ -1,9 +1,8 @@ //===-- llvm-diff.cpp - Module comparator command-line driver ---*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-dis/llvm-dis.cpp b/contrib/llvm/tools/llvm-dis/llvm-dis.cpp index 8143a2a5a934..3f337b874b16 100644 --- a/contrib/llvm/tools/llvm-dis/llvm-dis.cpp +++ b/contrib/llvm/tools/llvm-dis/llvm-dis.cpp @@ -1,9 +1,8 @@ //===-- llvm-dis.cpp - The low-level LLVM disassembler --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-dwarfdump/Statistics.cpp b/contrib/llvm/tools/llvm-dwarfdump/Statistics.cpp index 5fe7e8b4615b..f26369b935cb 100644 --- a/contrib/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/contrib/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -15,14 +15,38 @@ using namespace object; struct PerFunctionStats { /// Number of inlined instances of this function. unsigned NumFnInlined = 0; - /// Number of variables with location across all inlined instances. + /// Number of inlined instances that have abstract origins. + unsigned NumAbstractOrigins = 0; + /// Number of variables and parameters with location across all inlined + /// instances. unsigned TotalVarWithLoc = 0; /// Number of constants with location across all inlined instances. unsigned ConstantMembers = 0; - /// List of all Variables in this function. + /// List of all Variables and parameters in this function. StringSet<> VarsInFunction; /// Compile units also cover a PC range, but have this flag set to false. bool IsFunction = false; + /// Verify function definition has PC addresses (for detecting when + /// a function has been inlined everywhere). + bool HasPCAddresses = false; + /// Function has source location information. + bool HasSourceLocation = false; + /// Number of function parameters. + unsigned NumParams = 0; + /// Number of function parameters with source location. + unsigned NumParamSourceLocations = 0; + /// Number of function parameters with type. + unsigned NumParamTypes = 0; + /// Number of function parameters with a DW_AT_location. + unsigned NumParamLocations = 0; + /// Number of variables. + unsigned NumVars = 0; + /// Number of variables with source location. + unsigned NumVarSourceLocations = 0; + /// Number of variables wtih type. + unsigned NumVarTypes = 0; + /// Number of variables wtih DW_AT_location. + unsigned NumVarLocations = 0; }; /// Holds accumulated global statistics about DIEs. @@ -32,7 +56,8 @@ struct GlobalStats { /// Total number of PC range bytes in each variable's enclosing scope, /// starting from the first definition of the variable. unsigned ScopeBytesFromFirstDefinition = 0; - /// Total number of call site entries (DW_TAG_call_site). + /// Total number of call site entries (DW_TAG_call_site) or + /// (DW_AT_call_file & DW_AT_call_line). unsigned CallSiteEntries = 0; /// Total byte size of concrete functions. This byte size includes /// inline functions contained in the concrete functions. @@ -59,11 +84,13 @@ static uint64_t getLowPC(DWARFDie Die) { /// Collect debug info quality metrics for one DIE. static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, std::string VarPrefix, uint64_t ScopeLowPC, - uint64_t BytesInScope, - uint32_t InlineDepth, + uint64_t BytesInScope, uint32_t InlineDepth, StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats) { bool HasLoc = false; + bool HasSrcLoc = false; + bool HasType = false; + bool IsArtificial = false; uint64_t BytesCovered = 0; uint64_t OffsetToFirstDefinition = 0; @@ -79,6 +106,16 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, return; } + if (Die.findRecursively(dwarf::DW_AT_decl_file) && + Die.findRecursively(dwarf::DW_AT_decl_line)) + HasSrcLoc = true; + + if (Die.findRecursively(dwarf::DW_AT_type)) + HasType = true; + + if (Die.find(dwarf::DW_AT_artificial)) + IsArtificial = true; + if (Die.find(dwarf::DW_AT_const_value)) { // This catches constant members *and* variables. HasLoc = true; @@ -125,7 +162,7 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, // By using the variable name + the path through the lexical block tree, the // keys are consistent across duplicate abstract origins in different CUs. std::string VarName = StringRef(Die.getName(DINameKind::ShortName)); - FnStats.VarsInFunction.insert(VarPrefix+VarName); + FnStats.VarsInFunction.insert(VarPrefix + VarName); if (BytesInScope) { FnStats.TotalVarWithLoc += (unsigned)HasLoc; // Adjust for the fact the variables often start their lifetime in the @@ -136,16 +173,36 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, GlobalStats.ScopeBytesFromFirstDefinition += BytesInScope; assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytesFromFirstDefinition); - } else { + } else if (Die.getTag() == dwarf::DW_TAG_member) { FnStats.ConstantMembers++; + } else { + FnStats.TotalVarWithLoc += (unsigned)HasLoc; + } + if (!IsArtificial) { + if (Die.getTag() == dwarf::DW_TAG_formal_parameter) { + FnStats.NumParams++; + if (HasType) + FnStats.NumParamTypes++; + if (HasSrcLoc) + FnStats.NumParamSourceLocations++; + if (HasLoc) + FnStats.NumParamLocations++; + } else if (Die.getTag() == dwarf::DW_TAG_variable) { + FnStats.NumVars++; + if (HasType) + FnStats.NumVarTypes++; + if (HasSrcLoc) + FnStats.NumVarSourceLocations++; + if (HasLoc) + FnStats.NumVarLocations++; + } } } /// Recursively collect debug info quality metrics. static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, std::string VarPrefix, uint64_t ScopeLowPC, - uint64_t BytesInScope, - uint32_t InlineDepth, + uint64_t BytesInScope, uint32_t InlineDepth, StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats) { // Handle any kind of lexical scope. @@ -164,20 +221,9 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, if (Die.find(dwarf::DW_AT_declaration)) return; - // Count the function. - if (!IsBlock) { - StringRef Name = Die.getName(DINameKind::LinkageName); - if (Name.empty()) - Name = Die.getName(DINameKind::ShortName); - FnPrefix = Name; - // Skip over abstract origins. - if (Die.find(dwarf::DW_AT_inline)) - return; - // We've seen an (inlined) instance of this function. - auto &FnStats = FnStatMap[Name]; - FnStats.NumFnInlined++; - FnStats.IsFunction = true; - } + // Check for call sites. + if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line)) + GlobalStats.CallSiteEntries++; // PC Ranges. auto RangesOrError = Die.getAddressRanges(); @@ -192,6 +238,31 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, BytesInThisScope += Range.HighPC - Range.LowPC; ScopeLowPC = getLowPC(Die); + // Count the function. + if (!IsBlock) { + StringRef Name = Die.getName(DINameKind::LinkageName); + if (Name.empty()) + Name = Die.getName(DINameKind::ShortName); + FnPrefix = Name; + // Skip over abstract origins. + if (Die.find(dwarf::DW_AT_inline)) + return; + // We've seen an (inlined) instance of this function. + auto &FnStats = FnStatMap[Name]; + if (IsInlinedFunction) { + FnStats.NumFnInlined++; + if (Die.findRecursively(dwarf::DW_AT_abstract_origin)) + FnStats.NumAbstractOrigins++; + } + FnStats.IsFunction = true; + if (BytesInThisScope && !IsInlinedFunction) + FnStats.HasPCAddresses = true; + std::string FnName = StringRef(Die.getName(DINameKind::ShortName)); + if (Die.findRecursively(dwarf::DW_AT_decl_file) && + Die.findRecursively(dwarf::DW_AT_decl_line)) + FnStats.HasSourceLocation = true; + } + if (BytesInThisScope) { BytesInScope = BytesInThisScope; if (IsFunction) @@ -252,29 +323,53 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, GlobalStats GlobalStats; StringMap<PerFunctionStats> Statistics; for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) - if (DWARFDie CUDie = CU->getUnitDIE(false)) + if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) collectStatsRecursive(CUDie, "/", "g", 0, 0, 0, Statistics, GlobalStats); /// 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 = 1; - unsigned VarTotal = 0; - unsigned VarUnique = 0; - unsigned VarWithLoc = 0; + unsigned Version = 3; + unsigned VarParamTotal = 0; + unsigned VarParamUnique = 0; + unsigned VarParamWithLoc = 0; unsigned NumFunctions = 0; unsigned NumInlinedFunctions = 0; + unsigned NumFuncsWithSrcLoc = 0; + unsigned NumAbstractOrigins = 0; + unsigned ParamTotal = 0; + unsigned ParamWithType = 0; + unsigned ParamWithLoc = 0; + unsigned ParamWithSrcLoc = 0; + unsigned VarTotal = 0; + unsigned VarWithType = 0; + unsigned VarWithSrcLoc = 0; + unsigned VarWithLoc = 0; for (auto &Entry : Statistics) { PerFunctionStats &Stats = Entry.getValue(); unsigned TotalVars = Stats.VarsInFunction.size() * Stats.NumFnInlined; + // Count variables in concrete out-of-line functions and in global scope. + if (Stats.HasPCAddresses || !Stats.IsFunction) + TotalVars += Stats.VarsInFunction.size(); unsigned Constants = Stats.ConstantMembers; - VarWithLoc += Stats.TotalVarWithLoc + Constants; - VarTotal += TotalVars + Constants; - VarUnique += Stats.VarsInFunction.size(); - LLVM_DEBUG(for (auto &V : Stats.VarsInFunction) llvm::dbgs() + VarParamWithLoc += Stats.TotalVarWithLoc + Constants; + VarParamTotal += TotalVars; + VarParamUnique += Stats.VarsInFunction.size(); + LLVM_DEBUG(for (auto &V + : Stats.VarsInFunction) llvm::dbgs() << Entry.getKey() << ": " << V.getKey() << "\n"); NumFunctions += Stats.IsFunction; + NumFuncsWithSrcLoc += Stats.HasSourceLocation; NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; + NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins; + ParamTotal += Stats.NumParams; + ParamWithType += Stats.NumParamTypes; + ParamWithLoc += Stats.NumParamLocations; + ParamWithSrcLoc += Stats.NumParamSourceLocations; + VarTotal += Stats.NumVars; + VarWithType += Stats.NumVarTypes; + VarWithLoc += Stats.NumVarLocations; + VarWithSrcLoc += Stats.NumVarSourceLocations; } // Print summary. @@ -285,20 +380,31 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, printDatum(OS, "file", Filename.str()); printDatum(OS, "format", FormatName); printDatum(OS, "source functions", NumFunctions); + printDatum(OS, "source functions with location", NumFuncsWithSrcLoc); printDatum(OS, "inlined functions", NumInlinedFunctions); - printDatum(OS, "unique source variables", VarUnique); - printDatum(OS, "source variables", VarTotal); - printDatum(OS, "variables with location", VarWithLoc); + printDatum(OS, "inlined funcs with abstract origins", NumAbstractOrigins); + printDatum(OS, "unique source variables", VarParamUnique); + printDatum(OS, "source variables", VarParamTotal); + printDatum(OS, "variables with location", VarParamWithLoc); printDatum(OS, "call site entries", GlobalStats.CallSiteEntries); printDatum(OS, "scope bytes total", GlobalStats.ScopeBytesFromFirstDefinition); printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); printDatum(OS, "total function size", GlobalStats.FunctionSize); printDatum(OS, "total inlined function size", GlobalStats.InlineFunctionSize); + printDatum(OS, "total formal params", ParamTotal); + printDatum(OS, "formal params with source location", ParamWithSrcLoc); + printDatum(OS, "formal params with type", ParamWithType); + printDatum(OS, "formal params with binary location", ParamWithLoc); + printDatum(OS, "total vars", VarTotal); + printDatum(OS, "vars with source location", VarWithSrcLoc); + printDatum(OS, "vars with type", VarWithType); + printDatum(OS, "vars with binary location", VarWithLoc); OS << "}\n"; LLVM_DEBUG( llvm::dbgs() << "Total Availability: " - << (int)std::round((VarWithLoc * 100.0) / VarTotal) << "%\n"; + << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal) + << "%\n"; llvm::dbgs() << "PC Ranges covered: " << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) / GlobalStats.ScopeBytesFromFirstDefinition) diff --git a/contrib/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/contrib/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index d9e8e36efe5c..05a7aef67ece 100644 --- a/contrib/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/contrib/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -1,9 +1,8 @@ //===-- llvm-dwarfdump.cpp - Debug info dumping utility for llvm ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -93,8 +92,6 @@ namespace { using namespace cl; OptionCategory DwarfDumpCategory("Specific Options"); -static opt<bool> Help("h", desc("Alias for -help"), Hidden, - cat(DwarfDumpCategory)); static list<std::string> InputFilenames(Positional, desc("<input object files or .dSYM bundles>"), ZeroOrMore, cat(DwarfDumpCategory)); @@ -142,10 +139,9 @@ static list<std::string> "-name option can be used instead."), value_desc("name"), cat(DwarfDumpCategory)); static alias FindAlias("f", desc("Alias for -find."), aliasopt(Find)); -static opt<bool> - IgnoreCase("ignore-case", - desc("Ignore case distinctions in when searching by name."), - value_desc("i"), cat(DwarfDumpCategory)); +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 list<std::string> Name( @@ -155,17 +151,17 @@ static list<std::string> Name( "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 opt<unsigned long long> Lookup("lookup", +static opt<uint64_t> + Lookup("lookup", desc("Lookup <address> in the debug information and print out any " "available file, function, block and line table details."), value_desc("address"), cat(DwarfDumpCategory)); static opt<std::string> - OutputFilename("out-file", cl::init(""), + OutputFilename("o", cl::init("-"), cl::desc("Redirect output to the specified file."), - cl::value_desc("filename")); -static alias OutputFilenameAlias("o", desc("Alias for -out-file."), - aliasopt(OutputFilename), - cat(DwarfDumpCategory)); + cl::value_desc("filename"), cat(DwarfDumpCategory)); +static alias OutputFilenameAlias("out-file", desc("Alias for -o."), + aliasopt(OutputFilename)); static opt<bool> UseRegex("regex", desc("Treat any <pattern> strings as regular expressions when " @@ -175,14 +171,14 @@ static alias RegexAlias("x", desc("Alias for -regex"), aliasopt(UseRegex)); static opt<bool> ShowChildren("show-children", desc("Show a debug info entry's children when selectively " - "printing with the =<offset> option."), + "printing entries."), cat(DwarfDumpCategory)); static alias ShowChildrenAlias("c", desc("Alias for -show-children."), aliasopt(ShowChildren)); static opt<bool> ShowParents("show-parents", desc("Show a debug info entry's parents when selectively " - "printing with the =<offset> option."), + "printing entries."), cat(DwarfDumpCategory)); static alias ShowParentsAlias("p", desc("Alias for -show-parents."), aliasopt(ShowParents)); @@ -192,13 +188,18 @@ static opt<bool> cat(DwarfDumpCategory)); static alias ShowFormAlias("F", desc("Alias for -show-form."), aliasopt(ShowForm), cat(DwarfDumpCategory)); -static opt<unsigned> RecurseDepth( - "recurse-depth", - desc("Only recurse to a depth of N when displaying debug info entries."), - cat(DwarfDumpCategory), init(-1U), value_desc("N")); -static alias RecurseDepthAlias("r", desc("Alias for -recurse-depth."), - aliasopt(RecurseDepth)); - +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 opt<unsigned> + ParentRecurseDepth("parent-recurse-depth", + desc("Only recurse to a depth of N when displaying " + "parents of debug info entries."), + cat(DwarfDumpCategory), init(-1U), value_desc("N")); static opt<bool> SummarizeTypes("summarize-types", desc("Abbreviate the description of type unit entries."), @@ -219,6 +220,8 @@ static opt<bool> Verbose("verbose", cat(DwarfDumpCategory)); static alias VerboseAlias("v", desc("Alias for -verbose."), aliasopt(Verbose), cat(DwarfDumpCategory)); +static cl::extrahelp + HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); } // namespace /// @} //===----------------------------------------------------------------------===// @@ -233,7 +236,8 @@ static void error(StringRef Prefix, std::error_code EC) { static DIDumpOptions getDumpOpts() { DIDumpOptions DumpOpts; DumpOpts.DumpType = DumpType; - DumpOpts.RecurseDepth = RecurseDepth; + DumpOpts.ChildRecurseDepth = ChildRecurseDepth; + DumpOpts.ParentRecurseDepth = ParentRecurseDepth; DumpOpts.ShowAddresses = !Diff; DumpOpts.ShowChildren = ShowChildren; DumpOpts.ShowParents = ShowParents; @@ -259,19 +263,16 @@ static bool filterArch(ObjectFile &Obj) { return true; if (auto *MachO = dyn_cast<MachOObjectFile>(&Obj)) { - std::string ObjArch = - Triple::getArchTypeName(MachO->getArchTriple().getArch()); - for (auto Arch : ArchFilters) { - // Match name. - if (Arch == ObjArch) - return true; - // Match architecture number. unsigned Value; if (!StringRef(Arch).getAsInteger(0, Value)) if (Value == getCPUType(*MachO)) return true; + + // Match as name. + if (MachO->getArchTriple().getArch() == Triple(Arch).getArch()) + return true; } } return false; @@ -380,14 +381,19 @@ static void filterByAccelName(ArrayRef<std::string> Names, DWARFContext &DICtx, /// Handle the --lookup option and dump the DIEs and line info for the given /// address. -static bool lookup(DWARFContext &DICtx, uint64_t Address, raw_ostream &OS) { +/// TODO: specified Address for --lookup option could relate for several +/// different sections(in case not-linked object file). llvm-dwarfdump +/// need to do something with this: extend lookup option with section +/// information or probably display all matched entries, or something else... +static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address, + raw_ostream &OS) { auto DIEsForAddr = DICtx.getDIEsForAddress(Lookup); if (!DIEsForAddr) return false; DIDumpOptions DumpOpts = getDumpOpts(); - DumpOpts.RecurseDepth = 0; + DumpOpts.ChildRecurseDepth = 0; DIEsForAddr.CompileUnit->dump(OS, DumpOpts); if (DIEsForAddr.FunctionDIE) { DIEsForAddr.FunctionDIE.dump(OS, 2, DumpOpts); @@ -395,7 +401,10 @@ static bool lookup(DWARFContext &DICtx, uint64_t Address, raw_ostream &OS) { DIEsForAddr.BlockDIE.dump(OS, 4, DumpOpts); } - if (DILineInfo LineInfo = DICtx.getLineInfoForAddress(Lookup)) + // TODO: it is neccessary to set proper SectionIndex here. + // object::SectionedAddress::UndefSection works for only absolute addresses. + if (DILineInfo LineInfo = DICtx.getLineInfoForAddress( + {Lookup, object::SectionedAddress::UndefSection})) LineInfo.dump(OS); return true; @@ -414,7 +423,7 @@ static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename, // Handle the --lookup option. if (Lookup) - return lookup(DICtx, Lookup, OS); + return lookup(Obj, DICtx, Lookup, OS); // Handle the --name option. if (!Name.empty()) { @@ -566,11 +575,6 @@ int main(int argc, char **argv) { "pretty-print DWARF debug information in object files" " and debug info archives.\n"); - if (Help) { - PrintHelpMessage(/*Hidden =*/false, /*Categorized =*/true); - return 0; - } - // FIXME: Audit interactions between these two options and make them // compatible. if (Diff && Verbose) { @@ -579,17 +583,12 @@ int main(int argc, char **argv) { return 0; } - std::unique_ptr<ToolOutputFile> OutputFile; - if (!OutputFilename.empty()) { - std::error_code EC; - OutputFile = llvm::make_unique<ToolOutputFile>(OutputFilename, EC, - sys::fs::F_None); - error("Unable to open output file" + OutputFilename, EC); - // Don't remove output file if we exit with an error. - OutputFile->keep(); - } + std::error_code EC; + ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_None); + error("Unable to open output file" + OutputFilename, EC); + // Don't remove output file if we exit with an error. + OutputFile.keep(); - raw_ostream &OS = OutputFile ? OutputFile->os() : outs(); bool OffsetRequested = false; // Defaults to dumping all sections, unless brief mode is specified in which @@ -633,15 +632,15 @@ int main(int argc, char **argv) { if (Verify) { // If we encountered errors during verify, exit with a non-zero exit status. if (!all_of(Objects, [&](std::string Object) { - return handleFile(Object, verifyObjectFile, OS); + return handleFile(Object, verifyObjectFile, OutputFile.os()); })) - exit(1); + return 1; } else if (Statistics) for (auto Object : Objects) - handleFile(Object, collectStatsForObjectFile, OS); + handleFile(Object, collectStatsForObjectFile, OutputFile.os()); else for (auto Object : Objects) - handleFile(Object, dumpObjectFile, OS); + handleFile(Object, dumpObjectFile, OutputFile.os()); return EXIT_SUCCESS; } diff --git a/contrib/llvm/tools/llvm-extract/llvm-extract.cpp b/contrib/llvm/tools/llvm-extract/llvm-extract.cpp index 94aaa2f52eb5..300bc0b4bd52 100644 --- a/contrib/llvm/tools/llvm-extract/llvm-extract.cpp +++ b/contrib/llvm/tools/llvm-extract/llvm-extract.cpp @@ -1,9 +1,8 @@ //===- llvm-extract.cpp - LLVM function extraction utility ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -34,86 +33,99 @@ #include <memory> using namespace llvm; +cl::OptionCategory ExtractCat("llvm-extract Options"); + // InputFilename - The filename to read from. -static cl::opt<std::string> -InputFilename(cl::Positional, cl::desc("<input bitcode file>"), - cl::init("-"), cl::value_desc("filename")); +static cl::opt<std::string> InputFilename(cl::Positional, + cl::desc("<input bitcode file>"), + cl::init("-"), + cl::value_desc("filename")); -static cl::opt<std::string> -OutputFilename("o", cl::desc("Specify output filename"), - cl::value_desc("filename"), cl::init("-")); +static cl::opt<std::string> OutputFilename("o", + cl::desc("Specify output filename"), + cl::value_desc("filename"), + cl::init("-"), cl::cat(ExtractCat)); -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(ExtractCat)); -static cl::opt<bool> -DeleteFn("delete", cl::desc("Delete specified Globals from Module")); +static cl::opt<bool> DeleteFn("delete", + cl::desc("Delete specified Globals from Module"), + cl::cat(ExtractCat)); static cl::opt<bool> - Recursive("recursive", - cl::desc("Recursively extract all called functions")); + Recursive("recursive", cl::desc("Recursively extract all called functions"), + cl::cat(ExtractCat)); // ExtractFuncs - The functions to extract from the module. static cl::list<std::string> -ExtractFuncs("func", cl::desc("Specify function to extract"), - cl::ZeroOrMore, cl::value_desc("function")); + ExtractFuncs("func", cl::desc("Specify function to extract"), + cl::ZeroOrMore, cl::value_desc("function"), + cl::cat(ExtractCat)); // ExtractRegExpFuncs - The functions, matched via regular expression, to // extract from the module. static cl::list<std::string> -ExtractRegExpFuncs("rfunc", cl::desc("Specify function(s) to extract using a " - "regular expression"), - cl::ZeroOrMore, cl::value_desc("rfunction")); + ExtractRegExpFuncs("rfunc", + cl::desc("Specify function(s) to extract using a " + "regular expression"), + cl::ZeroOrMore, cl::value_desc("rfunction"), + cl::cat(ExtractCat)); // ExtractBlocks - The blocks to extract from the module. -static cl::list<std::string> - ExtractBlocks("bb", - cl::desc("Specify <function, basic block> pairs to extract"), - cl::ZeroOrMore, cl::value_desc("function:bb")); +static cl::list<std::string> ExtractBlocks( + "bb", cl::desc("Specify <function, basic block> pairs to extract"), + cl::ZeroOrMore, cl::value_desc("function:bb"), cl::cat(ExtractCat)); // ExtractAlias - The alias to extract from the module. static cl::list<std::string> -ExtractAliases("alias", cl::desc("Specify alias to extract"), - cl::ZeroOrMore, cl::value_desc("alias")); - + ExtractAliases("alias", cl::desc("Specify alias to extract"), + cl::ZeroOrMore, cl::value_desc("alias"), + cl::cat(ExtractCat)); // ExtractRegExpAliases - The aliases, matched via regular expression, to // extract from the module. static cl::list<std::string> -ExtractRegExpAliases("ralias", cl::desc("Specify alias(es) to extract using a " - "regular expression"), - cl::ZeroOrMore, cl::value_desc("ralias")); + ExtractRegExpAliases("ralias", + cl::desc("Specify alias(es) to extract using a " + "regular expression"), + cl::ZeroOrMore, cl::value_desc("ralias"), + cl::cat(ExtractCat)); // ExtractGlobals - The globals to extract from the module. static cl::list<std::string> -ExtractGlobals("glob", cl::desc("Specify global to extract"), - cl::ZeroOrMore, cl::value_desc("global")); + ExtractGlobals("glob", cl::desc("Specify global to extract"), + cl::ZeroOrMore, cl::value_desc("global"), + cl::cat(ExtractCat)); // ExtractRegExpGlobals - The globals, matched via regular expression, to // extract from the module... static cl::list<std::string> -ExtractRegExpGlobals("rglob", cl::desc("Specify global(s) to extract using a " - "regular expression"), - cl::ZeroOrMore, cl::value_desc("rglobal")); + ExtractRegExpGlobals("rglob", + cl::desc("Specify global(s) to extract using a " + "regular expression"), + cl::ZeroOrMore, cl::value_desc("rglobal"), + cl::cat(ExtractCat)); -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(ExtractCat)); 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(ExtractCat)); 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(ExtractCat)); int main(int argc, char **argv) { InitLLVM X(argc, argv); LLVMContext Context; + cl::HideUnrelatedOptions(ExtractCat); cl::ParseCommandLineOptions(argc, argv, "llvm extractor\n"); // Use lazy loading, since we only care about selected global values. @@ -230,7 +242,7 @@ int main(int argc, char **argv) { } // Figure out which BasicBlocks we should extract. - SmallVector<BasicBlock *, 4> BBs; + SmallVector<SmallVector<BasicBlock *, 16>, 4> GroupOfBBs; for (StringRef StrPair : ExtractBlocks) { auto BBInfo = StrPair.split(':'); // Get the function. @@ -242,17 +254,24 @@ int main(int argc, char **argv) { } // Do not materialize this function. GVs.insert(F); - // Get the basic block. - auto Res = llvm::find_if(*F, [&](const BasicBlock &BB) { - return BB.getName().equals(BBInfo.second); - }); - if (Res == F->end()) { - errs() << argv[0] << ": function " << F->getName() - << " doesn't contain a basic block named '" << BBInfo.second - << "'!\n"; - return 1; + // Get the basic blocks. + SmallVector<BasicBlock *, 16> BBs; + SmallVector<StringRef, 16> BBNames; + BBInfo.second.split(BBNames, ';', /*MaxSplit=*/-1, + /*KeepEmpty=*/false); + for (StringRef BBName : BBNames) { + auto Res = llvm::find_if(*F, [&](const BasicBlock &BB) { + return BB.getName().equals(BBName); + }); + if (Res == F->end()) { + errs() << argv[0] << ": function " << F->getName() + << " doesn't contain a basic block named '" << BBInfo.second + << "'!\n"; + return 1; + } + BBs.push_back(&*Res); } - BBs.push_back(&*Res); + GroupOfBBs.push_back(BBs); } // Use *argv instead of argv[0] to work around a wrong GCC warning. @@ -271,10 +290,10 @@ int main(int argc, char **argv) { ExitOnErr(F->materialize()); for (auto &BB : *F) { for (auto &I : BB) { - auto *CI = dyn_cast<CallInst>(&I); - if (!CI) + CallBase *CB = dyn_cast<CallBase>(&I); + if (!CB) continue; - Function *CF = CI->getCalledFunction(); + Function *CF = CB->getCalledFunction(); if (!CF) continue; if (CF->isDeclaration() || GVs.count(CF)) @@ -317,7 +336,7 @@ int main(int argc, char **argv) { // functions. if (!ExtractBlocks.empty()) { legacy::PassManager PM; - PM.add(createBlockExtractorPass(BBs, true)); + PM.add(createBlockExtractorPass(GroupOfBBs, true)); PM.run(*M); } diff --git a/contrib/llvm/tools/llvm-link/llvm-link.cpp b/contrib/llvm/tools/llvm-link/llvm-link.cpp index b7a888375b3d..50ba57178d02 100644 --- a/contrib/llvm/tools/llvm-link/llvm-link.cpp +++ b/contrib/llvm/tools/llvm-link/llvm-link.cpp @@ -1,9 +1,8 @@ //===- llvm-link.cpp - Low-level LLVM linker ------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-lto/llvm-lto.cpp b/contrib/llvm/tools/llvm-lto/llvm-lto.cpp index b6facc919b51..585207b25185 100644 --- a/contrib/llvm/tools/llvm-lto/llvm-lto.cpp +++ b/contrib/llvm/tools/llvm-lto/llvm-lto.cpp @@ -1,9 +1,8 @@ //===- llvm-lto: a simple command-line program to link modules with LTO ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -158,8 +157,8 @@ static cl::opt<int> ThinLTOCachePruningInterval("thinlto-cache-pruning-interval", cl::init(1200), cl::desc("Set ThinLTO cache pruning interval.")); -static cl::opt<unsigned long long> - ThinLTOCacheMaxSizeBytes("thinlto-cache-max-size-bytes", +static cl::opt<uint64_t> ThinLTOCacheMaxSizeBytes( + "thinlto-cache-max-size-bytes", cl::desc("Set ThinLTO cache pruning directory maximum size in bytes.")); static cl::opt<int> @@ -205,6 +204,10 @@ static cl::opt<bool> ListSymbolsOnly( "list-symbols-only", cl::init(false), cl::desc("Instead of running LTO, list the symbols in each IR file")); +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")); + static cl::opt<bool> SetMergedModule( "set-merged-module", cl::init(false), cl::desc("Use the first input module as the merged module")); @@ -373,6 +376,34 @@ static void listSymbols(const TargetOptions &Options) { } } +static std::unique_ptr<MemoryBuffer> loadFile(StringRef Filename) { + ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename.str() + + "': "); + return ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Filename))); +} + +static void listDependentLibraries() { + for (auto &Filename : InputFilenames) { + auto Buffer = loadFile(Filename); + std::string E; + std::unique_ptr<lto::InputFile> Input(LTOModule::createInputFile( + Buffer->getBufferStart(), Buffer->getBufferSize(), Filename.c_str(), + E)); + if (!Input) + error(E); + + // List the dependent libraries. + outs() << Filename << ":\n"; + for (size_t I = 0, C = LTOModule::getDependentLibraryCount(Input.get()); + I != C; ++I) { + size_t L = 0; + const char *S = LTOModule::getDependentLibrary(Input.get(), I, &L); + assert(S); + outs() << StringRef(S, L) << "\n"; + } + } +} + /// Create a combined index file from the input IR files and write it. /// /// This is meant to enable testing of ThinLTO combined index generation, @@ -450,22 +481,31 @@ std::unique_ptr<ModuleSummaryIndex> loadCombinedIndex() { return ExitOnErr(getModuleSummaryIndexForFile(ThinLTOIndex)); } -static std::unique_ptr<Module> loadModule(StringRef Filename, - LLVMContext &Ctx) { - SMDiagnostic Err; - std::unique_ptr<Module> M(parseIRFile(Filename, Err, Ctx)); - if (!M) { - Err.print("llvm-lto", errs()); - report_fatal_error("Can't load module for file " + Filename); - } - maybeVerifyModule(*M); +static std::unique_ptr<lto::InputFile> loadInputFile(MemoryBufferRef Buffer) { + ExitOnError ExitOnErr("llvm-lto: error loading input '" + + Buffer.getBufferIdentifier().str() + "': "); + return ExitOnErr(lto::InputFile::create(Buffer)); +} +static std::unique_ptr<Module> loadModuleFromInput(lto::InputFile &File, + LLVMContext &CTX) { + auto &Mod = File.getSingleBitcodeModule(); + auto ModuleOrErr = Mod.parseModule(CTX); + if (!ModuleOrErr) { + handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) { + SMDiagnostic Err = SMDiagnostic(Mod.getModuleIdentifier(), + SourceMgr::DK_Error, EIB.message()); + Err.print("llvm-lto", errs()); + }); + report_fatal_error("Can't load module, abort."); + } + maybeVerifyModule(**ModuleOrErr); if (ThinLTOModuleId.getNumOccurrences()) { if (InputFilenames.size() != 1) report_fatal_error("Can't override the module id for multiple files"); - M->setModuleIdentifier(ThinLTOModuleId); + (*ModuleOrErr)->setModuleIdentifier(ThinLTOModuleId); } - return M; + return std::move(*ModuleOrErr); } static void writeModuleToFile(Module &TheModule, StringRef Filename) { @@ -563,13 +603,15 @@ private: auto Index = loadCombinedIndex(); for (auto &Filename : InputFilenames) { LLVMContext Ctx; - auto TheModule = loadModule(Filename, Ctx); + auto Buffer = loadFile(Filename); + auto Input = loadInputFile(Buffer->getMemBufferRef()); + auto TheModule = loadModuleFromInput(*Input, Ctx); // Build a map of module to the GUIDs and summary objects that should // be written to its index. std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex; - ThinGenerator.gatherImportedSummariesForModule(*TheModule, *Index, - ModuleToSummariesForIndex); + ThinGenerator.gatherImportedSummariesForModule( + *TheModule, *Index, ModuleToSummariesForIndex, *Input); std::string OutputName = OutputFilename; if (OutputName.empty()) { @@ -598,13 +640,16 @@ private: auto Index = loadCombinedIndex(); for (auto &Filename : InputFilenames) { LLVMContext Ctx; - auto TheModule = loadModule(Filename, Ctx); + auto Buffer = loadFile(Filename); + auto Input = loadInputFile(Buffer->getMemBufferRef()); + auto TheModule = loadModuleFromInput(*Input, Ctx); std::string OutputName = OutputFilename; if (OutputName.empty()) { OutputName = Filename + ".imports"; } - OutputName = getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix); - ThinGenerator.emitImports(*TheModule, OutputName, *Index); + OutputName = + getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix); + ThinGenerator.emitImports(*TheModule, OutputName, *Index, *Input); } } @@ -622,9 +667,11 @@ private: auto Index = loadCombinedIndex(); for (auto &Filename : InputFilenames) { LLVMContext Ctx; - auto TheModule = loadModule(Filename, Ctx); + auto Buffer = loadFile(Filename); + auto Input = loadInputFile(Buffer->getMemBufferRef()); + auto TheModule = loadModuleFromInput(*Input, Ctx); - ThinGenerator.promote(*TheModule, *Index); + ThinGenerator.promote(*TheModule, *Index, *Input); std::string OutputName = OutputFilename; if (OutputName.empty()) { @@ -653,9 +700,11 @@ private: for (auto &Filename : InputFilenames) { LLVMContext Ctx; - auto TheModule = loadModule(Filename, Ctx); + auto Buffer = loadFile(Filename); + auto Input = loadInputFile(Buffer->getMemBufferRef()); + auto TheModule = loadModuleFromInput(*Input, Ctx); - ThinGenerator.crossModuleImport(*TheModule, *Index); + ThinGenerator.crossModuleImport(*TheModule, *Index, *Input); std::string OutputName = OutputFilename; if (OutputName.empty()) { @@ -684,9 +733,11 @@ private: for (auto &Filename : InputFilenames) { LLVMContext Ctx; - auto TheModule = loadModule(Filename, Ctx); + auto Buffer = loadFile(Filename); + auto Input = loadInputFile(Buffer->getMemBufferRef()); + auto TheModule = loadModuleFromInput(*Input, Ctx); - ThinGenerator.internalize(*TheModule, *Index); + ThinGenerator.internalize(*TheModule, *Index, *Input); std::string OutputName = OutputFilename; if (OutputName.empty()) { @@ -707,7 +758,9 @@ private: for (auto &Filename : InputFilenames) { LLVMContext Ctx; - auto TheModule = loadModule(Filename, Ctx); + auto Buffer = loadFile(Filename); + auto Input = loadInputFile(Buffer->getMemBufferRef()); + auto TheModule = loadModuleFromInput(*Input, Ctx); ThinGenerator.optimize(*TheModule); @@ -827,6 +880,11 @@ int main(int argc, char **argv) { return 0; } + if (ListDependentLibrariesOnly) { + listDependentLibraries(); + return 0; + } + if (IndexStats) { printIndexStats(); return 0; diff --git a/contrib/llvm/tools/llvm-lto2/llvm-lto2.cpp b/contrib/llvm/tools/llvm-lto2/llvm-lto2.cpp index 26426367e252..0bd9289dc938 100644 --- a/contrib/llvm/tools/llvm-lto2/llvm-lto2.cpp +++ b/contrib/llvm/tools/llvm-lto2/llvm-lto2.cpp @@ -1,9 +1,8 @@ //===-- llvm-lto2: test harness for the resolution-based LTO interface ----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -92,19 +91,40 @@ static cl::opt<std::string> DefaultTriple( cl::desc( "Replace unspecified target triples in input files with this triple")); +static cl::opt<bool> RemarksWithHotness( + "pass-remarks-with-hotness", + cl::desc("With PGO, include profile count in optimization remarks"), + cl::Hidden); + static cl::opt<std::string> - OptRemarksOutput("pass-remarks-output", - cl::desc("YAML output file for optimization remarks")); + RemarksFilename("pass-remarks-output", + cl::desc("Output filename for pass remarks"), + cl::value_desc("filename")); -static cl::opt<bool> OptRemarksWithHotness( - "pass-remarks-with-hotness", - cl::desc("Whether to include hotness informations in the remarks.\n" - "Has effect only if -pass-remarks-output is specified.")); +static cl::opt<std::string> + RemarksPasses("pass-remarks-filter", + cl::desc("Only record optimization remarks from passes whose " + "names match the given regular expression"), + cl::value_desc("regex")); + +static cl::opt<std::string> RemarksFormat( + "pass-remarks-format", + cl::desc("The format used for serializing remarks (default: YAML)"), + cl::value_desc("format"), cl::init("yaml")); static cl::opt<std::string> SamplePGOFile("lto-sample-profile-file", cl::desc("Specify a SamplePGO profile file")); +static cl::opt<std::string> + CSPGOFile("lto-cspgo-profile-file", + cl::desc("Specify a context sensitive PGO profile file")); + +static cl::opt<bool> + RunCSIRInstr("lto-cspgo-gen", + cl::desc("Run PGO context sensitive IR instrumentation"), + cl::init(false), cl::Hidden); + static cl::opt<bool> UseNewPM("use-new-pm", cl::desc("Run LTO passes using the new pass manager"), @@ -211,10 +231,14 @@ static int run(int argc, char **argv) { "Config::addSaveTemps failed"); // Optimization remarks. - Conf.RemarksFilename = OptRemarksOutput; - Conf.RemarksWithHotness = OptRemarksWithHotness; + Conf.RemarksFilename = RemarksFilename; + Conf.RemarksPasses = RemarksPasses; + Conf.RemarksWithHotness = RemarksWithHotness; + Conf.RemarksFormat = RemarksFormat; Conf.SampleProfile = SamplePGOFile; + Conf.CSIRProfile = CSPGOFile; + Conf.RunCSIRInstr = RunCSIRInstr; // Run a custom pipeline, if asked for. Conf.OptPipeline = OptPipeline; @@ -343,6 +367,13 @@ static int dumpSymtab(int argc, char **argv) { if (TT.isOSBinFormatCOFF()) outs() << "linker opts: " << Input->getCOFFLinkerOpts() << '\n'; + if (TT.isOSBinFormatELF()) { + outs() << "dependent libraries:"; + for (auto L : Input->getDependentLibraries()) + outs() << " \"" << L << "\""; + outs() << '\n'; + } + std::vector<StringRef> ComdatTable = Input->getComdatTable(); for (const InputFile::Symbol &Sym : Input->symbols()) { switch (Sym.getVisibility()) { diff --git a/contrib/llvm/tools/llvm-mc/Disassembler.cpp b/contrib/llvm/tools/llvm-mc/Disassembler.cpp index acc5a5f4cab2..e2af2e7f2e32 100644 --- a/contrib/llvm/tools/llvm-mc/Disassembler.cpp +++ b/contrib/llvm/tools/llvm-mc/Disassembler.cpp @@ -1,9 +1,8 @@ //===- Disassembler.cpp - Disassembler for hex strings --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-mc/Disassembler.h b/contrib/llvm/tools/llvm-mc/Disassembler.h index 1f18ac075f85..11b685233abc 100644 --- a/contrib/llvm/tools/llvm-mc/Disassembler.h +++ b/contrib/llvm/tools/llvm-mc/Disassembler.h @@ -1,9 +1,8 @@ //===- Disassembler.h - Text File Disassembler ----------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-mc/llvm-mc.cpp b/contrib/llvm/tools/llvm-mc/llvm-mc.cpp index c0976502f545..ec189c297860 100644 --- a/contrib/llvm/tools/llvm-mc/llvm-mc.cpp +++ b/contrib/llvm/tools/llvm-mc/llvm-mc.cpp @@ -1,9 +1,8 @@ //===-- llvm-mc.cpp - Machine Code Hacking Driver ---------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -402,18 +401,8 @@ int main(int argc, char **argv) { } if (!MainFileName.empty()) Ctx.setMainFileName(MainFileName); - if (GenDwarfForAssembly && DwarfVersion >= 5) { - // DWARF v5 needs the root file as well as the compilation directory. - // If we find a '.file 0' directive that will supersede these values. - MD5 Hash; - MD5::MD5Result *Cksum = - (MD5::MD5Result *)Ctx.allocate(sizeof(MD5::MD5Result), 1); - Hash.update(Buffer->getBuffer()); - Hash.final(*Cksum); - Ctx.setMCLineTableRootFile( - /*CUID=*/0, Ctx.getCompilationDir(), - !MainFileName.empty() ? MainFileName : InputFilename, Cksum, None); - } + if (GenDwarfForAssembly) + Ctx.setGenDwarfRootFile(InputFilename, Buffer->getBuffer()); // Package up features to be passed to target/subtarget std::string FeaturesStr; diff --git a/contrib/llvm/tools/llvm-mca/CodeRegion.cpp b/contrib/llvm/tools/llvm-mca/CodeRegion.cpp index 29a27c50c171..bf592f67245e 100644 --- a/contrib/llvm/tools/llvm-mca/CodeRegion.cpp +++ b/contrib/llvm/tools/llvm-mca/CodeRegion.cpp @@ -1,9 +1,8 @@ //===-------------------------- CodeRegion.cpp -----------------*- C++ -* -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -17,7 +16,12 @@ namespace llvm { namespace mca { -bool CodeRegion::isLocInRange(llvm::SMLoc Loc) const { +CodeRegions::CodeRegions(llvm::SourceMgr &S) : SM(S), FoundErrors(false) { + // Create a default region for the input code sequence. + Regions.emplace_back(make_unique<CodeRegion>("", SMLoc())); +} + +bool CodeRegion::isLocInRange(SMLoc Loc) const { if (RangeEnd.isValid() && Loc.getPointer() > RangeEnd.getPointer()) return false; if (RangeStart.isValid() && Loc.getPointer() < RangeStart.getPointer()) @@ -25,42 +29,88 @@ bool CodeRegion::isLocInRange(llvm::SMLoc Loc) const { return true; } -void CodeRegions::beginRegion(llvm::StringRef Description, llvm::SMLoc Loc) { - assert(!Regions.empty() && "Missing Default region"); - const CodeRegion &CurrentRegion = *Regions.back(); - if (CurrentRegion.startLoc().isValid() && !CurrentRegion.endLoc().isValid()) { - SM.PrintMessage(Loc, llvm::SourceMgr::DK_Warning, - "Ignoring invalid region start"); - return; +void CodeRegions::beginRegion(StringRef Description, SMLoc Loc) { + if (ActiveRegions.empty()) { + // Remove the default region if there is at least one user defined region. + // By construction, only the default region has an invalid start location. + if (Regions.size() == 1 && !Regions[0]->startLoc().isValid() && + !Regions[0]->endLoc().isValid()) { + ActiveRegions[Description] = 0; + Regions[0] = make_unique<CodeRegion>(Description, Loc); + return; + } + } else { + auto It = ActiveRegions.find(Description); + if (It != ActiveRegions.end()) { + const CodeRegion &R = *Regions[It->second]; + if (Description.empty()) { + SM.PrintMessage(Loc, SourceMgr::DK_Error, + "found multiple overlapping anonymous regions"); + SM.PrintMessage(R.startLoc(), SourceMgr::DK_Note, + "Previous anonymous region was defined here"); + FoundErrors = true; + return; + } + + SM.PrintMessage(Loc, SourceMgr::DK_Error, + "overlapping regions cannot have the same name"); + SM.PrintMessage(R.startLoc(), SourceMgr::DK_Note, + "region " + Description + " was previously defined here"); + FoundErrors = true; + return; + } } - // Remove the default region if there are user defined regions. - if (!CurrentRegion.startLoc().isValid()) - Regions.erase(Regions.begin()); - addRegion(Description, Loc); + ActiveRegions[Description] = Regions.size(); + Regions.emplace_back(make_unique<CodeRegion>(Description, Loc)); + return; } -void CodeRegions::endRegion(llvm::SMLoc Loc) { - assert(!Regions.empty() && "Missing Default region"); - CodeRegion &CurrentRegion = *Regions.back(); - if (CurrentRegion.endLoc().isValid()) { - SM.PrintMessage(Loc, llvm::SourceMgr::DK_Warning, - "Ignoring invalid region end"); +void CodeRegions::endRegion(StringRef Description, SMLoc Loc) { + if (Description.empty()) { + // Special case where there is only one user defined region, + // and this LLVM-MCA-END directive doesn't provide a region name. + // In this case, we assume that the user simply wanted to just terminate + // the only active region. + if (ActiveRegions.size() == 1) { + auto It = ActiveRegions.begin(); + Regions[It->second]->setEndLocation(Loc); + ActiveRegions.erase(It); + return; + } + + // Special case where the region end marker applies to the default region. + if (ActiveRegions.empty() && Regions.size() == 1 && + !Regions[0]->startLoc().isValid() && !Regions[0]->endLoc().isValid()) { + Regions[0]->setEndLocation(Loc); + return; + } + } + + auto It = ActiveRegions.find(Description); + if (It != ActiveRegions.end()) { + Regions[It->second]->setEndLocation(Loc); + ActiveRegions.erase(It); return; } - CurrentRegion.setEndLocation(Loc); + FoundErrors = true; + SM.PrintMessage(Loc, SourceMgr::DK_Error, + "found an invalid region end directive"); + if (!Description.empty()) { + SM.PrintMessage(Loc, SourceMgr::DK_Note, + "unable to find an active region named " + Description); + } else { + SM.PrintMessage(Loc, SourceMgr::DK_Note, + "unable to find an active anonymous region"); + } } -void CodeRegions::addInstruction(const llvm::MCInst &Instruction) { - const llvm::SMLoc &Loc = Instruction.getLoc(); - const auto It = - std::find_if(Regions.rbegin(), Regions.rend(), - [Loc](const std::unique_ptr<CodeRegion> &Region) { - return Region->isLocInRange(Loc); - }); - if (It != Regions.rend()) - (*It)->addInstruction(Instruction); +void CodeRegions::addInstruction(const MCInst &Instruction) { + SMLoc Loc = Instruction.getLoc(); + for (UniqueCodeRegion &Region : Regions) + if (Region->isLocInRange(Loc)) + Region->addInstruction(Instruction); } } // namespace mca diff --git a/contrib/llvm/tools/llvm-mca/CodeRegion.h b/contrib/llvm/tools/llvm-mca/CodeRegion.h index 867aa18bb4fe..cabb4a5d4484 100644 --- a/contrib/llvm/tools/llvm-mca/CodeRegion.h +++ b/contrib/llvm/tools/llvm-mca/CodeRegion.h @@ -1,9 +1,8 @@ //===-------------------------- CodeRegion.h -------------------*- C++ -* -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -35,6 +34,7 @@ #define LLVM_TOOLS_LLVM_MCA_CODEREGION_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCInst.h" #include "llvm/Support/SMLoc.h" @@ -51,7 +51,7 @@ class CodeRegion { // An optional descriptor for this region. llvm::StringRef Description; // Instructions that form this region. - std::vector<llvm::MCInst> Instructions; + llvm::SmallVector<llvm::MCInst, 8> Instructions; // Source location range. llvm::SMLoc RangeStart; llvm::SMLoc RangeEnd; @@ -79,24 +79,25 @@ public: llvm::StringRef getDescription() const { return Description; } }; +class CodeRegionParseError final : public Error {}; + class CodeRegions { // A source manager. Used by the tool to generate meaningful warnings. llvm::SourceMgr &SM; - std::vector<std::unique_ptr<CodeRegion>> Regions; - - // Construct a new region of code guarded by LLVM-MCA comments. - void addRegion(llvm::StringRef Description, llvm::SMLoc Loc) { - Regions.emplace_back(llvm::make_unique<CodeRegion>(Description, Loc)); - } + using UniqueCodeRegion = std::unique_ptr<CodeRegion>; + std::vector<UniqueCodeRegion> Regions; + llvm::StringMap<unsigned> ActiveRegions; + bool FoundErrors; CodeRegions(const CodeRegions &) = delete; CodeRegions &operator=(const CodeRegions &) = delete; public: - typedef std::vector<std::unique_ptr<CodeRegion>>::iterator iterator; - typedef std::vector<std::unique_ptr<CodeRegion>>::const_iterator - const_iterator; + CodeRegions(llvm::SourceMgr &S); + + typedef std::vector<UniqueCodeRegion>::iterator iterator; + typedef std::vector<UniqueCodeRegion>::const_iterator const_iterator; iterator begin() { return Regions.begin(); } iterator end() { return Regions.end(); } @@ -104,24 +105,21 @@ public: const_iterator end() const { return Regions.cend(); } void beginRegion(llvm::StringRef Description, llvm::SMLoc Loc); - void endRegion(llvm::SMLoc Loc); + void endRegion(llvm::StringRef Description, llvm::SMLoc Loc); void addInstruction(const llvm::MCInst &Instruction); llvm::SourceMgr &getSourceMgr() const { return SM; } - CodeRegions(llvm::SourceMgr &S) : SM(S) { - // Create a default region for the input code sequence. - addRegion("Default", llvm::SMLoc()); - } - llvm::ArrayRef<llvm::MCInst> getInstructionSequence(unsigned Idx) const { return Regions[Idx]->getInstructions(); } bool empty() const { - return llvm::all_of(Regions, [](const std::unique_ptr<CodeRegion> &Region) { + return llvm::all_of(Regions, [](const UniqueCodeRegion &Region) { return Region->empty(); }); } + + bool isValid() const { return !FoundErrors; } }; } // namespace mca diff --git a/contrib/llvm/tools/llvm-mca/CodeRegionGenerator.cpp b/contrib/llvm/tools/llvm-mca/CodeRegionGenerator.cpp index 5bd37adeeae9..c793169e64e0 100644 --- a/contrib/llvm/tools/llvm-mca/CodeRegionGenerator.cpp +++ b/contrib/llvm/tools/llvm-mca/CodeRegionGenerator.cpp @@ -1,9 +1,8 @@ //===----------------------- CodeRegionGenerator.cpp ------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -49,8 +48,7 @@ public: // We only want to intercept the emission of new instructions. virtual void EmitInstruction(const MCInst &Inst, - const MCSubtargetInfo & /* unused */, - bool /* unused */) override { + const MCSubtargetInfo &/* unused */) override { Regions.addInstruction(Inst); } @@ -88,7 +86,11 @@ void MCACommentConsumer::HandleComment(SMLoc Loc, StringRef CommentText) { Comment = Comment.drop_front(Position); if (Comment.consume_front("LLVM-MCA-END")) { - Regions.endRegion(Loc); + // Skip spaces and tabs. + Position = Comment.find_first_not_of(" \t"); + if (Position < Comment.size()) + Comment = Comment.drop_front(Position); + Regions.endRegion(Comment, Loc); return; } @@ -117,7 +119,6 @@ Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions() { MCACommentConsumer CC(Regions); Lexer.setCommentConsumer(&CC); - // Create a target-specific parser and perform the parse. std::unique_ptr<MCTargetAsmParser> TAP( TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts)); if (!TAP) @@ -127,7 +128,7 @@ Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions() { Parser->setTargetParser(*TAP); Parser->Run(false); - // Get the assembler dialect from the input. llvm-mca will use this as the + // Set the assembler dialect from the input. llvm-mca will use this as the // default dialect when printing reports. AssemblerDialect = Parser->getAssemblerDialect(); return Regions; diff --git a/contrib/llvm/tools/llvm-mca/CodeRegionGenerator.h b/contrib/llvm/tools/llvm-mca/CodeRegionGenerator.h index 892cafb92686..9a10aa2c148b 100644 --- a/contrib/llvm/tools/llvm-mca/CodeRegionGenerator.h +++ b/contrib/llvm/tools/llvm-mca/CodeRegionGenerator.h @@ -1,9 +1,8 @@ //===----------------------- CodeRegionGenerator.h --------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/PipelinePrinter.cpp b/contrib/llvm/tools/llvm-mca/PipelinePrinter.cpp index 18ef45fc2a65..90d468075996 100644 --- a/contrib/llvm/tools/llvm-mca/PipelinePrinter.cpp +++ b/contrib/llvm/tools/llvm-mca/PipelinePrinter.cpp @@ -1,9 +1,8 @@ //===--------------------- PipelinePrinter.cpp ------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/PipelinePrinter.h b/contrib/llvm/tools/llvm-mca/PipelinePrinter.h index 456026e12df3..004309cd7b8e 100644 --- a/contrib/llvm/tools/llvm-mca/PipelinePrinter.h +++ b/contrib/llvm/tools/llvm-mca/PipelinePrinter.h @@ -1,9 +1,8 @@ //===--------------------- PipelinePrinter.h --------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp b/contrib/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp new file mode 100644 index 000000000000..560c6c6e8a33 --- /dev/null +++ b/contrib/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp @@ -0,0 +1,624 @@ +//===--------------------- BottleneckAnalysis.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 the functionalities used by the BottleneckAnalysis +/// to report bottleneck info. +/// +//===----------------------------------------------------------------------===// + +#include "Views/BottleneckAnalysis.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MCA/Support.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormattedStream.h" + +namespace llvm { +namespace mca { + +#define DEBUG_TYPE "llvm-mca" + +PressureTracker::PressureTracker(const MCSchedModel &Model) + : SM(Model), + ResourcePressureDistribution(Model.getNumProcResourceKinds(), 0), + ProcResID2Mask(Model.getNumProcResourceKinds(), 0), + ResIdx2ProcResID(Model.getNumProcResourceKinds(), 0), + ProcResID2ResourceUsersIndex(Model.getNumProcResourceKinds(), 0) { + computeProcResourceMasks(SM, ProcResID2Mask); + + // Ignore the invalid resource at index zero. + unsigned NextResourceUsersIdx = 0; + for (unsigned I = 1, E = Model.getNumProcResourceKinds(); I < E; ++I) { + const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); + ProcResID2ResourceUsersIndex[I] = NextResourceUsersIdx; + NextResourceUsersIdx += ProcResource.NumUnits; + uint64_t ResourceMask = ProcResID2Mask[I]; + ResIdx2ProcResID[getResourceStateIndex(ResourceMask)] = I; + } + + ResourceUsers.resize(NextResourceUsersIdx); + std::fill(ResourceUsers.begin(), ResourceUsers.end(), + std::make_pair<unsigned, unsigned>(~0U, 0U)); +} + +void PressureTracker::getResourceUsers(uint64_t ResourceMask, + SmallVectorImpl<User> &Users) const { + unsigned Index = getResourceStateIndex(ResourceMask); + unsigned ProcResID = ResIdx2ProcResID[Index]; + const MCProcResourceDesc &PRDesc = *SM.getProcResource(ProcResID); + for (unsigned I = 0, E = PRDesc.NumUnits; I < E; ++I) { + const User U = getResourceUser(ProcResID, I); + if (U.second && IPI.find(U.first) != IPI.end()) + Users.emplace_back(U); + } +} + +void PressureTracker::onInstructionDispatched(unsigned IID) { + IPI.insert(std::make_pair(IID, InstructionPressureInfo())); +} + +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]; + Index += countTrailingZeros(RR.second); + ResourceUsers[Index] = std::make_pair(IID, Use.second.getNumerator()); + } +} + +void PressureTracker::updateResourcePressureDistribution( + uint64_t CumulativeMask) { + while (CumulativeMask) { + uint64_t Current = CumulativeMask & (-CumulativeMask); + unsigned ResIdx = getResourceStateIndex(Current); + unsigned ProcResID = ResIdx2ProcResID[ResIdx]; + uint64_t Mask = ProcResID2Mask[ProcResID]; + + if (Mask == Current) { + ResourcePressureDistribution[ProcResID]++; + CumulativeMask ^= Current; + continue; + } + + Mask ^= Current; + while (Mask) { + uint64_t SubUnit = Mask & (-Mask); + ResIdx = getResourceStateIndex(SubUnit); + ProcResID = ResIdx2ProcResID[ResIdx]; + ResourcePressureDistribution[ProcResID]++; + Mask ^= SubUnit; + } + + CumulativeMask ^= Current; + } +} + +void PressureTracker::handlePressureEvent(const HWPressureEvent &Event) { + assert(Event.Reason != HWPressureEvent::INVALID && + "Unexpected invalid event!"); + + switch (Event.Reason) { + default: + break; + + case HWPressureEvent::RESOURCES: { + const uint64_t ResourceMask = Event.ResourceMask; + updateResourcePressureDistribution(Event.ResourceMask); + + for (const InstRef &IR : Event.AffectedInstructions) { + const Instruction &IS = *IR.getInstruction(); + unsigned BusyResources = IS.getCriticalResourceMask() & ResourceMask; + if (!BusyResources) + continue; + + unsigned IID = IR.getSourceIndex(); + IPI[IID].ResourcePressureCycles++; + } + break; + } + + case HWPressureEvent::REGISTER_DEPS: + for (const InstRef &IR : Event.AffectedInstructions) { + unsigned IID = IR.getSourceIndex(); + IPI[IID].RegisterPressureCycles++; + } + break; + + case HWPressureEvent::MEMORY_DEPS: + for (const InstRef &IR : Event.AffectedInstructions) { + unsigned IID = IR.getSourceIndex(); + IPI[IID].MemoryPressureCycles++; + } + } +} + +#ifndef NDEBUG +void DependencyGraph::dumpDependencyEdge(raw_ostream &OS, + const DependencyEdge &DepEdge, + MCInstPrinter &MCIP) const { + unsigned FromIID = DepEdge.FromIID; + unsigned ToIID = DepEdge.ToIID; + assert(FromIID < ToIID && "Graph should be acyclic!"); + + const DependencyEdge::Dependency &DE = DepEdge.Dep; + assert(DE.Type != DependencyEdge::DT_INVALID && "Unexpected invalid edge!"); + + OS << " FROM: " << FromIID << " TO: " << ToIID << " "; + if (DE.Type == DependencyEdge::DT_REGISTER) { + OS << " - REGISTER: "; + MCIP.printRegName(OS, DE.ResourceOrRegID); + } else if (DE.Type == DependencyEdge::DT_MEMORY) { + OS << " - MEMORY"; + } else { + assert(DE.Type == DependencyEdge::DT_RESOURCE && + "Unsupported dependency type!"); + OS << " - RESOURCE MASK: " << DE.ResourceOrRegID; + } + OS << " - CYCLES: " << DE.Cost << '\n'; +} +#endif // NDEBUG + +void DependencyGraph::initializeRootSet( + SmallVectorImpl<unsigned> &RootSet) const { + for (unsigned I = 0, E = Nodes.size(); I < E; ++I) { + const DGNode &N = Nodes[I]; + if (N.NumPredecessors == 0 && !N.OutgoingEdges.empty()) + RootSet.emplace_back(I); + } +} + +void DependencyGraph::propagateThroughEdges( + SmallVectorImpl<unsigned> &RootSet) { + SmallVector<unsigned, 8> ToVisit; + + // A critical sequence is computed as the longest path from a node of the + // RootSet to a leaf node (i.e. a node with no successors). The RootSet is + // composed of nodes with at least one successor, and no predecessors. + // + // Each node of the graph starts with an initial default cost of zero. The + // cost of a node is a measure of criticality: the higher the cost, the bigger + // is the performance impact. + // + // This algorithm is very similar to a (reverse) Dijkstra. Every iteration of + // the inner loop selects (i.e. visits) a node N from a set of `unvisited + // nodes`, and then propagates the cost of N to all its neighbors. + // + // 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. + do { + for (unsigned IID : RootSet) { + const DGNode &N = Nodes[IID]; + for (const DependencyEdge &DepEdge : N.OutgoingEdges) { + unsigned ToIID = DepEdge.ToIID; + DGNode &To = Nodes[ToIID]; + uint64_t Cost = N.Cost + DepEdge.Dep.Cost; + // Check if this is the most expensive incoming edge seen so far. In + // case, update the total cost of the destination node (ToIID), as well + // its field `CriticalPredecessor`. + if (Cost > To.Cost) { + To.CriticalPredecessor = DepEdge; + To.Cost = Cost; + To.Depth = N.Depth + 1; + } + To.NumVisitedPredecessors++; + if (To.NumVisitedPredecessors == To.NumPredecessors) + ToVisit.emplace_back(ToIID); + } + } + + std::swap(RootSet, ToVisit); + ToVisit.clear(); + } while (!RootSet.empty()); +} + +void DependencyGraph::getCriticalSequence( + SmallVectorImpl<const DependencyEdge *> &Seq) const { + // At this stage, nodes of the graph have been already visited, and costs have + // been propagated through the edges (see method `propagateThroughEdges()`). + + // Identify the node N with the highest cost in the graph. By construction, + // 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). + const auto It = std::max_element( + Nodes.begin(), Nodes.end(), + [](const DGNode &Lhs, const DGNode &Rhs) { return Lhs.Cost < Rhs.Cost; }); + unsigned IID = std::distance(Nodes.begin(), It); + Seq.resize(Nodes[IID].Depth); + for (unsigned I = Seq.size(), E = 0; I > E; --I) { + const DGNode &N = Nodes[IID]; + Seq[I - 1] = &N.CriticalPredecessor; + IID = N.CriticalPredecessor.FromIID; + } +} + +static void printInstruction(formatted_raw_ostream &FOS, + const MCSubtargetInfo &STI, MCInstPrinter &MCIP, + const MCInst &MCI, + bool UseDifferentColor = false) { + std::string Instruction; + raw_string_ostream InstrStream(Instruction); + + FOS.PadToColumn(14); + + MCIP.printInst(&MCI, InstrStream, "", STI); + InstrStream.flush(); + + if (UseDifferentColor) + FOS.changeColor(raw_ostream::CYAN, true, false); + FOS << StringRef(Instruction).ltrim(); + if (UseDifferentColor) + FOS.resetColor(); +} + +void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const { + SmallVector<const DependencyEdge *, 16> Seq; + DG.getCriticalSequence(Seq); + if (Seq.empty()) + return; + + OS << "\nCritical sequence based on the simulation:\n\n"; + + const DependencyEdge &FirstEdge = *Seq[0]; + unsigned FromIID = FirstEdge.FromIID % Source.size(); + unsigned ToIID = FirstEdge.ToIID % Source.size(); + bool IsLoopCarried = FromIID >= ToIID; + + formatted_raw_ostream FOS(OS); + FOS.PadToColumn(14); + FOS << "Instruction"; + FOS.PadToColumn(58); + FOS << "Dependency Information"; + + bool HasColors = FOS.has_colors(); + + unsigned CurrentIID = 0; + if (IsLoopCarried) { + FOS << "\n +----< " << FromIID << "."; + printInstruction(FOS, STI, MCIP, Source[FromIID], HasColors); + FOS << "\n |\n | < loop carried > \n |"; + } else { + while (CurrentIID < FromIID) { + FOS << "\n " << CurrentIID << "."; + printInstruction(FOS, STI, MCIP, Source[CurrentIID]); + CurrentIID++; + } + + FOS << "\n +----< " << CurrentIID << "."; + printInstruction(FOS, STI, MCIP, Source[CurrentIID], HasColors); + CurrentIID++; + } + + for (const DependencyEdge *&DE : Seq) { + ToIID = DE->ToIID % Source.size(); + unsigned LastIID = CurrentIID > ToIID ? Source.size() : ToIID; + + while (CurrentIID < LastIID) { + FOS << "\n | " << CurrentIID << "."; + printInstruction(FOS, STI, MCIP, Source[CurrentIID]); + CurrentIID++; + } + + if (CurrentIID == ToIID) { + FOS << "\n +----> " << ToIID << "."; + printInstruction(FOS, STI, MCIP, Source[CurrentIID], HasColors); + } else { + FOS << "\n |\n | < loop carried > \n |" + << "\n +----> " << ToIID << "."; + printInstruction(FOS, STI, MCIP, Source[ToIID], HasColors); + } + FOS.PadToColumn(58); + + const DependencyEdge::Dependency &Dep = DE->Dep; + if (HasColors) + FOS.changeColor(raw_ostream::SAVEDCOLOR, true, false); + + if (Dep.Type == DependencyEdge::DT_REGISTER) { + FOS << "## REGISTER dependency: "; + if (HasColors) + FOS.changeColor(raw_ostream::MAGENTA, true, false); + MCIP.printRegName(FOS, Dep.ResourceOrRegID); + } else if (Dep.Type == DependencyEdge::DT_MEMORY) { + FOS << "## MEMORY dependency."; + } else { + assert(Dep.Type == DependencyEdge::DT_RESOURCE && + "Unsupported dependency type!"); + FOS << "## RESOURCE interference: "; + if (HasColors) + FOS.changeColor(raw_ostream::MAGENTA, true, false); + FOS << Tracker.resolveResourceName(Dep.ResourceOrRegID); + if (HasColors) { + FOS.resetColor(); + FOS.changeColor(raw_ostream::SAVEDCOLOR, true, false); + } + FOS << " [ probability: " << ((DE->Frequency * 100) / Iterations) + << "% ]"; + } + if (HasColors) + FOS.resetColor(); + ++CurrentIID; + } + + while (CurrentIID < Source.size()) { + FOS << "\n " << CurrentIID << "."; + printInstruction(FOS, STI, MCIP, Source[CurrentIID]); + CurrentIID++; + } + + FOS << '\n'; + FOS.flush(); +} + +#ifndef NDEBUG +void DependencyGraph::dump(raw_ostream &OS, MCInstPrinter &MCIP) const { + OS << "\nREG DEPS\n"; + for (const DGNode &Node : Nodes) + for (const DependencyEdge &DE : Node.OutgoingEdges) + if (DE.Dep.Type == DependencyEdge::DT_REGISTER) + dumpDependencyEdge(OS, DE, MCIP); + + OS << "\nMEM DEPS\n"; + for (const DGNode &Node : Nodes) + for (const DependencyEdge &DE : Node.OutgoingEdges) + if (DE.Dep.Type == DependencyEdge::DT_MEMORY) + dumpDependencyEdge(OS, DE, MCIP); + + OS << "\nRESOURCE DEPS\n"; + for (const DGNode &Node : Nodes) + for (const DependencyEdge &DE : Node.OutgoingEdges) + if (DE.Dep.Type == DependencyEdge::DT_RESOURCE) + dumpDependencyEdge(OS, DE, MCIP); +} +#endif // NDEBUG + +void DependencyGraph::addDependency(unsigned From, unsigned To, + DependencyEdge::Dependency &&Dep) { + DGNode &NodeFrom = Nodes[From]; + DGNode &NodeTo = Nodes[To]; + SmallVectorImpl<DependencyEdge> &Vec = NodeFrom.OutgoingEdges; + + auto It = find_if(Vec, [To, Dep](DependencyEdge &DE) { + return DE.ToIID == To && DE.Dep.ResourceOrRegID == Dep.ResourceOrRegID; + }); + + if (It != Vec.end()) { + It->Dep.Cost += Dep.Cost; + It->Frequency++; + return; + } + + DependencyEdge DE = {Dep, From, To, 1}; + Vec.emplace_back(DE); + NodeTo.NumPredecessors++; +} + +BottleneckAnalysis::BottleneckAnalysis(const MCSubtargetInfo &sti, + MCInstPrinter &Printer, + ArrayRef<MCInst> S, unsigned NumIter) + : STI(sti), MCIP(Printer), Tracker(STI.getSchedModel()), DG(S.size() * 3), + Source(S), Iterations(NumIter), TotalCycles(0), + PressureIncreasedBecauseOfResources(false), + PressureIncreasedBecauseOfRegisterDependencies(false), + PressureIncreasedBecauseOfMemoryDependencies(false), + SeenStallCycles(false), BPI() {} + +void BottleneckAnalysis::addRegisterDep(unsigned From, unsigned To, + unsigned RegID, unsigned Cost) { + bool IsLoopCarried = From >= To; + unsigned SourceSize = Source.size(); + if (IsLoopCarried) { + Cost *= Iterations / 2; + DG.addRegisterDep(From, To + SourceSize, RegID, Cost); + DG.addRegisterDep(From + SourceSize, To + (SourceSize * 2), RegID, Cost); + return; + } + DG.addRegisterDep(From + SourceSize, To + SourceSize, RegID, Cost); +} + +void BottleneckAnalysis::addMemoryDep(unsigned From, unsigned To, + unsigned Cost) { + bool IsLoopCarried = From >= To; + unsigned SourceSize = Source.size(); + if (IsLoopCarried) { + Cost *= Iterations / 2; + DG.addMemoryDep(From, To + SourceSize, Cost); + DG.addMemoryDep(From + SourceSize, To + (SourceSize * 2), Cost); + return; + } + DG.addMemoryDep(From + SourceSize, To + SourceSize, Cost); +} + +void BottleneckAnalysis::addResourceDep(unsigned From, unsigned To, + uint64_t Mask, unsigned Cost) { + bool IsLoopCarried = From >= To; + unsigned SourceSize = Source.size(); + if (IsLoopCarried) { + Cost *= Iterations / 2; + DG.addResourceDep(From, To + SourceSize, Mask, Cost); + DG.addResourceDep(From + SourceSize, To + (SourceSize * 2), Mask, Cost); + return; + } + DG.addResourceDep(From + SourceSize, To + SourceSize, Mask, Cost); +} + +void BottleneckAnalysis::onEvent(const HWInstructionEvent &Event) { + const unsigned IID = Event.IR.getSourceIndex(); + if (Event.Type == HWInstructionEvent::Dispatched) { + Tracker.onInstructionDispatched(IID); + return; + } + if (Event.Type == HWInstructionEvent::Executed) { + Tracker.onInstructionExecuted(IID); + return; + } + + if (Event.Type != HWInstructionEvent::Issued) + return; + + const Instruction &IS = *Event.IR.getInstruction(); + unsigned To = IID % Source.size(); + + unsigned Cycles = 2 * Tracker.getResourcePressureCycles(IID); + uint64_t ResourceMask = IS.getCriticalResourceMask(); + SmallVector<std::pair<unsigned, unsigned>, 4> Users; + while (ResourceMask) { + uint64_t Current = ResourceMask & (-ResourceMask); + Tracker.getResourceUsers(Current, Users); + for (const std::pair<unsigned, unsigned> &U : Users) + addResourceDep(U.first % Source.size(), To, Current, U.second + Cycles); + Users.clear(); + ResourceMask ^= Current; + } + + const CriticalDependency &RegDep = IS.getCriticalRegDep(); + if (RegDep.Cycles) { + Cycles = RegDep.Cycles + 2 * Tracker.getRegisterPressureCycles(IID); + unsigned From = RegDep.IID % Source.size(); + addRegisterDep(From, To, RegDep.RegID, Cycles); + } + + const CriticalDependency &MemDep = IS.getCriticalMemDep(); + if (MemDep.Cycles) { + Cycles = MemDep.Cycles + 2 * Tracker.getMemoryPressureCycles(IID); + unsigned From = MemDep.IID % Source.size(); + addMemoryDep(From, To, Cycles); + } + + Tracker.handleInstructionIssuedEvent( + static_cast<const HWInstructionIssuedEvent &>(Event)); + + // Check if this is the last simulated instruction. + if (IID == ((Iterations * Source.size()) - 1)) + DG.finalizeGraph(); +} + +void BottleneckAnalysis::onEvent(const HWPressureEvent &Event) { + assert(Event.Reason != HWPressureEvent::INVALID && + "Unexpected invalid event!"); + + Tracker.handlePressureEvent(Event); + + switch (Event.Reason) { + default: + break; + + case HWPressureEvent::RESOURCES: + PressureIncreasedBecauseOfResources = true; + break; + case HWPressureEvent::REGISTER_DEPS: + PressureIncreasedBecauseOfRegisterDependencies = true; + break; + case HWPressureEvent::MEMORY_DEPS: + PressureIncreasedBecauseOfMemoryDependencies = true; + break; + } +} + +void BottleneckAnalysis::onCycleEnd() { + ++TotalCycles; + + bool PressureIncreasedBecauseOfDataDependencies = + PressureIncreasedBecauseOfRegisterDependencies || + PressureIncreasedBecauseOfMemoryDependencies; + if (!PressureIncreasedBecauseOfResources && + !PressureIncreasedBecauseOfDataDependencies) + return; + + ++BPI.PressureIncreaseCycles; + if (PressureIncreasedBecauseOfRegisterDependencies) + ++BPI.RegisterDependencyCycles; + if (PressureIncreasedBecauseOfMemoryDependencies) + ++BPI.MemoryDependencyCycles; + if (PressureIncreasedBecauseOfDataDependencies) + ++BPI.DataDependencyCycles; + if (PressureIncreasedBecauseOfResources) + ++BPI.ResourcePressureCycles; + PressureIncreasedBecauseOfResources = false; + PressureIncreasedBecauseOfRegisterDependencies = false; + PressureIncreasedBecauseOfMemoryDependencies = false; +} + +void BottleneckAnalysis::printBottleneckHints(raw_ostream &OS) const { + if (!SeenStallCycles || !BPI.PressureIncreaseCycles) { + OS << "\n\nNo resource or data dependency bottlenecks discovered.\n"; + return; + } + + double PressurePerCycle = + (double)BPI.PressureIncreaseCycles * 100 / TotalCycles; + double ResourcePressurePerCycle = + (double)BPI.ResourcePressureCycles * 100 / TotalCycles; + double DDPerCycle = (double)BPI.DataDependencyCycles * 100 / TotalCycles; + double RegDepPressurePerCycle = + (double)BPI.RegisterDependencyCycles * 100 / TotalCycles; + double MemDepPressurePerCycle = + (double)BPI.MemoryDependencyCycles * 100 / TotalCycles; + + OS << "\n\nCycles with backend pressure increase [ " + << format("%.2f", floor((PressurePerCycle * 100) + 0.5) / 100) << "% ]"; + + OS << "\nThroughput Bottlenecks: " + << "\n Resource Pressure [ " + << format("%.2f", floor((ResourcePressurePerCycle * 100) + 0.5) / 100) + << "% ]"; + + if (BPI.PressureIncreaseCycles) { + ArrayRef<unsigned> Distribution = Tracker.getResourcePressureDistribution(); + const MCSchedModel &SM = STI.getSchedModel(); + for (unsigned I = 0, E = Distribution.size(); I < E; ++I) { + unsigned ResourceCycles = Distribution[I]; + if (ResourceCycles) { + double Frequency = (double)ResourceCycles * 100 / TotalCycles; + const MCProcResourceDesc &PRDesc = *SM.getProcResource(I); + OS << "\n - " << PRDesc.Name << " [ " + << format("%.2f", floor((Frequency * 100) + 0.5) / 100) << "% ]"; + } + } + } + + OS << "\n Data Dependencies: [ " + << format("%.2f", floor((DDPerCycle * 100) + 0.5) / 100) << "% ]"; + OS << "\n - Register Dependencies [ " + << format("%.2f", floor((RegDepPressurePerCycle * 100) + 0.5) / 100) + << "% ]"; + OS << "\n - Memory Dependencies [ " + << format("%.2f", floor((MemDepPressurePerCycle * 100) + 0.5) / 100) + << "% ]\n"; +} + +void BottleneckAnalysis::printView(raw_ostream &OS) const { + std::string Buffer; + raw_string_ostream TempStream(Buffer); + printBottleneckHints(TempStream); + TempStream.flush(); + OS << Buffer; + printCriticalSequence(OS); +} + +} // namespace mca. +} // namespace llvm diff --git a/contrib/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h b/contrib/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h new file mode 100644 index 000000000000..7564b1a48206 --- /dev/null +++ b/contrib/llvm/tools/llvm-mca/Views/BottleneckAnalysis.h @@ -0,0 +1,341 @@ +//===--------------------- BottleneckAnalysis.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 implements the bottleneck analysis view. +/// +/// This view internally observes backend pressure increase events in order to +/// identify problematic data dependencies and processor resource interferences. +/// +/// Example of bottleneck analysis report for a dot-product on X86 btver2: +/// +/// Cycles with backend pressure increase [ 40.76% ] +/// Throughput Bottlenecks: +/// Resource Pressure [ 39.34% ] +/// - JFPA [ 39.34% ] +/// - JFPU0 [ 39.34% ] +/// Data Dependencies: [ 1.42% ] +/// - Register Dependencies [ 1.42% ] +/// - Memory Dependencies [ 0.00% ] +/// +/// According to the example, backend pressure increased during the 40.76% of +/// the simulated cycles. In particular, the major cause of backend pressure +/// increases was the contention on floating point adder JFPA accessible from +/// pipeline resource JFPU0. +/// +/// At the end of each cycle, if pressure on the simulated out-of-order buffers +/// has increased, a backend pressure event is reported. +/// 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. +/// +/// Below is the critical sequence computed for the dot-product example on +/// btver2: +/// +/// Instruction Dependency Information +/// +----< 2. vhaddps %xmm3, %xmm3, %xmm4 +/// | +/// | < loop carried > +/// | +/// | 0. vmulps %xmm0, %xmm0, %xmm2 +/// +----> 1. vhaddps %xmm2, %xmm2, %xmm3 ## RESOURCE interference: JFPA [ probability: 73% ] +/// +----> 2. vhaddps %xmm3, %xmm3, %xmm4 ## REGISTER dependency: %xmm3 +/// | +/// | < loop carried > +/// | +/// +----> 1. vhaddps %xmm2, %xmm2, %xmm3 ## RESOURCE interference: JFPA [ probability: 73% ] +/// +/// +/// The algorithm that computes the critical sequence is very similar to a +/// critical path analysis. +/// +/// A dependency graph is used internally to track dependencies between nodes. +/// Nodes of the graph represent instructions from the input assembly sequence, +/// 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. +/// +/// 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 +/// set of nodes with no predecessors) to a leaf node is reported as critical +/// sequence. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_MCA_BOTTLENECK_ANALYSIS_H +#define LLVM_TOOLS_LLVM_MCA_BOTTLENECK_ANALYSIS_H + +#include "Views/View.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCSchedule.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace mca { + +class PressureTracker { + const MCSchedModel &SM; + + // Resource pressure distribution. There is an element for every processor + // resource declared by the scheduling model. Quantities are number of cycles. + SmallVector<unsigned, 4> ResourcePressureDistribution; + + // Each processor resource is associated with a so-called processor resource + // mask. This vector allows to correlate processor resource IDs with processor + // resource masks. There is exactly one element per each processor resource + // declared by the scheduling model. + SmallVector<uint64_t, 4> ProcResID2Mask; + + // Maps processor resource state indices (returned by calls to + // `getResourceStateIndex(Mask)` to processor resource identifiers. + SmallVector<unsigned, 4> ResIdx2ProcResID; + + // Maps Processor Resource identifiers to ResourceUsers indices. + SmallVector<unsigned, 4> ProcResID2ResourceUsersIndex; + + // Identifies the last user of a processor resource unit. + // This vector is updated on every instruction issued event. + // There is one entry for every processor resource unit declared by the + // processor model. An all_ones value is treated like an invalid instruction + // identifier. + using User = std::pair<unsigned, unsigned>; + SmallVector<User, 4> ResourceUsers; + + struct InstructionPressureInfo { + unsigned RegisterPressureCycles; + unsigned MemoryPressureCycles; + unsigned ResourcePressureCycles; + }; + DenseMap<unsigned, InstructionPressureInfo> IPI; + + void updateResourcePressureDistribution(uint64_t CumulativeMask); + + User getResourceUser(unsigned ProcResID, unsigned UnitID) const { + unsigned Index = ProcResID2ResourceUsersIndex[ProcResID]; + return ResourceUsers[Index + UnitID]; + } + +public: + PressureTracker(const MCSchedModel &Model); + + ArrayRef<unsigned> getResourcePressureDistribution() const { + return ResourcePressureDistribution; + } + + void getResourceUsers(uint64_t ResourceMask, + SmallVectorImpl<User> &Users) const; + + unsigned getRegisterPressureCycles(unsigned IID) const { + assert(IPI.find(IID) != IPI.end() && "Instruction is not tracked!"); + const InstructionPressureInfo &Info = IPI.find(IID)->second; + return Info.RegisterPressureCycles; + } + + unsigned getMemoryPressureCycles(unsigned IID) const { + assert(IPI.find(IID) != IPI.end() && "Instruction is not tracked!"); + const InstructionPressureInfo &Info = IPI.find(IID)->second; + return Info.MemoryPressureCycles; + } + + unsigned getResourcePressureCycles(unsigned IID) const { + assert(IPI.find(IID) != IPI.end() && "Instruction is not tracked!"); + const InstructionPressureInfo &Info = IPI.find(IID)->second; + return Info.ResourcePressureCycles; + } + + const char *resolveResourceName(uint64_t ResourceMask) const { + unsigned Index = getResourceStateIndex(ResourceMask); + unsigned ProcResID = ResIdx2ProcResID[Index]; + const MCProcResourceDesc &PRDesc = *SM.getProcResource(ProcResID); + return PRDesc.Name; + } + + void onInstructionDispatched(unsigned IID); + void onInstructionExecuted(unsigned IID); + + void handlePressureEvent(const HWPressureEvent &Event); + void handleInstructionIssuedEvent(const HWInstructionIssuedEvent &Event); +}; + +// A dependency edge. +struct DependencyEdge { + enum DependencyType { DT_INVALID, DT_REGISTER, DT_MEMORY, DT_RESOURCE }; + + // Dependency edge descriptor. + // + // It specifies the dependency type, as well as the edge cost in cycles. + struct Dependency { + DependencyType Type; + uint64_t ResourceOrRegID; + uint64_t Cost; + }; + Dependency Dep; + + unsigned FromIID; + unsigned ToIID; + + // Used by the bottleneck analysis to compute the interference + // probability for processor resources. + unsigned Frequency; +}; + +// A dependency graph used by the bottleneck analysis to describe data +// dependencies and processor resource interferences between instructions. +// +// There is a node (an instance of struct DGNode) for every instruction in the +// input assembly sequence. Edges of the graph represent dependencies between +// instructions. +// +// Each edge of the graph is associated with a cost value which is used +// internally to rank dependency based on their impact on the runtime +// performance (see field DependencyEdge::Dependency::Cost). In general, the +// higher the cost of an edge, the higher the impact on performance. +// +// The cost of a dependency is a function of both the latency and the number of +// cycles where the dependency has been seen as critical (i.e. contributing to +// back-pressure increases). +// +// 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. +class DependencyGraph { + struct DGNode { + unsigned NumPredecessors; + unsigned NumVisitedPredecessors; + uint64_t Cost; + unsigned Depth; + + DependencyEdge CriticalPredecessor; + SmallVector<DependencyEdge, 8> OutgoingEdges; + }; + SmallVector<DGNode, 16> Nodes; + + DependencyGraph(const DependencyGraph &) = delete; + DependencyGraph &operator=(const DependencyGraph &) = delete; + + void addDependency(unsigned From, unsigned To, + DependencyEdge::Dependency &&DE); + + void initializeRootSet(SmallVectorImpl<unsigned> &RootSet) const; + void propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet); + +#ifndef NDEBUG + void dumpDependencyEdge(raw_ostream &OS, const DependencyEdge &DE, + MCInstPrinter &MCIP) const; +#endif + +public: + DependencyGraph(unsigned Size) : Nodes(Size) {} + + void addRegisterDep(unsigned From, unsigned To, unsigned RegID, + unsigned Cost) { + addDependency(From, To, {DependencyEdge::DT_REGISTER, RegID, Cost}); + } + + void addMemoryDep(unsigned From, unsigned To, unsigned Cost) { + addDependency(From, To, {DependencyEdge::DT_MEMORY, /* unused */ 0, Cost}); + } + + void addResourceDep(unsigned From, unsigned To, uint64_t Mask, + unsigned Cost) { + addDependency(From, To, {DependencyEdge::DT_RESOURCE, Mask, Cost}); + } + + // Called by the bottleneck analysis at the end of simulation to propagate + // costs through the edges of the graph, and compute a critical path. + void finalizeGraph() { + SmallVector<unsigned, 16> RootSet; + initializeRootSet(RootSet); + propagateThroughEdges(RootSet); + } + + // Returns a sequence of edges representing the critical sequence based on the + // simulated run. It assumes that the graph has already been finalized (i.e. + // method `finalizeGraph()` has already been called on this graph). + void getCriticalSequence(SmallVectorImpl<const DependencyEdge *> &Seq) const; + +#ifndef NDEBUG + void dump(raw_ostream &OS, MCInstPrinter &MCIP) const; +#endif +}; + +/// A view that collects and prints a few performance numbers. +class BottleneckAnalysis : public View { + const MCSubtargetInfo &STI; + MCInstPrinter &MCIP; + PressureTracker Tracker; + DependencyGraph DG; + + ArrayRef<MCInst> Source; + unsigned Iterations; + unsigned TotalCycles; + + bool PressureIncreasedBecauseOfResources; + bool PressureIncreasedBecauseOfRegisterDependencies; + bool PressureIncreasedBecauseOfMemoryDependencies; + // True if throughput was affected by dispatch stalls. + bool SeenStallCycles; + + struct BackPressureInfo { + // Cycles where backpressure increased. + unsigned PressureIncreaseCycles; + // Cycles where backpressure increased because of pipeline pressure. + unsigned ResourcePressureCycles; + // Cycles where backpressure increased because of data dependencies. + unsigned DataDependencyCycles; + // Cycles where backpressure increased because of register dependencies. + unsigned RegisterDependencyCycles; + // Cycles where backpressure increased because of memory dependencies. + unsigned MemoryDependencyCycles; + }; + BackPressureInfo BPI; + + // Used to populate the dependency graph DG. + void addRegisterDep(unsigned From, unsigned To, unsigned RegID, unsigned Cy); + void addMemoryDep(unsigned From, unsigned To, unsigned Cy); + void addResourceDep(unsigned From, unsigned To, uint64_t Mask, unsigned Cy); + + // Prints a bottleneck message to OS. + void printBottleneckHints(raw_ostream &OS) const; + void printCriticalSequence(raw_ostream &OS) const; + +public: + BottleneckAnalysis(const MCSubtargetInfo &STI, MCInstPrinter &MCIP, + ArrayRef<MCInst> Sequence, unsigned Iterations); + + void onCycleEnd() override; + void onEvent(const HWStallEvent &Event) override { SeenStallCycles = true; } + void onEvent(const HWPressureEvent &Event) override; + void onEvent(const HWInstructionEvent &Event) override; + + void printView(raw_ostream &OS) const override; + +#ifndef NDEBUG + void dump(raw_ostream &OS, MCInstPrinter &MCIP) const { DG.dump(OS, MCIP); } +#endif +}; + +} // namespace mca +} // namespace llvm + +#endif diff --git a/contrib/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp b/contrib/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp index 2562c82407bf..557b8ba17b17 100644 --- a/contrib/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp +++ b/contrib/llvm/tools/llvm-mca/Views/DispatchStatistics.cpp @@ -1,10 +1,9 @@ //===--------------------- DispatchStatistics.cpp ---------------------*- C++ //-*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/DispatchStatistics.h b/contrib/llvm/tools/llvm-mca/Views/DispatchStatistics.h index 6679c81efe95..07c0f5a4c68f 100644 --- a/contrib/llvm/tools/llvm-mca/Views/DispatchStatistics.h +++ b/contrib/llvm/tools/llvm-mca/Views/DispatchStatistics.h @@ -1,9 +1,8 @@ //===--------------------- DispatchStatistics.h -----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp b/contrib/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp index 5016afb49e44..1fbffa3e5b69 100644 --- a/contrib/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp +++ b/contrib/llvm/tools/llvm-mca/Views/InstructionInfoView.cpp @@ -1,9 +1,8 @@ //===--------------------- InstructionInfoView.cpp --------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -44,6 +43,9 @@ void InstructionInfoView::printView(raw_ostream &OS) const { const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID); unsigned NumMicroOpcodes = SCDesc.NumMicroOps; unsigned Latency = MCSchedModel::computeInstrLatency(STI, SCDesc); + // Add extra latency due to delays in the forwarding data paths. + Latency += MCSchedModel::getForwardingDelayCycles( + STI.getReadAdvanceEntries(SCDesc)); Optional<double> RThroughput = MCSchedModel::getReciprocalThroughput(STI, SCDesc); diff --git a/contrib/llvm/tools/llvm-mca/Views/InstructionInfoView.h b/contrib/llvm/tools/llvm-mca/Views/InstructionInfoView.h index 3ef95d474490..640d87383436 100644 --- a/contrib/llvm/tools/llvm-mca/Views/InstructionInfoView.h +++ b/contrib/llvm/tools/llvm-mca/Views/InstructionInfoView.h @@ -1,9 +1,8 @@ //===--------------------- InstructionInfoView.h ----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp b/contrib/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp index 06202bc41421..58736ee0d18c 100644 --- a/contrib/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp +++ b/contrib/llvm/tools/llvm-mca/Views/RegisterFileStatistics.cpp @@ -1,9 +1,8 @@ //===--------------------- RegisterFileStatistics.cpp -----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h b/contrib/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h index a2c52a668dae..a2273dd48b22 100644 --- a/contrib/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h +++ b/contrib/llvm/tools/llvm-mca/Views/RegisterFileStatistics.h @@ -1,9 +1,8 @@ //===--------------------- RegisterFileStatistics.h -------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp b/contrib/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp index 6df61840437d..38a2478cf4fe 100644 --- a/contrib/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp +++ b/contrib/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp @@ -1,9 +1,8 @@ //===--------------------- ResourcePressureView.cpp -------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/ResourcePressureView.h b/contrib/llvm/tools/llvm-mca/Views/ResourcePressureView.h index 572ce6fe6b70..0fa0b9a36aa3 100644 --- a/contrib/llvm/tools/llvm-mca/Views/ResourcePressureView.h +++ b/contrib/llvm/tools/llvm-mca/Views/ResourcePressureView.h @@ -1,9 +1,8 @@ //===--------------------- ResourcePressureView.h ---------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp b/contrib/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp index 54eb28f1add9..cb4fbae78039 100644 --- a/contrib/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp +++ b/contrib/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp @@ -1,9 +1,8 @@ //===--------------------- RetireControlUnitStatistics.cpp ------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h b/contrib/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h index 02aa13bc444a..1a4d3dec5c56 100644 --- a/contrib/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h +++ b/contrib/llvm/tools/llvm-mca/Views/RetireControlUnitStatistics.h @@ -1,9 +1,8 @@ //===--------------------- RetireControlUnitStatistics.h --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/SchedulerStatistics.cpp b/contrib/llvm/tools/llvm-mca/Views/SchedulerStatistics.cpp index 670f90127f18..bd0ba350ab68 100644 --- a/contrib/llvm/tools/llvm-mca/Views/SchedulerStatistics.cpp +++ b/contrib/llvm/tools/llvm-mca/Views/SchedulerStatistics.cpp @@ -1,9 +1,8 @@ //===--------------------- SchedulerStatistics.cpp --------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -23,7 +22,6 @@ SchedulerStatistics::SchedulerStatistics(const llvm::MCSubtargetInfo &STI) : SM(STI.getSchedModel()), LQResourceID(0), SQResourceID(0), NumIssued(0), NumCycles(0), MostRecentLoadDispatched(~0U), MostRecentStoreDispatched(~0U), - IssuedPerCycle(STI.getSchedModel().NumProcResourceKinds, 0), Usage(STI.getSchedModel().NumProcResourceKinds, {0, 0, 0}) { if (SM.hasExtraProcessorInfo()) { const MCExtraProcessorInfo &EPI = SM.getExtraProcessorInfo(); @@ -44,9 +42,10 @@ SchedulerStatistics::SchedulerStatistics(const llvm::MCSubtargetInfo &STI) // In future we should add a new "memory queue" event type, so that we stop // making assumptions on how LSUnit internally works (See PR39828). void SchedulerStatistics::onEvent(const HWInstructionEvent &Event) { - if (Event.Type == HWInstructionEvent::Issued) - ++NumIssued; - else if (Event.Type == HWInstructionEvent::Dispatched) { + if (Event.Type == HWInstructionEvent::Issued) { + const Instruction &Inst = *Event.IR.getInstruction(); + NumIssued += Inst.getDesc().NumMicroOps; + } else if (Event.Type == HWInstructionEvent::Dispatched) { const Instruction &Inst = *Event.IR.getInstruction(); const unsigned Index = Event.IR.getSourceIndex(); if (LQResourceID && Inst.getDesc().MayLoad && @@ -96,29 +95,25 @@ void SchedulerStatistics::updateHistograms() { BU.MaxUsedSlots = std::max(BU.MaxUsedSlots, BU.SlotsInUse); } - IssuedPerCycle[NumIssued]++; + IssueWidthPerCycle[NumIssued]++; NumIssued = 0; } void SchedulerStatistics::printSchedulerStats(raw_ostream &OS) const { OS << "\n\nSchedulers - " - << "number of cycles where we saw N instructions issued:\n"; + << "number of cycles where we saw N micro opcodes issued:\n"; OS << "[# issued], [# cycles]\n"; - const auto It = - std::max_element(IssuedPerCycle.begin(), IssuedPerCycle.end()); - unsigned Index = std::distance(IssuedPerCycle.begin(), It); - bool HasColors = OS.has_colors(); - for (unsigned I = 0, E = IssuedPerCycle.size(); I < E; ++I) { - unsigned IPC = IssuedPerCycle[I]; - if (!IPC) - continue; - - if (I == Index && HasColors) + const auto It = + std::max_element(IssueWidthPerCycle.begin(), IssueWidthPerCycle.end()); + for (const std::pair<unsigned, unsigned> &Entry : IssueWidthPerCycle) { + unsigned NumIssued = Entry.first; + if (NumIssued == It->first && HasColors) OS.changeColor(raw_ostream::SAVEDCOLOR, true, false); - OS << " " << I << ", " << IPC << " (" + unsigned IPC = Entry.second; + OS << " " << NumIssued << ", " << IPC << " (" << format("%.1f", ((double)IPC / NumCycles) * 100) << "%)\n"; if (HasColors) OS.resetColor(); diff --git a/contrib/llvm/tools/llvm-mca/Views/SchedulerStatistics.h b/contrib/llvm/tools/llvm-mca/Views/SchedulerStatistics.h index d99a395a726d..32711b4483b4 100644 --- a/contrib/llvm/tools/llvm-mca/Views/SchedulerStatistics.h +++ b/contrib/llvm/tools/llvm-mca/Views/SchedulerStatistics.h @@ -1,9 +1,8 @@ //===--------------------- SchedulerStatistics.h ----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -63,7 +62,9 @@ class SchedulerStatistics final : public View { uint64_t CumulativeNumUsedSlots; }; - std::vector<unsigned> IssuedPerCycle; + using Histogram = std::map<unsigned, unsigned>; + Histogram IssueWidthPerCycle; + std::vector<BufferUsage> Usage; void updateHistograms(); diff --git a/contrib/llvm/tools/llvm-mca/Views/SummaryView.cpp b/contrib/llvm/tools/llvm-mca/Views/SummaryView.cpp index d8ac709e784d..ef5550048f4c 100644 --- a/contrib/llvm/tools/llvm-mca/Views/SummaryView.cpp +++ b/contrib/llvm/tools/llvm-mca/Views/SummaryView.cpp @@ -1,9 +1,8 @@ //===--------------------- SummaryView.cpp -------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -25,11 +24,17 @@ namespace mca { SummaryView::SummaryView(const MCSchedModel &Model, ArrayRef<MCInst> S, unsigned Width) - : SM(Model), Source(S), DispatchWidth(Width), LastInstructionIdx(0), + : SM(Model), Source(S), DispatchWidth(Width?Width: Model.IssueWidth), + LastInstructionIdx(0), TotalCycles(0), NumMicroOps(0), ProcResourceUsage(Model.getNumProcResourceKinds(), 0), - ProcResourceMasks(Model.getNumProcResourceKinds()) { + ProcResourceMasks(Model.getNumProcResourceKinds()), + ResIdx2ProcResID(Model.getNumProcResourceKinds(), 0) { computeProcResourceMasks(SM, ProcResourceMasks); + for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) { + unsigned Index = getResourceStateIndex(ProcResourceMasks[I]); + ResIdx2ProcResID[Index] = I; + } } void SummaryView::onEvent(const HWInstructionEvent &Event) { @@ -51,11 +56,8 @@ void SummaryView::onEvent(const HWInstructionEvent &Event) { NumMicroOps += Desc.NumMicroOps; for (const std::pair<uint64_t, const ResourceUsage> &RU : Desc.Resources) { if (RU.second.size()) { - const auto It = find(ProcResourceMasks, RU.first); - assert(It != ProcResourceMasks.end() && - "Invalid processor resource mask!"); - ProcResourceUsage[std::distance(ProcResourceMasks.begin(), It)] += - RU.second.size(); + unsigned ProcResID = ResIdx2ProcResID[getResourceStateIndex(RU.first)]; + ProcResourceUsage[ProcResID] += RU.second.size(); } } } @@ -87,5 +89,6 @@ void SummaryView::printView(raw_ostream &OS) const { TempStream.flush(); OS << Buffer; } + } // namespace mca. } // namespace llvm diff --git a/contrib/llvm/tools/llvm-mca/Views/SummaryView.h b/contrib/llvm/tools/llvm-mca/Views/SummaryView.h index f59fd4233fbe..9be31b7d51bd 100644 --- a/contrib/llvm/tools/llvm-mca/Views/SummaryView.h +++ b/contrib/llvm/tools/llvm-mca/Views/SummaryView.h @@ -1,9 +1,8 @@ //===--------------------- SummaryView.h ---------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -46,6 +45,7 @@ class SummaryView : public View { unsigned TotalCycles; // The total number of micro opcodes contributed by a block of instructions. unsigned NumMicroOps; + // For each processor resource, this vector stores the cumulative number of // resource cycles consumed by the analyzed code block. llvm::SmallVector<unsigned, 8> ProcResourceUsage; @@ -56,6 +56,9 @@ class SummaryView : public View { // declared by the scheduling model. llvm::SmallVector<uint64_t, 8> ProcResourceMasks; + // Used to map resource indices to actual processor resource IDs. + llvm::SmallVector<unsigned, 8> ResIdx2ProcResID; + // Compute the reciprocal throughput for the analyzed code block. // The reciprocal block throughput is computed as the MAX between: // - NumMicroOps / DispatchWidth @@ -68,9 +71,9 @@ public: void onCycleEnd() override { ++TotalCycles; } void onEvent(const HWInstructionEvent &Event) override; - void printView(llvm::raw_ostream &OS) const override; }; + } // namespace mca } // namespace llvm diff --git a/contrib/llvm/tools/llvm-mca/Views/TimelineView.cpp b/contrib/llvm/tools/llvm-mca/Views/TimelineView.cpp index 7d55bbc99c73..fe3f16ba344c 100644 --- a/contrib/llvm/tools/llvm-mca/Views/TimelineView.cpp +++ b/contrib/llvm/tools/llvm-mca/Views/TimelineView.cpp @@ -1,9 +1,8 @@ //===--------------------- TimelineView.cpp ---------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// \brief diff --git a/contrib/llvm/tools/llvm-mca/Views/TimelineView.h b/contrib/llvm/tools/llvm-mca/Views/TimelineView.h index ee981800161c..b63b234293cd 100644 --- a/contrib/llvm/tools/llvm-mca/Views/TimelineView.h +++ b/contrib/llvm/tools/llvm-mca/Views/TimelineView.h @@ -1,9 +1,8 @@ //===--------------------- TimelineView.h -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// \brief diff --git a/contrib/llvm/tools/llvm-mca/Views/View.cpp b/contrib/llvm/tools/llvm-mca/Views/View.cpp index 6cfb9dd9f394..8e5c34d2d5c2 100644 --- a/contrib/llvm/tools/llvm-mca/Views/View.cpp +++ b/contrib/llvm/tools/llvm-mca/Views/View.cpp @@ -1,9 +1,8 @@ //===----------------------- View.cpp ---------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/Views/View.h b/contrib/llvm/tools/llvm-mca/Views/View.h index 4b82b0da0d27..3b52511b4d29 100644 --- a/contrib/llvm/tools/llvm-mca/Views/View.h +++ b/contrib/llvm/tools/llvm-mca/Views/View.h @@ -1,9 +1,8 @@ //===----------------------- View.h -----------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/contrib/llvm/tools/llvm-mca/llvm-mca.cpp b/contrib/llvm/tools/llvm-mca/llvm-mca.cpp index 68d63db599d7..b3590b5910ec 100644 --- a/contrib/llvm/tools/llvm-mca/llvm-mca.cpp +++ b/contrib/llvm/tools/llvm-mca/llvm-mca.cpp @@ -1,9 +1,8 @@ //===-- llvm-mca.cpp - Machine Code Analyzer -------------------*- C++ -* -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -24,6 +23,7 @@ #include "CodeRegion.h" #include "CodeRegionGenerator.h" #include "PipelinePrinter.h" +#include "Views/BottleneckAnalysis.h" #include "Views/DispatchStatistics.h" #include "Views/InstructionInfoView.h" #include "Views/RegisterFileStatistics.h" @@ -68,8 +68,9 @@ static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename")); static cl::opt<std::string> - ArchName("march", cl::desc("Target architecture. " - "See -version for available targets"), + ArchName("march", + cl::desc("Target architecture. " + "See -version for available targets"), cl::cat(ToolOptions)); static cl::opt<std::string> @@ -101,6 +102,17 @@ static cl::opt<unsigned> "be used for register mappings"), cl::cat(ToolOptions), cl::init(0)); +static cl::opt<unsigned> + MicroOpQueue("micro-op-queue-size", cl::Hidden, + cl::desc("Number of entries in the micro-op queue"), + cl::cat(ToolOptions), cl::init(0)); + +static cl::opt<unsigned> + DecoderThroughput("decoder-throughput", cl::Hidden, + cl::desc("Maximum throughput from the decoders " + "(instructions per cycle)"), + cl::cat(ToolOptions), cl::init(0)); + static cl::opt<bool> PrintRegisterFileStats("register-file-stats", cl::desc("Print register file statistics"), @@ -176,6 +188,11 @@ static cl::opt<bool> cl::desc("Print all views including hardware statistics"), cl::cat(ViewOptions), cl::init(false)); +static cl::opt<bool> EnableBottleneckAnalysis( + "bottleneck-analysis", + cl::desc("Enable bottleneck analysis (disabled by default)"), + cl::cat(ViewOptions), cl::init(false)); + namespace { const Target *getTarget(const char *ProgName) { @@ -220,6 +237,7 @@ static void processViewOptions() { if (EnableAllViews.getNumOccurrences()) { processOptionImpl(PrintSummaryView, EnableAllViews); + processOptionImpl(EnableBottleneckAnalysis, EnableAllViews); processOptionImpl(PrintResourcePressureView, EnableAllViews); processOptionImpl(PrintTimelineView, EnableAllViews); processOptionImpl(PrintInstructionInfoView, EnableAllViews); @@ -348,6 +366,11 @@ int main(int argc, char **argv) { return 1; } const mca::CodeRegions &Regions = *RegionsOrErr; + + // Early exit if errors were found by the code region parsing logic. + if (!Regions.isValid()) + return 1; + if (Regions.empty()) { WithColor::error() << "no assembly instructions found.\n"; return 1; @@ -377,18 +400,15 @@ int main(int argc, char **argv) { const MCSchedModel &SM = STI->getSchedModel(); - unsigned Width = SM.IssueWidth; - if (DispatchWidth) - Width = DispatchWidth; - // Create an instruction builder. mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get()); // Create a context to control ownership of the pipeline hardware. mca::Context MCA(*MRI, *STI); - mca::PipelineOptions PO(Width, RegisterFileSize, LoadQueueSize, - StoreQueueSize, AssumeNoAlias); + mca::PipelineOptions PO(MicroOpQueue, DecoderThroughput, DispatchWidth, + RegisterFileSize, LoadQueueSize, StoreQueueSize, + AssumeNoAlias, EnableBottleneckAnalysis); // Number each region in the sequence. unsigned RegionIdx = 0; @@ -423,8 +443,8 @@ int main(int argc, char **argv) { WithColor::error() << IE.Message << '\n'; IP->printInst(&IE.Inst, SS, "", *STI); SS.flush(); - WithColor::note() << "instruction: " << InstructionStr - << '\n'; + WithColor::note() + << "instruction: " << InstructionStr << '\n'; })) { // Default case. WithColor::error() << toString(std::move(NewE)); @@ -464,7 +484,13 @@ int main(int argc, char **argv) { mca::PipelinePrinter Printer(*P); if (PrintSummaryView) - Printer.addView(llvm::make_unique<mca::SummaryView>(SM, Insts, Width)); + Printer.addView( + llvm::make_unique<mca::SummaryView>(SM, Insts, DispatchWidth)); + + if (EnableBottleneckAnalysis) { + Printer.addView(llvm::make_unique<mca::BottleneckAnalysis>( + *STI, *IP, Insts, S.getNumIterations())); + } if (PrintInstructionInfoView) Printer.addView( diff --git a/contrib/llvm/tools/llvm-modextract/llvm-modextract.cpp b/contrib/llvm/tools/llvm-modextract/llvm-modextract.cpp index 9fd8340505aa..3adefc5f0d3e 100644 --- a/contrib/llvm/tools/llvm-modextract/llvm-modextract.cpp +++ b/contrib/llvm/tools/llvm-modextract/llvm-modextract.cpp @@ -1,9 +1,8 @@ //===-- llvm-modextract.cpp - LLVM module extractor utility ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-nm/llvm-nm.cpp b/contrib/llvm/tools/llvm-nm/llvm-nm.cpp index 042e284e8369..aa62e6f0209b 100644 --- a/contrib/llvm/tools/llvm-nm/llvm-nm.cpp +++ b/contrib/llvm/tools/llvm-nm/llvm-nm.cpp @@ -1,9 +1,8 @@ //===-- llvm-nm.cpp - Symbol table dumping utility for llvm ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -47,12 +46,15 @@ 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::init(bsd), cl::cat(NMCat)); cl::alias OutputFormat2("f", cl::desc("Alias for --format"), cl::aliasopt(OutputFormat)); @@ -60,50 +62,53 @@ 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::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.")); + "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::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::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::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::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::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::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::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::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); @@ -111,40 +116,52 @@ 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::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::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::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::desc("Demangle C++ symbol names")); -cl::alias DemangleC("C", cl::desc("Alias for --demangle"), cl::aliasopt(Demangle), - 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::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 instead of address")); + 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::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::desc("Exclude aliases from output"), + cl::cat(NMCat)); -cl::opt<bool> ArchiveMap("print-armap", cl::desc("Print the archive map")); +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); @@ -153,38 +170,45 @@ 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::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::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); -// FIXME: This option takes exactly two strings and should be allowed anywhere -// on the command line. Such that "llvm-nm -s __TEXT __text foo.o" would work. -// But that does not as the CommandLine Library does not have a way to make -// this work. For now the "-s __TEXT __text" has to be last on the command -// line. -cl::list<std::string> SegSect("s", cl::Positional, cl::ZeroOrMore, +cl::opt<bool> SpecialSyms("special-syms", + cl::desc("No-op. Used for GNU compatibility only")); + +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")); + "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::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")); + "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")); + "Mach-O only"), + cl::cat(NMCat)); cl::opt<bool> DyldInfoOnly("dyldinfo-only", cl::desc("Show only symbols from the dyldinfo, " - "Mach-O only")); + "Mach-O only"), + cl::cat(NMCat)); cl::opt<bool> NoLLVMBitcode("no-llvm-bc", - cl::desc("Disable LLVM bitcode reader")); + cl::desc("Disable LLVM bitcode reader"), + cl::cat(NMCat)); cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); @@ -263,6 +287,8 @@ struct NMSymbol { uint64_t Size; char TypeChar; StringRef Name; + StringRef SectionName; + StringRef TypeName; BasicSymbolRef Sym; // The Sym field above points to the native symbol in the object file, // for Mach-O when we are creating symbols from the dyld info the above @@ -316,8 +342,7 @@ static char isSymbolList64Bit(SymbolicFile &Obj) { } static StringRef CurrentFilename; -typedef std::vector<NMSymbol> SymbolListT; -static SymbolListT SymbolList; +static std::vector<NMSymbol> SymbolList; static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I); @@ -326,9 +351,10 @@ static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I); // the darwin format it produces the same output as darwin's nm(1) -m output // and when printing Mach-O symbols in hex it produces the same output as // darwin's nm(1) -x format. -static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I, +static void darwinPrintSymbol(SymbolicFile &Obj, const NMSymbol &S, char *SymbolAddrStr, const char *printBlanks, - const char *printDashes, const char *printFormat) { + const char *printDashes, + const char *printFormat) { MachO::mach_header H; MachO::mach_header_64 H_64; uint32_t Filetype = MachO::MH_OBJECT; @@ -340,7 +366,7 @@ static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I, uint64_t NValue = 0; MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj); if (Obj.isIR()) { - uint32_t SymFlags = I->Sym.getFlags(); + uint32_t SymFlags = S.Sym.getFlags(); if (SymFlags & SymbolRef::SF_Global) NType |= MachO::N_EXT; if (SymFlags & SymbolRef::SF_Hidden) @@ -362,7 +388,7 @@ static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I, if (SymFlags & SymbolRef::SF_Weak) NDesc |= MachO::N_WEAK_DEF; } else { - DataRefImpl SymDRI = I->Sym.getRawDataRefImpl(); + DataRefImpl SymDRI = S.Sym.getRawDataRefImpl(); if (MachO->is64Bit()) { H_64 = MachO->MachOObjectFile::getHeader64(); Filetype = H_64.filetype; @@ -375,11 +401,11 @@ static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I, NStrx = STE_64.n_strx; NValue = STE_64.n_value; } else { - NType = I->NType; - NSect = I->NSect; - NDesc = I->NDesc; + NType = S.NType; + NSect = S.NSect; + NDesc = S.NDesc; NStrx = 0; - NValue = I->Address; + NValue = S.Address; } } else { H = MachO->MachOObjectFile::getHeader(); @@ -393,42 +419,31 @@ static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I, NStrx = STE.n_strx; NValue = STE.n_value; } else { - NType = I->NType; - NSect = I->NSect; - NDesc = I->NDesc; + NType = S.NType; + NSect = S.NSect; + NDesc = S.NDesc; NStrx = 0; - NValue = I->Address; + NValue = S.Address; } } } // If we are printing Mach-O symbols in hex do that and return. if (FormatMachOasHex) { - char Str[18] = ""; - format(printFormat, NValue).print(Str, sizeof(Str)); - outs() << Str << ' '; - format("%02x", NType).print(Str, sizeof(Str)); - outs() << Str << ' '; - format("%02x", NSect).print(Str, sizeof(Str)); - outs() << Str << ' '; - format("%04x", NDesc).print(Str, sizeof(Str)); - outs() << Str << ' '; - format("%08x", NStrx).print(Str, sizeof(Str)); - outs() << Str << ' '; - outs() << I->Name; + outs() << format(printFormat, NValue) << ' ' + << format("%02x %02x %04x %08x", NType, NSect, NDesc, NStrx) << ' ' + << S.Name; if ((NType & MachO::N_TYPE) == MachO::N_INDR) { outs() << " (indirect for "; - format(printFormat, NValue).print(Str, sizeof(Str)); - outs() << Str << ' '; + outs() << format(printFormat, NValue) << ' '; StringRef IndirectName; - if (I->Sym.getRawDataRefImpl().p) { - if (MachO->getIndirectName(I->Sym.getRawDataRefImpl(), IndirectName)) + if (S.Sym.getRawDataRefImpl().p) { + if (MachO->getIndirectName(S.Sym.getRawDataRefImpl(), IndirectName)) outs() << "?)"; else outs() << IndirectName << ")"; - } - else - outs() << I->IndirectName << ")"; + } else + outs() << S.IndirectName << ")"; } outs() << "\n"; return; @@ -487,9 +502,9 @@ static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I, break; } section_iterator Sec = SectionRef(); - if (I->Sym.getRawDataRefImpl().p) { + if (S.Sym.getRawDataRefImpl().p) { Expected<section_iterator> SecOrErr = - MachO->getSymbolSection(I->Sym.getRawDataRefImpl()); + MachO->getSymbolSection(S.Sym.getRawDataRefImpl()); if (!SecOrErr) { consumeError(SecOrErr.takeError()); outs() << "(?,?) "; @@ -501,11 +516,12 @@ static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I, break; } } else { - Sec = I->Section; + Sec = S.Section; } DataRefImpl Ref = Sec->getRawDataRefImpl(); StringRef SectionName; - MachO->getSectionName(Ref, SectionName); + if (Expected<StringRef> NameOrErr = MachO->getSectionName(Ref)) + SectionName = *NameOrErr; StringRef SegmentName = MachO->getSectionFinalSegmentName(Ref); outs() << "(" << SegmentName << "," << SectionName << ") "; break; @@ -541,39 +557,36 @@ static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I, outs() << "non-external "; } - if (Filetype == MachO::MH_OBJECT && - (NDesc & MachO::N_NO_DEAD_STRIP) == MachO::N_NO_DEAD_STRIP) - outs() << "[no dead strip] "; - - if (Filetype == MachO::MH_OBJECT && - ((NType & MachO::N_TYPE) != MachO::N_UNDF) && - (NDesc & MachO::N_SYMBOL_RESOLVER) == MachO::N_SYMBOL_RESOLVER) - outs() << "[symbol resolver] "; - - if (Filetype == MachO::MH_OBJECT && - ((NType & MachO::N_TYPE) != MachO::N_UNDF) && - (NDesc & MachO::N_ALT_ENTRY) == MachO::N_ALT_ENTRY) - outs() << "[alt entry] "; + if (Filetype == MachO::MH_OBJECT) { + if (NDesc & MachO::N_NO_DEAD_STRIP) + outs() << "[no dead strip] "; + if ((NType & MachO::N_TYPE) != MachO::N_UNDF && + NDesc & MachO::N_SYMBOL_RESOLVER) + outs() << "[symbol resolver] "; + if ((NType & MachO::N_TYPE) != MachO::N_UNDF && NDesc & MachO::N_ALT_ENTRY) + outs() << "[alt entry] "; + if ((NType & MachO::N_TYPE) != MachO::N_UNDF && NDesc & MachO::N_COLD_FUNC) + outs() << "[cold func] "; + } if ((NDesc & MachO::N_ARM_THUMB_DEF) == MachO::N_ARM_THUMB_DEF) outs() << "[Thumb] "; if ((NType & MachO::N_TYPE) == MachO::N_INDR) { - outs() << I->Name << " (for "; + outs() << S.Name << " (for "; StringRef IndirectName; if (MachO) { - if (I->Sym.getRawDataRefImpl().p) { - if (MachO->getIndirectName(I->Sym.getRawDataRefImpl(), IndirectName)) + if (S.Sym.getRawDataRefImpl().p) { + if (MachO->getIndirectName(S.Sym.getRawDataRefImpl(), IndirectName)) outs() << "?)"; else outs() << IndirectName << ")"; - } - else - outs() << I->IndirectName << ")"; + } else + outs() << S.IndirectName << ")"; } else outs() << "?)"; } else - outs() << I->Name; + outs() << S.Name; if ((Flags & MachO::MH_TWOLEVEL) == MachO::MH_TWOLEVEL && (((NType & MachO::N_TYPE) == MachO::N_UNDF && NValue == 0) || @@ -635,25 +648,24 @@ static const struct DarwinStabName DarwinStabNames[] = { {MachO::N_ECOMM, "ECOMM"}, {MachO::N_ECOML, "ECOML"}, {MachO::N_LENG, "LENG"}, - {0, nullptr}}; +}; static const char *getDarwinStabString(uint8_t NType) { - for (unsigned i = 0; DarwinStabNames[i].Name; i++) { - if (DarwinStabNames[i].NType == NType) - return DarwinStabNames[i].Name; - } + for (auto I : makeArrayRef(DarwinStabNames)) + if (I.NType == NType) + return I.Name; return nullptr; } // darwinPrintStab() prints the n_sect, n_desc along with a symbolic name of // a stab n_type value in a Mach-O file. -static void darwinPrintStab(MachOObjectFile *MachO, SymbolListT::iterator I) { +static void darwinPrintStab(MachOObjectFile *MachO, const NMSymbol &S) { MachO::nlist_64 STE_64; MachO::nlist STE; uint8_t NType; uint8_t NSect; uint16_t NDesc; - DataRefImpl SymDRI = I->Sym.getRawDataRefImpl(); + DataRefImpl SymDRI = S.Sym.getRawDataRefImpl(); if (MachO->is64Bit()) { STE_64 = MachO->getSymbol64TableEntry(SymDRI); NType = STE_64.n_type; @@ -666,16 +678,11 @@ static void darwinPrintStab(MachOObjectFile *MachO, SymbolListT::iterator I) { NDesc = STE.n_desc; } - char Str[18] = ""; - format("%02x", NSect).print(Str, sizeof(Str)); - outs() << ' ' << Str << ' '; - format("%04x", NDesc).print(Str, sizeof(Str)); - outs() << Str << ' '; + outs() << format(" %02x %04x ", NSect, NDesc); if (const char *stabString = getDarwinStabString(NType)) - format("%5.5s", stabString).print(Str, sizeof(Str)); + outs() << format("%5.5s", stabString); else - format(" %02x", NType).print(Str, sizeof(Str)); - outs() << Str; + outs() << format(" %02x", NType); } static Optional<std::string> demangle(StringRef Name, bool StripUnderscore) { @@ -780,26 +787,24 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, errs() << "no symbols\n"; } - for (SymbolListT::iterator I = SymbolList.begin(), E = SymbolList.end(); - I != E; ++I) { + for (const NMSymbol &S : SymbolList) { uint32_t SymFlags; - std::string Name = I->Name.str(); + std::string Name = S.Name.str(); MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj); if (Demangle) { - if (Optional<std::string> Opt = demangle(I->Name, MachO)) + if (Optional<std::string> Opt = demangle(S.Name, MachO)) Name = *Opt; } - if (I->Sym.getRawDataRefImpl().p) - SymFlags = I->Sym.getFlags(); + if (S.Sym.getRawDataRefImpl().p) + SymFlags = S.Sym.getFlags(); else - SymFlags = I->SymFlags; + SymFlags = S.SymFlags; bool Undefined = SymFlags & SymbolRef::SF_Undefined; bool Global = SymFlags & SymbolRef::SF_Global; bool Weak = SymFlags & SymbolRef::SF_Weak; if ((!Undefined && UndefinedOnly) || (Undefined && DefinedOnly) || - (!Global && ExternalOnly) || (SizeSort && !PrintAddress) || - (Weak && NoWeakSymbols)) + (!Global && ExternalOnly) || (Weak && NoWeakSymbols)) continue; if (PrintFileName) writeFileName(outs()); @@ -810,32 +815,30 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, continue; } - char SymbolAddrStr[18] = ""; - char SymbolSizeStr[18] = ""; + char SymbolAddrStr[23], SymbolSizeStr[23]; // If the format is SysV or the symbol isn't defined, then print spaces. - if (OutputFormat == sysv || !symbolIsDefined(*I)) { + if (OutputFormat == sysv || !symbolIsDefined(S)) { if (OutputFormat == posix) { - format(printFormat, I->Address) - .print(SymbolAddrStr, sizeof(SymbolAddrStr)); - format(printFormat, I->Size) - .print(SymbolSizeStr, sizeof(SymbolSizeStr)); + format(printFormat, S.Address) + .print(SymbolAddrStr, sizeof(SymbolAddrStr)); + format(printFormat, S.Size).print(SymbolSizeStr, sizeof(SymbolSizeStr)); } else { strcpy(SymbolAddrStr, printBlanks); strcpy(SymbolSizeStr, printBlanks); } } - // Otherwise, print the symbol address and size. - if (symbolIsDefined(*I)) { + if (symbolIsDefined(S)) { + // Otherwise, print the symbol address and size. if (Obj.isIR()) strcpy(SymbolAddrStr, printDashes); - else if(MachO && I->TypeChar == 'I') + else if (MachO && S.TypeChar == 'I') strcpy(SymbolAddrStr, printBlanks); else - format(printFormat, I->Address) - .print(SymbolAddrStr, sizeof(SymbolAddrStr)); - format(printFormat, I->Size).print(SymbolSizeStr, sizeof(SymbolSizeStr)); + format(printFormat, S.Address) + .print(SymbolAddrStr, sizeof(SymbolAddrStr)); + format(printFormat, S.Size).print(SymbolSizeStr, sizeof(SymbolSizeStr)); } // If OutputFormat is darwin or we are printing Mach-O symbols in hex and @@ -844,43 +847,36 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, // printing Mach-O symbols in hex and not a Mach-O object fall back to // OutputFormat bsd (see below). if ((OutputFormat == darwin || FormatMachOasHex) && (MachO || Obj.isIR())) { - darwinPrintSymbol(Obj, I, SymbolAddrStr, printBlanks, printDashes, + darwinPrintSymbol(Obj, S, SymbolAddrStr, printBlanks, printDashes, printFormat); } else if (OutputFormat == posix) { - outs() << Name << " " << I->TypeChar << " "; - if (MachO) - outs() << SymbolAddrStr << " " << "0" /* SymbolSizeStr */ << "\n"; - else - outs() << SymbolAddrStr << " " << SymbolSizeStr << "\n"; + outs() << Name << " " << S.TypeChar << " " << SymbolAddrStr << " " + << (MachO ? "0" : SymbolSizeStr) << "\n"; } else if (OutputFormat == bsd || (OutputFormat == darwin && !MachO)) { if (PrintAddress) outs() << SymbolAddrStr << ' '; - if (PrintSize) { - outs() << SymbolSizeStr; - outs() << ' '; - } - outs() << I->TypeChar; - if (I->TypeChar == '-' && MachO) - darwinPrintStab(MachO, I); + if (PrintSize) + outs() << SymbolSizeStr << ' '; + outs() << S.TypeChar; + if (S.TypeChar == '-' && MachO) + darwinPrintStab(MachO, S); outs() << " " << Name; - if (I->TypeChar == 'I' && MachO) { + if (S.TypeChar == 'I' && MachO) { outs() << " (indirect for "; - if (I->Sym.getRawDataRefImpl().p) { + if (S.Sym.getRawDataRefImpl().p) { StringRef IndirectName; - if (MachO->getIndirectName(I->Sym.getRawDataRefImpl(), IndirectName)) + if (MachO->getIndirectName(S.Sym.getRawDataRefImpl(), IndirectName)) outs() << "?)"; else outs() << IndirectName << ")"; } else - outs() << I->IndirectName << ")"; + outs() << S.IndirectName << ")"; } outs() << "\n"; } else if (OutputFormat == sysv) { - std::string PaddedName(Name); - while (PaddedName.length() < 20) - PaddedName += " "; - outs() << PaddedName << "|" << SymbolAddrStr << "| " << I->TypeChar - << " | |" << SymbolSizeStr << "| |\n"; + outs() << left_justify(Name, 20) << "|" << SymbolAddrStr << "| " + << S.TypeChar << " |" << right_justify(S.TypeName, 18) << "|" + << SymbolSizeStr << "| |" << S.SectionName << "\n"; } } @@ -898,44 +894,35 @@ static char getSymbolNMTypeChar(ELFObjectFileBase &Obj, return '?'; } + uint8_t Binding = SymI->getBinding(); + if (Binding == ELF::STB_GNU_UNIQUE) + return 'u'; + + assert(Binding != ELF::STB_WEAK && "STB_WEAK not tested in calling function"); + if (Binding != ELF::STB_GLOBAL && Binding != ELF::STB_LOCAL) + return '?'; + elf_section_iterator SecI = *SecIOrErr; if (SecI != Obj.section_end()) { - switch (SecI->getType()) { - case ELF::SHT_PROGBITS: - case ELF::SHT_DYNAMIC: - switch (SecI->getFlags()) { - case (ELF::SHF_ALLOC | ELF::SHF_EXECINSTR): - return 't'; - case (ELF::SHF_TLS | ELF::SHF_ALLOC | ELF::SHF_WRITE): - case (ELF::SHF_ALLOC | ELF::SHF_WRITE): - return 'd'; - case ELF::SHF_ALLOC: - case (ELF::SHF_ALLOC | ELF::SHF_MERGE): - case (ELF::SHF_ALLOC | ELF::SHF_MERGE | ELF::SHF_STRINGS): - return 'r'; - } - break; - case ELF::SHT_NOBITS: - return 'b'; - case ELF::SHT_INIT_ARRAY: - case ELF::SHT_FINI_ARRAY: + uint32_t Type = SecI->getType(); + uint64_t Flags = SecI->getFlags(); + if (Flags & ELF::SHF_EXECINSTR) return 't'; - } - } + if (Type == ELF::SHT_NOBITS) + return 'b'; + if (Flags & ELF::SHF_ALLOC) + return Flags & ELF::SHF_WRITE ? 'd' : 'r'; - if (SymI->getELFType() == ELF::STT_SECTION) { - Expected<StringRef> Name = SymI->getName(); - if (!Name) { - consumeError(Name.takeError()); + StringRef SecName; + if (SecI->getName(SecName)) return '?'; - } - return StringSwitch<char>(*Name) - .StartsWith(".debug", 'N') - .StartsWith(".note", 'n') - .Default('?'); + if (SecName.startswith(".debug")) + return 'N'; + if (!(Flags & ELF::SHF_WRITE)) + return 'n'; } - return 'n'; + return '?'; } static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) { @@ -967,10 +954,9 @@ static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) { section_iterator SecI = *SecIOrErr; const coff_section *Section = Obj.getCOFFSection(*SecI); Characteristics = Section->Characteristics; - StringRef SectionName; - Obj.getSectionName(Section, SectionName); - if (SectionName.startswith(".idata")) - return 'i'; + if (Expected<StringRef> NameOrErr = Obj.getSectionName(Section)) + if (NameOrErr->startswith(".idata")) + return 'i'; } switch (Symb.getSectionNumber()) { @@ -1030,7 +1016,8 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) { return 's'; DataRefImpl Ref = Sec->getRawDataRefImpl(); StringRef SectionName; - Obj.getSectionName(Ref, SectionName); + if (Expected<StringRef> NameOrErr = Obj.getSectionName(Ref)) + SectionName = *NameOrErr; StringRef SegmentName = Obj.getSectionFinalSegmentName(Ref); if (Obj.is64Bit() && Obj.getHeader64().filetype == MachO::MH_KEXT_BUNDLE && SegmentName == "__TEXT_EXEC" && SectionName == "__text") @@ -1074,8 +1061,40 @@ static bool isObject(SymbolicFile &Obj, basic_symbol_iterator I) { : elf_symbol_iterator(I)->getELFType() == ELF::STT_OBJECT; } -static char getNMTypeChar(SymbolicFile &Obj, basic_symbol_iterator I) { +// For ELF object files, Set TypeName to the symbol typename, to be printed +// in the 'Type' column of the SYSV format output. +static StringRef getNMTypeName(SymbolicFile &Obj, basic_symbol_iterator I) { + if (isa<ELFObjectFileBase>(&Obj)) { + elf_symbol_iterator SymI(I); + return SymI->getELFTypeName(); + } + return ""; +} + +// Return Posix nm class type tag (single letter), but also set SecName and +// section and name, to be used in format=sysv output. +static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I, + StringRef &SecName) { uint32_t Symflags = I->getFlags(); + if (isa<ELFObjectFileBase>(&Obj)) { + if (Symflags & object::SymbolRef::SF_Absolute) + SecName = "*ABS*"; + else if (Symflags & object::SymbolRef::SF_Common) + SecName = "*COM*"; + else if (Symflags & object::SymbolRef::SF_Undefined) + SecName = "*UND*"; + else { + elf_symbol_iterator SymI(I); + Expected<elf_section_iterator> SecIOrErr = SymI->getSection(); + if (!SecIOrErr) { + consumeError(SecIOrErr.takeError()); + return '?'; + } + elf_section_iterator secT = *SecIOrErr; + secT->getName(SecName); + } + } + 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; @@ -1103,10 +1122,13 @@ static char getNMTypeChar(SymbolicFile &Obj, basic_symbol_iterator I) { else Ret = getSymbolNMTypeChar(cast<ELFObjectFileBase>(Obj), I); - if (Symflags & object::SymbolRef::SF_Global) - Ret = toupper(Ret); + if (!(Symflags & object::SymbolRef::SF_Global)) + return Ret; - return Ret; + if (Obj.isELF() && ELFSymbolRef(*I).getBinding() == ELF::STB_GNU_UNIQUE) + return Ret; + + return toupper(Ret); } // getNsectForSegSect() is used to implement the Mach-O "-s segname sectname" @@ -1120,7 +1142,8 @@ static unsigned getNsectForSegSect(MachOObjectFile *Obj) { for (auto &S : Obj->sections()) { DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SectionName; - Obj->getSectionName(Ref, SectionName); + if (Expected<StringRef> NameOrErr = Obj->getSectionName(Ref)) + SectionName = *NameOrErr; StringRef SegmentName = Obj->getSectionFinalSegmentName(Ref); if (SegmentName == SegSect[0] && SectionName == SegSect[1]) return Nsect; @@ -1155,9 +1178,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, error("File format has no dynamic symbol table", Obj.getFileName()); return; } - auto DynSymbols = E->getDynamicSymbolIterators(); - Symbols = - make_range<basic_symbol_iterator>(DynSymbols.begin(), DynSymbols.end()); + Symbols = E->getDynamicSymbolIterators(); } std::string NameBuffer; raw_string_ostream OS(NameBuffer); @@ -1186,10 +1207,8 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, NMSymbol S = {}; S.Size = 0; S.Address = 0; - if (PrintSize) { - if (isa<ELFObjectFileBase>(&Obj)) - S.Size = ELFSymbolRef(Sym).getSize(); - } + if (isa<ELFObjectFileBase>(&Obj)) + S.Size = ELFSymbolRef(Sym).getSize(); if (PrintAddress && isa<ObjectFile>(Obj)) { SymbolRef SymRef(Sym); Expected<uint64_t> AddressOrErr = SymRef.getAddress(); @@ -1199,12 +1218,15 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, } S.Address = *AddressOrErr; } - S.TypeChar = getNMTypeChar(Obj, Sym); - std::error_code EC = Sym.printName(OS); - if (EC && MachO) - OS << "bad string index"; - else - error(EC); + S.TypeName = getNMTypeName(Obj, Sym); + S.TypeChar = getNMSectionTagAndName(Obj, Sym, S.SectionName); + if (Error E = Sym.printName(OS)) { + if (MachO) { + OS << "bad string index"; + consumeError(std::move(E)); + } else + error(std::move(E), Obj.getFileName()); + } OS << '\0'; S.Sym = Sym; SymbolList.push_back(S); @@ -1270,11 +1292,12 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, bool found = false; bool ReExport = false; if (!DyldInfoOnly) { - for (unsigned J = 0; J < SymbolList.size() && !found; ++J) { - if (SymbolList[J].Address == Entry.address() + BaseSegmentAddress && - SymbolList[J].Name == Entry.name()) + for (const NMSymbol &S : SymbolList) + if (S.Address == Entry.address() + BaseSegmentAddress && + S.Name == Entry.name()) { found = true; - } + break; + } } if (!found) { NMSymbol S = {}; @@ -1445,7 +1468,6 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, B.NType = MachO::N_EXT | MachO::N_UNDF; B.NSect = 0; B.NDesc = 0; - B.NDesc = 0; MachO::SET_LIBRARY_ORDINAL(B.NDesc, Entry.ordinal()); B.IndirectName = StringRef(); B.Name = Entry.symbolName(); @@ -1735,8 +1757,9 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { return; LLVMContext Context; - Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary( - BufferOrErr.get()->getMemBufferRef(), NoLLVMBitcode ? nullptr : &Context); + LLVMContext *ContextPtr = NoLLVMBitcode ? nullptr : &Context; + Expected<std::unique_ptr<Binary>> BinaryOrErr = + createBinary(BufferOrErr.get()->getMemBufferRef(), ContextPtr); if (!BinaryOrErr) { error(BinaryOrErr.takeError(), Filename); return; @@ -1770,7 +1793,8 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { { Error Err = Error::success(); for (auto &C : A->children(Err)) { - Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(&Context); + Expected<std::unique_ptr<Binary>> ChildOrErr = + C.getAsBinary(ContextPtr); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) error(std::move(E), Filename, C); @@ -1841,7 +1865,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { Error Err = Error::success(); for (auto &C : A->children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = - C.getAsBinary(&Context); + C.getAsBinary(ContextPtr); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType( ChildOrErr.takeError())) { @@ -1912,7 +1936,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { Error Err = Error::success(); for (auto &C : A->children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = - C.getAsBinary(&Context); + C.getAsBinary(ContextPtr); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType( ChildOrErr.takeError())) @@ -1946,10 +1970,8 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { // Either all architectures have been specified or none have been specified // and this does not contain the host architecture so dump all the slices. bool moreThanOneArch = UB->getNumberOfObjects() > 1; - for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), - E = UB->end_objects(); - I != E; ++I) { - Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); + for (const MachOUniversalBinary::ObjectForArch &O : UB->objects()) { + Expected<std::unique_ptr<ObjectFile>> ObjOrErr = O.getAsObjectFile(); std::string ArchiveName; std::string ArchitectureName; ArchiveName.clear(); @@ -1958,28 +1980,28 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { ObjectFile &Obj = *ObjOrErr.get(); if (PrintFileName) { if (isa<MachOObjectFile>(Obj) && moreThanOneArch) - ArchitectureName = I->getArchFlagName(); + ArchitectureName = O.getArchFlagName(); } else { if (moreThanOneArch) outs() << "\n"; outs() << Obj.getFileName(); if (isa<MachOObjectFile>(Obj) && moreThanOneArch) - outs() << " (for architecture " << I->getArchFlagName() << ")"; + outs() << " (for architecture " << O.getArchFlagName() << ")"; outs() << ":\n"; } dumpSymbolNamesFromObject(Obj, false, ArchiveName, ArchitectureName); } else if (auto E = isNotObjectErrorInvalidFileType( ObjOrErr.takeError())) { error(std::move(E), Filename, moreThanOneArch ? - StringRef(I->getArchFlagName()) : StringRef()); + StringRef(O.getArchFlagName()) : StringRef()); continue; } else if (Expected<std::unique_ptr<Archive>> AOrErr = - I->getAsArchive()) { + O.getAsArchive()) { std::unique_ptr<Archive> &A = *AOrErr; Error Err = Error::success(); for (auto &C : A->children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = - C.getAsBinary(&Context); + C.getAsBinary(ContextPtr); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType( ChildOrErr.takeError())) @@ -1987,23 +2009,23 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { StringRef(ArchitectureName) : StringRef()); continue; } - if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { + if (SymbolicFile *F = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { if (PrintFileName) { ArchiveName = A->getFileName(); - if (isa<MachOObjectFile>(O) && moreThanOneArch) - ArchitectureName = I->getArchFlagName(); + if (isa<MachOObjectFile>(F) && moreThanOneArch) + ArchitectureName = O.getArchFlagName(); } else { outs() << "\n" << A->getFileName(); - if (isa<MachOObjectFile>(O)) { - outs() << "(" << O->getFileName() << ")"; + if (isa<MachOObjectFile>(F)) { + outs() << "(" << F->getFileName() << ")"; if (moreThanOneArch) - outs() << " (for architecture " << I->getArchFlagName() + outs() << " (for architecture " << O.getArchFlagName() << ")"; } else - outs() << ":" << O->getFileName(); + outs() << ":" << F->getFileName(); outs() << ":\n"; } - dumpSymbolNamesFromObject(*O, false, ArchiveName, ArchitectureName); + dumpSymbolNamesFromObject(*F, false, ArchiveName, ArchitectureName); } } if (Err) @@ -2011,7 +2033,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { } else { consumeError(AOrErr.takeError()); error(Filename + " for architecture " + - StringRef(I->getArchFlagName()) + + StringRef(O.getArchFlagName()) + " is not a Mach-O file or an archive file", "Mach-O universal file"); } @@ -2021,7 +2043,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { if (SymbolicFile *O = dyn_cast<SymbolicFile>(&Bin)) { if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) { WithColor::warning(errs(), ToolName) - << "sizes with -print-size for Mach-O files are always zero.\n"; + << "sizes with --print-size for Mach-O files are always zero.\n"; MachOPrintSizeWarning = true; } if (!checkMachOAndArchFlags(O, Filename)) @@ -2032,6 +2054,7 @@ 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"); // llvm-nm only reads binary files. @@ -2063,13 +2086,17 @@ int main(int argc, char **argv) { 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 the -arch option"); + "for the --arch option"); } } @@ -2078,7 +2105,7 @@ int main(int argc, char **argv) { "for the -s option"); if (NoDyldInfo && (AddDyldInfo || DyldInfoOnly)) - error("-no-dyldinfo can't be used with -add-dyldinfo or -dyldinfo-only"); + error("--no-dyldinfo can't be used with --add-dyldinfo or --dyldinfo-only"); llvm::for_each(InputFilenames, dumpSymbolNamesFromFile); diff --git a/contrib/llvm/tools/llvm-objcopy/Buffer.cpp b/contrib/llvm/tools/llvm-objcopy/Buffer.cpp index 8044b023aaad..06b2a20a762f 100644 --- a/contrib/llvm/tools/llvm-objcopy/Buffer.cpp +++ b/contrib/llvm/tools/llvm-objcopy/Buffer.cpp @@ -1,16 +1,16 @@ //===- Buffer.cpp ---------------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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-objcopy.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 { @@ -18,23 +18,51 @@ namespace objcopy { Buffer::~Buffer() {} -void FileBuffer::allocate(size_t Size) { +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); - handleAllErrors(BufferOrErr.takeError(), [this](const ErrorInfoBase &E) { - error("failed to open " + getName() + ": " + E.message()); - }); + // 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() { return Buf->commit(); } +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()); } -void MemBuffer::allocate(size_t Size) { +Error MemBuffer::allocate(size_t Size) { Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName()); + return Error::success(); } Error MemBuffer::commit() { return Error::success(); } diff --git a/contrib/llvm/tools/llvm-objcopy/Buffer.h b/contrib/llvm/tools/llvm-objcopy/Buffer.h index e5b9c5b2d22b..487d5585c364 100644 --- a/contrib/llvm/tools/llvm-objcopy/Buffer.h +++ b/contrib/llvm/tools/llvm-objcopy/Buffer.h @@ -1,9 +1,8 @@ //===- Buffer.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -28,7 +27,7 @@ class Buffer { public: virtual ~Buffer(); - virtual void allocate(size_t Size) = 0; + virtual Error allocate(size_t Size) = 0; virtual uint8_t *getBufferStart() = 0; virtual Error commit() = 0; @@ -38,9 +37,12 @@ public: 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: - void allocate(size_t Size) override; + Error allocate(size_t Size) override; uint8_t *getBufferStart() override; Error commit() override; @@ -51,7 +53,7 @@ class MemBuffer : public Buffer { std::unique_ptr<WritableMemoryBuffer> Buf; public: - void allocate(size_t Size) override; + Error allocate(size_t Size) override; uint8_t *getBufferStart() override; Error commit() override; diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp index 6b386d29979c..4ae46851a66f 100644 --- a/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -1,9 +1,8 @@ //===- COFFObjcopy.cpp ----------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -18,6 +17,8 @@ #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/JamCRC.h" +#include "llvm/Support/Path.h" #include <cassert> namespace llvm { @@ -27,14 +28,104 @@ namespace coff { using namespace object; using namespace COFF; +static bool isDebugSection(const Section &Sec) { + return Sec.Name.startswith(".debug"); +} + +static uint64_t getNextRVA(const Object &Obj) { + if (Obj.getSections().empty()) + return 0; + const Section &Last = Obj.getSections().back(); + return alignTo(Last.Header.VirtualAddress + Last.Header.VirtualSize, + Obj.IsPE ? Obj.PeHeader.SectionAlignment : 1); +} + +static uint32_t getCRC32(StringRef Data) { + JamCRC CRC; + CRC.update(ArrayRef<char>(Data.data(), Data.size())); + // The CRC32 value needs to be complemented because the JamCRC dosn't + // finalize the CRC32 value. It also dosn't negate the initial CRC32 value + // but it starts by default at 0xFFFFFFFF which is the complement of zero. + return ~CRC.getCRC(); +} + +static std::vector<uint8_t> createGnuDebugLinkSectionContents(StringRef File) { + ErrorOr<std::unique_ptr<MemoryBuffer>> LinkTargetOrErr = + MemoryBuffer::getFile(File); + if (!LinkTargetOrErr) + error("'" + File + "': " + LinkTargetOrErr.getError().message()); + auto LinkTarget = std::move(*LinkTargetOrErr); + uint32_t CRC32 = getCRC32(LinkTarget->getBuffer()); + + StringRef FileName = sys::path::filename(File); + size_t CRCPos = alignTo(FileName.size() + 1, 4); + std::vector<uint8_t> Data(CRCPos + 4); + memcpy(Data.data(), FileName.data(), FileName.size()); + support::endian::write32le(Data.data() + CRCPos, CRC32); + return Data; +} + +static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) { + uint32_t StartRVA = getNextRVA(Obj); + + std::vector<Section> Sections; + Section Sec; + Sec.setOwnedContents(createGnuDebugLinkSectionContents(DebugLinkFile)); + Sec.Name = ".gnu_debuglink"; + Sec.Header.VirtualSize = Sec.getContents().size(); + Sec.Header.VirtualAddress = StartRVA; + Sec.Header.SizeOfRawData = alignTo(Sec.Header.VirtualSize, + Obj.IsPE ? Obj.PeHeader.FileAlignment : 1); + // Sec.Header.PointerToRawData is filled in by the writer. + Sec.Header.PointerToRelocations = 0; + Sec.Header.PointerToLinenumbers = 0; + // Sec.Header.NumberOfRelocations is filled in by the writer. + Sec.Header.NumberOfLinenumbers = 0; + Sec.Header.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE; + Sections.push_back(Sec); + Obj.addSections(Sections); +} + static Error handleArgs(const CopyConfig &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 + // aren't mentioned. + if (!Config.OnlySection.empty() && + !is_contained(Config.OnlySection, Sec.Name)) + return true; + + if (Config.StripDebug || Config.StripAll || Config.StripAllGNU || + Config.DiscardMode == DiscardType::All || Config.StripUnneeded) { + if (isDebugSection(Sec) && + (Sec.Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) + return true; + } + + if (is_contained(Config.ToRemove, Sec.Name)) + return true; + + return false; + }); + + if (Config.OnlyKeepDebug) { + // For --only-keep-debug, we keep all other sections, but remove their + // content. The VirtualSize field in the section header is kept intact. + Obj.truncateSections([](const Section &Sec) { + return !isDebugSection(Sec) && Sec.Name != ".buildid" && + ((Sec.Header.Characteristics & + (IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA)) != 0); + }); + } + // StripAll removes all symbols and thus also removes all relocations. if (Config.StripAll || Config.StripAllGNU) - for (Section &Sec : Obj.Sections) + for (Section &Sec : Obj.getMutableSections()) Sec.Relocs.clear(); // If we need to do per-symbol removals, initialize the Referenced field. - if (Config.StripUnneeded || Config.DiscardAll || + if (Config.StripUnneeded || Config.DiscardMode == DiscardType::All || !Config.SymbolsToRemove.empty()) if (Error E = Obj.markSymbols()) return E; @@ -50,47 +141,74 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { // Explicitly removing a referenced symbol is an error. if (Sym.Referenced) reportError(Config.OutputFilename, - make_error<StringError>( - "not stripping symbol '" + Sym.Name + - "' because it is named in a relocation.", - llvm::errc::invalid_argument)); + createStringError(llvm::errc::invalid_argument, + "not stripping symbol '%s' because it is " + "named in a relocation", + Sym.Name.str().c_str())); return true; } if (!Sym.Referenced) { // With --strip-unneeded, GNU objcopy removes all unreferenced local // symbols, and any unreferenced undefined external. - if (Config.StripUnneeded && - (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC || - Sym.Sym.SectionNumber == 0)) - return true; + // With --strip-unneeded-symbol we strip only specific unreferenced + // local symbol instead of removing all of such. + if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC || + Sym.Sym.SectionNumber == 0) + if (Config.StripUnneeded || + is_contained(Config.UnneededSymbolsToRemove, Sym.Name)) + return true; // GNU objcopy keeps referenced local symbols and external symbols // if --discard-all is set, similar to what --strip-unneeded does, // but undefined local symbols are kept when --discard-all is set. - if (Config.DiscardAll && Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC && + if (Config.DiscardMode == DiscardType::All && + Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC && Sym.Sym.SectionNumber != 0) return true; } return false; }); + + if (!Config.AddGnuDebugLink.empty()) + addGnuDebugLink(Obj, Config.AddGnuDebugLink); + + if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || + Config.BuildIdLinkInput || Config.BuildIdLinkOutput || + !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || + !Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() || + !Config.DumpSection.empty() || !Config.KeepSection.empty() || + !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || + !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || + !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || + !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty() || + Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || + Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc || + Config.StripSections || 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(); } -void executeObjcopyOnBinary(const CopyConfig &Config, - object::COFFObjectFile &In, Buffer &Out) { +Error executeObjcopyOnBinary(const CopyConfig &Config, COFFObjectFile &In, + Buffer &Out) { COFFReader Reader(In); Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create(); if (!ObjOrErr) - reportError(Config.InputFilename, ObjOrErr.takeError()); + return createFileError(Config.InputFilename, ObjOrErr.takeError()); Object *Obj = ObjOrErr->get(); assert(Obj && "Unable to deserialize COFF object"); if (Error E = handleArgs(Config, *Obj)) - reportError(Config.InputFilename, std::move(E)); + return createFileError(Config.InputFilename, std::move(E)); COFFWriter Writer(*Obj, Out); if (Error E = Writer.write()) - reportError(Config.OutputFilename, std::move(E)); + return createFileError(Config.OutputFilename, std::move(E)); + return Error::success(); } } // end namespace coff diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h b/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h index bf70bd9b4d84..858759e52c4a 100644 --- a/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h +++ b/contrib/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.h @@ -1,9 +1,8 @@ //===- COFFObjcopy.h --------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -11,6 +10,7 @@ #define LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H namespace llvm { +class Error; namespace object { class COFFObjectFile; @@ -21,8 +21,8 @@ struct CopyConfig; class Buffer; namespace coff { -void executeObjcopyOnBinary(const CopyConfig &Config, - object::COFFObjectFile &In, Buffer &Out); +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::COFFObjectFile &In, Buffer &Out); } // end namespace coff } // end namespace objcopy diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Object.cpp b/contrib/llvm/tools/llvm-objcopy/COFF/Object.cpp index 315d3a778623..b07532c1dc39 100644 --- a/contrib/llvm/tools/llvm-objcopy/COFF/Object.cpp +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Object.cpp @@ -1,13 +1,13 @@ //===- Object.cpp ---------------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 "Object.h" +#include "llvm/ADT/DenseSet.h" #include <algorithm> namespace llvm { @@ -26,12 +26,8 @@ void Object::addSymbols(ArrayRef<Symbol> NewSymbols) { void Object::updateSymbols() { SymbolMap = DenseMap<size_t, Symbol *>(Symbols.size()); - size_t RawSymIndex = 0; - for (Symbol &Sym : Symbols) { + for (Symbol &Sym : Symbols) SymbolMap[Sym.UniqueId] = &Sym; - Sym.RawIndex = RawSymIndex; - RawSymIndex += 1 + Sym.Sym.NumberOfAuxSymbols; - } } const Symbol *Object::findSymbol(size_t UniqueId) const { @@ -56,15 +52,86 @@ Error Object::markSymbols() { for (const Relocation &R : Sec.Relocs) { auto It = SymbolMap.find(R.Target); if (It == SymbolMap.end()) - return make_error<StringError>("Relocation target " + Twine(R.Target) + - " not found", - object_error::invalid_symbol_index); + return createStringError(object_error::invalid_symbol_index, + "relocation target %zu not found", R.Target); It->second->Referenced = true; } } return Error::success(); } +void Object::addSections(ArrayRef<Section> NewSections) { + for (Section S : NewSections) { + S.UniqueId = NextSectionUniqueId++; + Sections.emplace_back(S); + } + updateSections(); +} + +void Object::updateSections() { + SectionMap = DenseMap<ssize_t, Section *>(Sections.size()); + size_t Index = 1; + for (Section &S : Sections) { + SectionMap[S.UniqueId] = &S; + S.Index = Index++; + } +} + +const Section *Object::findSection(ssize_t UniqueId) const { + auto It = SectionMap.find(UniqueId); + if (It == SectionMap.end()) + return nullptr; + return It->second; +} + +void Object::removeSections(function_ref<bool(const Section &)> ToRemove) { + DenseSet<ssize_t> AssociatedSections; + auto RemoveAssociated = [&AssociatedSections](const Section &Sec) { + return AssociatedSections.count(Sec.UniqueId) == 1; + }; + do { + DenseSet<ssize_t> RemovedSections; + Sections.erase( + std::remove_if(std::begin(Sections), std::end(Sections), + [ToRemove, &RemovedSections](const Section &Sec) { + bool Remove = ToRemove(Sec); + if (Remove) + RemovedSections.insert(Sec.UniqueId); + return Remove; + }), + std::end(Sections)); + // Remove all symbols referring to the removed sections. + AssociatedSections.clear(); + Symbols.erase( + std::remove_if( + std::begin(Symbols), std::end(Symbols), + [&RemovedSections, &AssociatedSections](const Symbol &Sym) { + // If there are sections that are associative to a removed + // section, + // remove those as well as nothing will include them (and we can't + // leave them dangling). + if (RemovedSections.count(Sym.AssociativeComdatTargetSectionId) == + 1) + AssociatedSections.insert(Sym.TargetSectionId); + return RemovedSections.count(Sym.TargetSectionId) == 1; + }), + std::end(Symbols)); + ToRemove = RemoveAssociated; + } while (!AssociatedSections.empty()); + updateSections(); + updateSymbols(); +} + +void Object::truncateSections(function_ref<bool(const Section &)> ToTruncate) { + for (Section &Sec : Sections) { + if (ToTruncate(Sec)) { + Sec.clearContents(); + Sec.Relocs.clear(); + Sec.Header.SizeOfRawData = 0; + } + } +} + } // end namespace coff } // end namespace objcopy } // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Object.h b/contrib/llvm/tools/llvm-objcopy/COFF/Object.h index 7531fb4cf39e..21475b068629 100644 --- a/contrib/llvm/tools/llvm-objcopy/COFF/Object.h +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Object.h @@ -1,9 +1,8 @@ //===- Object.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -12,6 +11,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" #include "llvm/BinaryFormat/COFF.h" @@ -35,15 +35,58 @@ struct Relocation { struct Section { object::coff_section Header; - ArrayRef<uint8_t> Contents; std::vector<Relocation> Relocs; StringRef Name; + ssize_t UniqueId; + size_t Index; + + ArrayRef<uint8_t> getContents() const { + if (!OwnedContents.empty()) + return OwnedContents; + return ContentsRef; + } + + void setContentsRef(ArrayRef<uint8_t> Data) { + OwnedContents.clear(); + ContentsRef = Data; + } + + void setOwnedContents(std::vector<uint8_t> &&Data) { + ContentsRef = ArrayRef<uint8_t>(); + OwnedContents = std::move(Data); + } + + void clearContents() { + ContentsRef = ArrayRef<uint8_t>(); + OwnedContents.clear(); + } + +private: + ArrayRef<uint8_t> ContentsRef; + std::vector<uint8_t> OwnedContents; +}; + +struct AuxSymbol { + AuxSymbol(ArrayRef<uint8_t> In) { + assert(In.size() == sizeof(Opaque)); + std::copy(In.begin(), In.end(), Opaque); + } + + ArrayRef<uint8_t> getRef() const { + return ArrayRef<uint8_t>(Opaque, sizeof(Opaque)); + } + + uint8_t Opaque[sizeof(object::coff_symbol16)]; }; struct Symbol { object::coff_symbol32 Sym; StringRef Name; - ArrayRef<uint8_t> AuxData; + std::vector<AuxSymbol> AuxData; + StringRef AuxFile; + ssize_t TargetSectionId; + ssize_t AssociativeComdatTargetSectionId = 0; + Optional<size_t> WeakTargetSymbolId; size_t UniqueId; size_t RawIndex; bool Referenced; @@ -62,7 +105,6 @@ struct Object { uint32_t BaseOfData = 0; // pe32plus_header lacks this field. std::vector<object::data_directory> DataDirectories; - std::vector<Section> Sections; ArrayRef<Symbol> getSymbols() const { return Symbols; } // This allows mutating individual Symbols, but not mutating the list @@ -80,14 +122,35 @@ struct Object { // all sections. Error markSymbols(); + ArrayRef<Section> getSections() const { return Sections; } + // This allows mutating individual Sections, but not mutating the list + // of symbols itself. + iterator_range<std::vector<Section>::iterator> getMutableSections() { + return make_range(Sections.begin(), Sections.end()); + } + + const Section *findSection(ssize_t UniqueId) const; + + void addSections(ArrayRef<Section> NewSections); + void removeSections(function_ref<bool(const Section &)> ToRemove); + void truncateSections(function_ref<bool(const Section &)> ToTruncate); + private: std::vector<Symbol> Symbols; DenseMap<size_t, Symbol *> SymbolMap; size_t NextSymbolUniqueId = 0; - // Update SymbolMap and RawIndex in each Symbol. + std::vector<Section> Sections; + DenseMap<ssize_t, Section *> SectionMap; + + ssize_t NextSectionUniqueId = 1; // Allow a UniqueId 0 to mean undefined. + + // Update SymbolMap. void updateSymbols(); + + // Update SectionMap and Index in each Section. + void updateSections(); }; // Copy between coff_symbol16 and coff_symbol32. diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/contrib/llvm/tools/llvm-objcopy/COFF/Reader.cpp index a01768392d7d..1f0ec9fa9691 100644 --- a/contrib/llvm/tools/llvm-objcopy/COFF/Reader.cpp +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Reader.cpp @@ -1,17 +1,16 @@ //===- Reader.cpp ---------------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 "Reader.h" #include "Object.h" -#include "llvm-objcopy.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #include "llvm/Support/ErrorHandling.h" #include <cstddef> @@ -22,6 +21,7 @@ namespace objcopy { namespace coff { using namespace object; +using namespace COFF; Error COFFReader::readExecutableHeaders(Object &Obj) const { const dos_header *DH = COFFObj.getDOSHeader(); @@ -59,31 +59,38 @@ Error COFFReader::readExecutableHeaders(Object &Obj) const { } Error COFFReader::readSections(Object &Obj) const { + std::vector<Section> Sections; // Section indexing starts from 1. for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) { const coff_section *Sec; if (auto EC = COFFObj.getSection(I, Sec)) return errorCodeToError(EC); - Obj.Sections.push_back(Section()); - Section &S = Obj.Sections.back(); + Sections.push_back(Section()); + Section &S = Sections.back(); S.Header = *Sec; - if (auto EC = COFFObj.getSectionContents(Sec, S.Contents)) - return errorCodeToError(EC); + ArrayRef<uint8_t> Contents; + if (Error E = COFFObj.getSectionContents(Sec, Contents)) + return E; + S.setContentsRef(Contents); ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec); for (const coff_relocation &R : Relocs) S.Relocs.push_back(R); - if (auto EC = COFFObj.getSectionName(Sec, S.Name)) - return errorCodeToError(EC); + if (Expected<StringRef> NameOrErr = COFFObj.getSectionName(Sec)) + S.Name = *NameOrErr; + else + return NameOrErr.takeError(); if (Sec->hasExtendedRelocations()) - return make_error<StringError>("Extended relocations not supported yet", - object_error::parse_failed); + return createStringError(object_error::parse_failed, + "extended relocations not supported yet"); } + Obj.addSections(Sections); return Error::success(); } Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const { std::vector<Symbol> Symbols; Symbols.reserve(COFFObj.getRawNumberOfSymbols()); + ArrayRef<Section> Sections = Obj.getSections(); for (uint32_t I = 0, E = COFFObj.getRawNumberOfSymbols(); I < E;) { Expected<COFFSymbolRef> SymOrErr = COFFObj.getSymbol(I); if (!SymOrErr) @@ -101,31 +108,86 @@ Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const { *reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr())); if (auto EC = COFFObj.getSymbolName(SymRef, Sym.Name)) return errorCodeToError(EC); - Sym.AuxData = COFFObj.getSymbolAuxData(SymRef); - assert((Sym.AuxData.size() % - (IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16))) == 0); + + ArrayRef<uint8_t> AuxData = COFFObj.getSymbolAuxData(SymRef); + size_t SymSize = IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16); + assert(AuxData.size() == SymSize * SymRef.getNumberOfAuxSymbols()); + // The auxillary symbols are structs of sizeof(coff_symbol16) each. + // In the big object format (where symbols are coff_symbol32), each + // auxillary symbol is padded with 2 bytes at the end. Copy each + // auxillary symbol to the Sym.AuxData vector. For file symbols, + // the whole range of aux symbols are interpreted as one null padded + // string instead. + if (SymRef.isFileRecord()) + Sym.AuxFile = StringRef(reinterpret_cast<const char *>(AuxData.data()), + AuxData.size()) + .rtrim('\0'); + else + for (size_t I = 0; I < SymRef.getNumberOfAuxSymbols(); I++) + Sym.AuxData.push_back(AuxData.slice(I * SymSize, sizeof(AuxSymbol))); + + // Find the unique id of the section + if (SymRef.getSectionNumber() <= + 0) // Special symbol (undefined/absolute/debug) + Sym.TargetSectionId = SymRef.getSectionNumber(); + else if (static_cast<uint32_t>(SymRef.getSectionNumber() - 1) < + Sections.size()) + Sym.TargetSectionId = Sections[SymRef.getSectionNumber() - 1].UniqueId; + else + return createStringError(object_error::parse_failed, + "section number out of range"); + // For section definitions, check if it is comdat associative, and if + // it is, find the target section unique id. + const coff_aux_section_definition *SD = SymRef.getSectionDefinition(); + const coff_aux_weak_external *WE = SymRef.getWeakExternal(); + if (SD && SD->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + int32_t Index = SD->getNumber(IsBigObj); + if (Index <= 0 || static_cast<uint32_t>(Index - 1) >= Sections.size()) + return createStringError(object_error::parse_failed, + "unexpected associative section index"); + Sym.AssociativeComdatTargetSectionId = Sections[Index - 1].UniqueId; + } else if (WE) { + // This is a raw symbol index for now, but store it in the Symbol + // until we've added them to the Object, which assigns the final + // unique ids. + Sym.WeakTargetSymbolId = WE->TagIndex; + } I += 1 + SymRef.getNumberOfAuxSymbols(); } Obj.addSymbols(Symbols); return Error::success(); } -Error COFFReader::setRelocTargets(Object &Obj) const { +Error COFFReader::setSymbolTargets(Object &Obj) const { std::vector<const Symbol *> RawSymbolTable; for (const Symbol &Sym : Obj.getSymbols()) { RawSymbolTable.push_back(&Sym); for (size_t I = 0; I < Sym.Sym.NumberOfAuxSymbols; I++) RawSymbolTable.push_back(nullptr); } - for (Section &Sec : Obj.Sections) { + for (Symbol &Sym : Obj.getMutableSymbols()) { + // Convert WeakTargetSymbolId from the original raw symbol index to + // a proper unique id. + if (Sym.WeakTargetSymbolId) { + if (*Sym.WeakTargetSymbolId >= RawSymbolTable.size()) + return createStringError(object_error::parse_failed, + "weak external reference out of range"); + const Symbol *Target = RawSymbolTable[*Sym.WeakTargetSymbolId]; + if (Target == nullptr) + return createStringError(object_error::parse_failed, + "invalid SymbolTableIndex"); + Sym.WeakTargetSymbolId = Target->UniqueId; + } + } + for (Section &Sec : Obj.getMutableSections()) { for (Relocation &R : Sec.Relocs) { if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size()) - return make_error<StringError>("SymbolTableIndex out of range", - object_error::parse_failed); + return createStringError(object_error::parse_failed, + "SymbolTableIndex out of range"); const Symbol *Sym = RawSymbolTable[R.Reloc.SymbolTableIndex]; if (Sym == nullptr) - return make_error<StringError>("Invalid SymbolTableIndex", - object_error::parse_failed); + return createStringError(object_error::parse_failed, + "invalid SymbolTableIndex"); R.Target = Sym->UniqueId; R.TargetName = Sym->Name; } @@ -145,8 +207,8 @@ Expected<std::unique_ptr<Object>> COFFReader::create() const { Obj->CoffFileHeader = *CFH; } else { if (!CBFH) - return make_error<StringError>("No COFF file header returned", - object_error::parse_failed); + return createStringError(object_error::parse_failed, + "no COFF file header returned"); // Only copying the few fields from the bigobj header that we need // and won't recreate in the end. Obj->CoffFileHeader.Machine = CBFH->Machine; @@ -160,7 +222,7 @@ Expected<std::unique_ptr<Object>> COFFReader::create() const { return std::move(E); if (Error E = readSymbols(*Obj, IsBigObj)) return std::move(E); - if (Error E = setRelocTargets(*Obj)) + if (Error E = setSymbolTargets(*Obj)) return std::move(E); return std::move(Obj); diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Reader.h b/contrib/llvm/tools/llvm-objcopy/COFF/Reader.h index ca7057d08c9f..ec15369db0b8 100644 --- a/contrib/llvm/tools/llvm-objcopy/COFF/Reader.h +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Reader.h @@ -1,9 +1,8 @@ //===- Reader.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -29,7 +28,7 @@ class COFFReader { Error readExecutableHeaders(Object &Obj) const; Error readSections(Object &Obj) const; Error readSymbols(Object &Obj, bool IsBigObj) const; - Error setRelocTargets(Object &Obj) const; + Error setSymbolTargets(Object &Obj) const; public: explicit COFFReader(const COFFObjectFile &O) : COFFObj(O) {} diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/contrib/llvm/tools/llvm-objcopy/COFF/Writer.cpp index 385d43b1bae5..f3bb1ce331f2 100644 --- a/contrib/llvm/tools/llvm-objcopy/COFF/Writer.cpp +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Writer.cpp @@ -1,15 +1,13 @@ //===- Writer.cpp ---------------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 "Writer.h" #include "Object.h" -#include "llvm-objcopy.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/COFF.h" @@ -26,22 +24,75 @@ using namespace object; using namespace COFF; Error COFFWriter::finalizeRelocTargets() { - for (Section &Sec : Obj.Sections) { + for (Section &Sec : Obj.getMutableSections()) { for (Relocation &R : Sec.Relocs) { const Symbol *Sym = Obj.findSymbol(R.Target); if (Sym == nullptr) - return make_error<StringError>("Relocation target " + R.TargetName + - " (" + Twine(R.Target) + - ") not found", - object_error::invalid_symbol_index); + return createStringError(object_error::invalid_symbol_index, + "relocation target '%s' (%zu) not found", + R.TargetName.str().c_str(), R.Target); R.Reloc.SymbolTableIndex = Sym->RawIndex; } } return Error::success(); } +Error COFFWriter::finalizeSymbolContents() { + for (Symbol &Sym : Obj.getMutableSymbols()) { + if (Sym.TargetSectionId <= 0) { + // Undefined, or a special kind of symbol. These negative values + // are stored in the SectionNumber field which is unsigned. + Sym.Sym.SectionNumber = static_cast<uint32_t>(Sym.TargetSectionId); + } else { + const Section *Sec = Obj.findSection(Sym.TargetSectionId); + if (Sec == nullptr) + return createStringError(object_error::invalid_symbol_index, + "symbol '%s' points to a removed section", + Sym.Name.str().c_str()); + Sym.Sym.SectionNumber = Sec->Index; + + if (Sym.Sym.NumberOfAuxSymbols == 1 && + Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC) { + coff_aux_section_definition *SD = + reinterpret_cast<coff_aux_section_definition *>( + Sym.AuxData[0].Opaque); + uint32_t SDSectionNumber; + if (Sym.AssociativeComdatTargetSectionId == 0) { + // Not a comdat associative section; just set the Number field to + // the number of the section itself. + SDSectionNumber = Sec->Index; + } else { + Sec = Obj.findSection(Sym.AssociativeComdatTargetSectionId); + if (Sec == nullptr) + return createStringError( + object_error::invalid_symbol_index, + "symbol '%s' is associative to a removed section", + Sym.Name.str().c_str()); + SDSectionNumber = Sec->Index; + } + // Update the section definition with the new section number. + SD->NumberLowPart = static_cast<uint16_t>(SDSectionNumber); + SD->NumberHighPart = static_cast<uint16_t>(SDSectionNumber >> 16); + } + } + // Check that we actually have got AuxData to match the weak symbol target + // we want to set. Only >= 1 would be required, but only == 1 makes sense. + if (Sym.WeakTargetSymbolId && Sym.Sym.NumberOfAuxSymbols == 1) { + coff_aux_weak_external *WE = + reinterpret_cast<coff_aux_weak_external *>(Sym.AuxData[0].Opaque); + const Symbol *Target = Obj.findSymbol(*Sym.WeakTargetSymbolId); + if (Target == nullptr) + return createStringError(object_error::invalid_symbol_index, + "symbol '%s' is missing its weak target", + Sym.Name.str().c_str()); + WE->TagIndex = Target->RawIndex; + } + } + return Error::success(); +} + void COFFWriter::layoutSections() { - for (auto &S : Obj.Sections) { + for (auto &S : Obj.getMutableSections()) { if (S.Header.SizeOfRawData > 0) S.Header.PointerToRawData = FileSize; FileSize += S.Header.SizeOfRawData; // For executables, this is already @@ -58,7 +109,7 @@ void COFFWriter::layoutSections() { } size_t COFFWriter::finalizeStringTable() { - for (auto &S : Obj.Sections) + for (const auto &S : Obj.getSections()) if (S.Name.size() > COFF::NameSize) StrTabBuilder.add(S.Name); @@ -68,8 +119,9 @@ size_t COFFWriter::finalizeStringTable() { StrTabBuilder.finalize(); - for (auto &S : Obj.Sections) { + for (auto &S : Obj.getMutableSections()) { if (S.Name.size() > COFF::NameSize) { + memset(S.Header.Name, 0, sizeof(S.Header.Name)); snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d", (int)StrTabBuilder.getOffset(S.Name)); } else { @@ -89,15 +141,30 @@ size_t COFFWriter::finalizeStringTable() { template <class SymbolTy> std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() { - size_t SymTabSize = Obj.getSymbols().size() * sizeof(SymbolTy); - for (const auto &S : Obj.getSymbols()) - SymTabSize += S.AuxData.size(); - return std::make_pair(SymTabSize, sizeof(SymbolTy)); + size_t RawSymIndex = 0; + for (auto &S : Obj.getMutableSymbols()) { + // Symbols normally have NumberOfAuxSymbols set correctly all the time. + // For file symbols, we need to know the output file's symbol size to be + // able to calculate the number of slots it occupies. + if (!S.AuxFile.empty()) + S.Sym.NumberOfAuxSymbols = + alignTo(S.AuxFile.size(), sizeof(SymbolTy)) / sizeof(SymbolTy); + S.RawIndex = RawSymIndex; + RawSymIndex += 1 + S.Sym.NumberOfAuxSymbols; + } + return std::make_pair(RawSymIndex * sizeof(SymbolTy), sizeof(SymbolTy)); } Error COFFWriter::finalize(bool IsBigObj) { + size_t SymTabSize, SymbolSize; + std::tie(SymTabSize, SymbolSize) = IsBigObj + ? finalizeSymbolTable<coff_symbol32>() + : finalizeSymbolTable<coff_symbol16>(); + if (Error E = finalizeRelocTargets()) return E; + if (Error E = finalizeSymbolContents()) + return E; size_t SizeOfHeaders = 0; FileAlignment = 1; @@ -114,10 +181,10 @@ Error COFFWriter::finalize(bool IsBigObj) { SizeOfHeaders += PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); } - Obj.CoffFileHeader.NumberOfSections = Obj.Sections.size(); + Obj.CoffFileHeader.NumberOfSections = Obj.getSections().size(); SizeOfHeaders += IsBigObj ? sizeof(coff_bigobj_file_header) : sizeof(coff_file_header); - SizeOfHeaders += sizeof(coff_section) * Obj.Sections.size(); + SizeOfHeaders += sizeof(coff_section) * Obj.getSections().size(); SizeOfHeaders = alignTo(SizeOfHeaders, FileAlignment); Obj.CoffFileHeader.SizeOfOptionalHeader = @@ -132,8 +199,8 @@ Error COFFWriter::finalize(bool IsBigObj) { Obj.PeHeader.SizeOfHeaders = SizeOfHeaders; Obj.PeHeader.SizeOfInitializedData = SizeOfInitializedData; - if (!Obj.Sections.empty()) { - const Section &S = Obj.Sections.back(); + if (!Obj.getSections().empty()) { + const Section &S = Obj.getSections().back(); Obj.PeHeader.SizeOfImage = alignTo(S.Header.VirtualAddress + S.Header.VirtualSize, Obj.PeHeader.SectionAlignment); @@ -145,10 +212,6 @@ Error COFFWriter::finalize(bool IsBigObj) { } size_t StrTabSize = finalizeStringTable(); - size_t SymTabSize, SymbolSize; - std::tie(SymTabSize, SymbolSize) = IsBigObj - ? finalizeSymbolTable<coff_symbol32>() - : finalizeSymbolTable<coff_symbol16>(); size_t PointerToSymbolTable = FileSize; // StrTabSize <= 4 is the size of an empty string table, only consisting @@ -199,7 +262,7 @@ void COFFWriter::writeHeaders(bool IsBigObj) { BigObjHeader.unused4 = 0; // The value in Obj.CoffFileHeader.NumberOfSections is truncated, thus // get the original one instead. - BigObjHeader.NumberOfSections = Obj.Sections.size(); + BigObjHeader.NumberOfSections = Obj.getSections().size(); BigObjHeader.PointerToSymbolTable = Obj.CoffFileHeader.PointerToSymbolTable; BigObjHeader.NumberOfSymbols = Obj.CoffFileHeader.NumberOfSymbols; @@ -224,23 +287,24 @@ void COFFWriter::writeHeaders(bool IsBigObj) { Ptr += sizeof(DD); } } - for (const auto &S : Obj.Sections) { + for (const auto &S : Obj.getSections()) { memcpy(Ptr, &S.Header, sizeof(S.Header)); Ptr += sizeof(S.Header); } } void COFFWriter::writeSections() { - for (const auto &S : Obj.Sections) { + for (const auto &S : Obj.getSections()) { uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData; - std::copy(S.Contents.begin(), S.Contents.end(), Ptr); + ArrayRef<uint8_t> Contents = S.getContents(); + std::copy(Contents.begin(), Contents.end(), Ptr); // For executable sections, pad the remainder of the raw data size with // 0xcc, which is int3 on x86. if ((S.Header.Characteristics & IMAGE_SCN_CNT_CODE) && - S.Header.SizeOfRawData > S.Contents.size()) - memset(Ptr + S.Contents.size(), 0xcc, - S.Header.SizeOfRawData - S.Contents.size()); + S.Header.SizeOfRawData > Contents.size()) + memset(Ptr + Contents.size(), 0xcc, + S.Header.SizeOfRawData - Contents.size()); Ptr += S.Header.SizeOfRawData; for (const auto &R : S.Relocs) { @@ -257,8 +321,23 @@ template <class SymbolTy> void COFFWriter::writeSymbolStringTables() { copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr), S.Sym); Ptr += sizeof(SymbolTy); - std::copy(S.AuxData.begin(), S.AuxData.end(), Ptr); - Ptr += S.AuxData.size(); + if (!S.AuxFile.empty()) { + // For file symbols, just write the string into the aux symbol slots, + // assuming that the unwritten parts are initialized to zero in the memory + // mapped file. + std::copy(S.AuxFile.begin(), S.AuxFile.end(), Ptr); + Ptr += S.Sym.NumberOfAuxSymbols * sizeof(SymbolTy); + } else { + // For other auxillary symbols, write their opaque payload into one symbol + // table slot each. For big object files, the symbols are larger than the + // opaque auxillary symbol struct and we leave padding at the end of each + // entry. + for (const AuxSymbol &AuxSym : S.AuxData) { + ArrayRef<uint8_t> Ref = AuxSym.getRef(); + std::copy(Ref.begin(), Ref.end(), Ptr); + Ptr += sizeof(SymbolTy); + } + } } if (StrTabBuilder.getSize() > 4 || !Obj.IsPE) { // Always write a string table in object files, even an empty one. @@ -271,7 +350,8 @@ Error COFFWriter::write(bool IsBigObj) { if (Error E = finalize(IsBigObj)) return E; - Buf.allocate(FileSize); + if (Error E = Buf.allocate(FileSize)) + return E; writeHeaders(IsBigObj); writeSections(); @@ -296,15 +376,14 @@ Error COFFWriter::patchDebugDirectory() { const data_directory *Dir = &Obj.DataDirectories[DEBUG_DIRECTORY]; if (Dir->Size <= 0) return Error::success(); - for (const auto &S : Obj.Sections) { + for (const auto &S : Obj.getSections()) { if (Dir->RelativeVirtualAddress >= S.Header.VirtualAddress && Dir->RelativeVirtualAddress < S.Header.VirtualAddress + S.Header.SizeOfRawData) { if (Dir->RelativeVirtualAddress + Dir->Size > S.Header.VirtualAddress + S.Header.SizeOfRawData) - return make_error<StringError>( - "Debug directory extends past end of section", - object_error::parse_failed); + return createStringError(object_error::parse_failed, + "debug directory extends past end of section"); size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress; uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset; @@ -320,15 +399,15 @@ Error COFFWriter::patchDebugDirectory() { return Error::success(); } } - return make_error<StringError>("Debug directory not found", - object_error::parse_failed); + return createStringError(object_error::parse_failed, + "debug directory not found"); } Error COFFWriter::write() { - bool IsBigObj = Obj.Sections.size() > MaxNumberOfSections16; + bool IsBigObj = Obj.getSections().size() > MaxNumberOfSections16; if (IsBigObj && Obj.IsPE) - return make_error<StringError>("Too many sections for executable", - object_error::parse_failed); + return createStringError(object_error::parse_failed, + "too many sections for executable"); return write(IsBigObj); } diff --git a/contrib/llvm/tools/llvm-objcopy/COFF/Writer.h b/contrib/llvm/tools/llvm-objcopy/COFF/Writer.h index ab66e0cc1134..681a8d5e4a66 100644 --- a/contrib/llvm/tools/llvm-objcopy/COFF/Writer.h +++ b/contrib/llvm/tools/llvm-objcopy/COFF/Writer.h @@ -1,9 +1,8 @@ //===- Writer.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -31,10 +30,11 @@ class COFFWriter { size_t SizeOfInitializedData; StringTableBuilder StrTabBuilder; + template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable(); Error finalizeRelocTargets(); + Error finalizeSymbolContents(); void layoutSections(); size_t finalizeStringTable(); - template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable(); Error finalize(bool IsBigObj); diff --git a/contrib/llvm/tools/llvm-objcopy/CopyConfig.cpp b/contrib/llvm/tools/llvm-objcopy/CopyConfig.cpp index 3737f571ae61..8d6431b3044f 100644 --- a/contrib/llvm/tools/llvm-objcopy/CopyConfig.cpp +++ b/contrib/llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -1,27 +1,26 @@ //===- CopyConfig.cpp -----------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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-objcopy.h" -#include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Object/ELFTypes.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/JamCRC.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/StringSaver.h" #include <memory> -#include <string> namespace llvm { namespace objcopy { @@ -93,45 +92,47 @@ public: StripOptTable() : OptTable(StripInfoTable) {} }; -enum SectionFlag { - SecNone = 0, - SecAlloc = 1 << 0, - SecLoad = 1 << 1, - SecNoload = 1 << 2, - SecReadonly = 1 << 3, - SecDebug = 1 << 4, - SecCode = 1 << 5, - SecData = 1 << 6, - SecRom = 1 << 7, - SecMerge = 1 << 8, - SecStrings = 1 << 9, - SecContents = 1 << 10, - SecShare = 1 << 11, - LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ SecShare) -}; - } // namespace static SectionFlag parseSectionRenameFlag(StringRef SectionName) { return llvm::StringSwitch<SectionFlag>(SectionName) - .Case("alloc", SectionFlag::SecAlloc) - .Case("load", SectionFlag::SecLoad) - .Case("noload", SectionFlag::SecNoload) - .Case("readonly", SectionFlag::SecReadonly) - .Case("debug", SectionFlag::SecDebug) - .Case("code", SectionFlag::SecCode) - .Case("data", SectionFlag::SecData) - .Case("rom", SectionFlag::SecRom) - .Case("merge", SectionFlag::SecMerge) - .Case("strings", SectionFlag::SecStrings) - .Case("contents", SectionFlag::SecContents) - .Case("share", SectionFlag::SecShare) + .CaseLower("alloc", SectionFlag::SecAlloc) + .CaseLower("load", SectionFlag::SecLoad) + .CaseLower("noload", SectionFlag::SecNoload) + .CaseLower("readonly", SectionFlag::SecReadonly) + .CaseLower("debug", SectionFlag::SecDebug) + .CaseLower("code", SectionFlag::SecCode) + .CaseLower("data", SectionFlag::SecData) + .CaseLower("rom", SectionFlag::SecRom) + .CaseLower("merge", SectionFlag::SecMerge) + .CaseLower("strings", SectionFlag::SecStrings) + .CaseLower("contents", SectionFlag::SecContents) + .CaseLower("share", SectionFlag::SecShare) .Default(SectionFlag::SecNone); } -static SectionRename parseRenameSectionValue(StringRef FlagValue) { +static Expected<SectionFlag> +parseSectionFlagSet(ArrayRef<StringRef> SectionFlags) { + SectionFlag ParsedFlags = SectionFlag::SecNone; + for (StringRef Flag : SectionFlags) { + SectionFlag ParsedFlag = parseSectionRenameFlag(Flag); + if (ParsedFlag == SectionFlag::SecNone) + return createStringError( + errc::invalid_argument, + "unrecognized section flag '%s'. Flags supported for GNU " + "compatibility: alloc, load, noload, readonly, debug, code, data, " + "rom, share, contents, merge, strings", + Flag.str().c_str()); + ParsedFlags |= ParsedFlag; + } + + return ParsedFlags; +} + +static Expected<SectionRename> parseRenameSectionValue(StringRef FlagValue) { if (!FlagValue.contains('=')) - error("Bad format for --rename-section: missing '='"); + return createStringError(errc::invalid_argument, + "bad format for --rename-section: missing '='"); // Initial split: ".foo" = ".bar,f1,f2,..." auto Old2New = FlagValue.split('='); @@ -144,73 +145,210 @@ static SectionRename parseRenameSectionValue(StringRef FlagValue) { SR.NewName = NameAndFlags[0]; if (NameAndFlags.size() > 1) { - SectionFlag Flags = SectionFlag::SecNone; - for (size_t I = 1, Size = NameAndFlags.size(); I < Size; ++I) { - SectionFlag Flag = parseSectionRenameFlag(NameAndFlags[I]); - if (Flag == SectionFlag::SecNone) - error("Unrecognized section flag '" + NameAndFlags[I] + - "'. Flags supported for GNU compatibility: alloc, load, noload, " - "readonly, debug, code, data, rom, share, contents, merge, " - "strings."); - Flags |= Flag; - } - - SR.NewFlags = 0; - if (Flags & SectionFlag::SecAlloc) - *SR.NewFlags |= ELF::SHF_ALLOC; - if (!(Flags & SectionFlag::SecReadonly)) - *SR.NewFlags |= ELF::SHF_WRITE; - if (Flags & SectionFlag::SecCode) - *SR.NewFlags |= ELF::SHF_EXECINSTR; - if (Flags & SectionFlag::SecMerge) - *SR.NewFlags |= ELF::SHF_MERGE; - if (Flags & SectionFlag::SecStrings) - *SR.NewFlags |= ELF::SHF_STRINGS; + Expected<SectionFlag> ParsedFlagSet = + parseSectionFlagSet(makeArrayRef(NameAndFlags).drop_front()); + if (!ParsedFlagSet) + return ParsedFlagSet.takeError(); + SR.NewFlags = *ParsedFlagSet; } return SR; } +static Expected<SectionFlagsUpdate> +parseSetSectionFlagValue(StringRef FlagValue) { + if (!StringRef(FlagValue).contains('=')) + return createStringError(errc::invalid_argument, + "bad format for --set-section-flags: missing '='"); + + // Initial split: ".foo" = "f1,f2,..." + auto Section2Flags = StringRef(FlagValue).split('='); + SectionFlagsUpdate SFU; + SFU.Name = Section2Flags.first; + + // Flags split: "f1" "f2" ... + SmallVector<StringRef, 6> SectionFlags; + Section2Flags.second.split(SectionFlags, ','); + Expected<SectionFlag> ParsedFlagSet = parseSectionFlagSet(SectionFlags); + if (!ParsedFlagSet) + return ParsedFlagSet.takeError(); + SFU.NewFlags = *ParsedFlagSet; + + return SFU; +} + +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: + // 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()); + + 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("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; +} + static const StringMap<MachineInfo> ArchMap{ // Name, {EMachine, 64bit, LittleEndian} {"aarch64", {ELF::EM_AARCH64, true, true}}, {"arm", {ELF::EM_ARM, false, true}}, {"i386", {ELF::EM_386, false, true}}, {"i386:x86-64", {ELF::EM_X86_64, true, true}}, + {"mips", {ELF::EM_MIPS, false, false}}, {"powerpc:common64", {ELF::EM_PPC64, true, true}}, - {"sparc", {ELF::EM_SPARC, false, true}}, + {"riscv:rv32", {ELF::EM_RISCV, false, true}}, + {"riscv:rv64", {ELF::EM_RISCV, true, true}}, + {"sparc", {ELF::EM_SPARC, false, false}}, + {"sparcel", {ELF::EM_SPARC, false, true}}, {"x86-64", {ELF::EM_X86_64, true, true}}, }; -static const MachineInfo &getMachineInfo(StringRef Arch) { +static Expected<const MachineInfo &> getMachineInfo(StringRef Arch) { auto Iter = ArchMap.find(Arch); if (Iter == std::end(ArchMap)) - error("Invalid architecture: '" + Arch + "'"); + return createStringError(errc::invalid_argument, + "invalid architecture: '%s'", Arch.str().c_str()); return Iter->getValue(); } -static const StringMap<MachineInfo> OutputFormatMap{ +struct TargetInfo { + FileFormat Format; + MachineInfo Machine; +}; + +// FIXME: consolidate with the bfd parsing used by lld. +static const StringMap<MachineInfo> TargetMap{ // Name, {EMachine, 64bit, LittleEndian} + // x86 {"elf32-i386", {ELF::EM_386, false, true}}, - {"elf32-powerpcle", {ELF::EM_PPC, false, true}}, {"elf32-x86-64", {ELF::EM_X86_64, false, true}}, - {"elf64-powerpcle", {ELF::EM_PPC64, true, true}}, {"elf64-x86-64", {ELF::EM_X86_64, true, true}}, + // Intel MCU + {"elf32-iamcu", {ELF::EM_IAMCU, false, true}}, + // ARM + {"elf32-littlearm", {ELF::EM_ARM, false, true}}, + // ARM AArch64 + {"elf64-aarch64", {ELF::EM_AARCH64, true, true}}, + {"elf64-littleaarch64", {ELF::EM_AARCH64, true, true}}, + // RISC-V + {"elf32-littleriscv", {ELF::EM_RISCV, false, true}}, + {"elf64-littleriscv", {ELF::EM_RISCV, true, true}}, + // PowerPC + {"elf32-powerpc", {ELF::EM_PPC, false, false}}, + {"elf32-powerpcle", {ELF::EM_PPC, false, true}}, + {"elf64-powerpc", {ELF::EM_PPC64, true, false}}, + {"elf64-powerpcle", {ELF::EM_PPC64, true, true}}, + // MIPS + {"elf32-bigmips", {ELF::EM_MIPS, false, false}}, + {"elf32-ntradbigmips", {ELF::EM_MIPS, false, false}}, + {"elf32-ntradlittlemips", {ELF::EM_MIPS, false, true}}, + {"elf32-tradbigmips", {ELF::EM_MIPS, false, false}}, + {"elf32-tradlittlemips", {ELF::EM_MIPS, false, true}}, + {"elf64-tradbigmips", {ELF::EM_MIPS, true, false}}, + {"elf64-tradlittlemips", {ELF::EM_MIPS, true, true}}, + // SPARC + {"elf32-sparc", {ELF::EM_SPARC, false, false}}, + {"elf32-sparcel", {ELF::EM_SPARC, false, true}}, }; -static const MachineInfo &getOutputFormatMachineInfo(StringRef Format) { - auto Iter = OutputFormatMap.find(Format); - if (Iter == std::end(OutputFormatMap)) - error("Invalid output format: '" + Format + "'"); - return Iter->getValue(); +static Expected<TargetInfo> +getOutputTargetInfoByTargetName(StringRef TargetName) { + StringRef OriginalTargetName = TargetName; + bool IsFreeBSD = TargetName.consume_back("-freebsd"); + auto Iter = TargetMap.find(TargetName); + if (Iter == std::end(TargetMap)) + return createStringError(errc::invalid_argument, + "invalid output format: '%s'", + OriginalTargetName.str().c_str()); + MachineInfo MI = Iter->getValue(); + if (IsFreeBSD) + MI.OSABI = ELF::ELFOSABI_FREEBSD; + + FileFormat Format; + if (TargetName.startswith("elf")) + Format = FileFormat::ELF; + else + // This should never happen because `TargetName` is valid (it certainly + // exists in the TargetMap). + llvm_unreachable("unknown target prefix"); + + return {TargetInfo{Format, MI}}; } -static void addGlobalSymbolsFromFile(std::vector<std::string> &Symbols, - StringRef Filename) { +static Error addSymbolsFromFile(std::vector<NameOrRegex> &Symbols, + BumpPtrAllocator &Alloc, StringRef Filename, + bool UseRegex) { + StringSaver Saver(Alloc); SmallVector<StringRef, 16> Lines; auto BufOrErr = MemoryBuffer::getFile(Filename); if (!BufOrErr) - reportError(Filename, BufOrErr.getError()); + return createFileError(Filename, BufOrErr.getError()); BufOrErr.get()->getBuffer().split(Lines, '\n'); for (StringRef Line : Lines) { @@ -218,14 +356,62 @@ static void addGlobalSymbolsFromFile(std::vector<std::string> &Symbols, // it's not empty. auto TrimmedLine = Line.split('#').first.trim(); if (!TrimmedLine.empty()) - Symbols.push_back(TrimmedLine.str()); + Symbols.emplace_back(Saver.save(TrimmedLine), UseRegex); } + + return Error::success(); +} + +NameOrRegex::NameOrRegex(StringRef Pattern, bool IsRegex) { + if (!IsRegex) { + Name = Pattern; + return; + } + + SmallVector<char, 32> Data; + R = std::make_shared<Regex>( + ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data)); +} + +static Error addSymbolsToRenameFromFile(StringMap<StringRef> &SymbolsToRename, + BumpPtrAllocator &Alloc, + StringRef Filename) { + StringSaver Saver(Alloc); + SmallVector<StringRef, 16> Lines; + auto BufOrErr = MemoryBuffer::getFile(Filename); + if (!BufOrErr) + return createFileError(Filename, BufOrErr.getError()); + + BufOrErr.get()->getBuffer().split(Lines, '\n'); + size_t NumLines = Lines.size(); + for (size_t LineNo = 0; LineNo < NumLines; ++LineNo) { + StringRef TrimmedLine = Lines[LineNo].split('#').first.trim(); + if (TrimmedLine.empty()) + continue; + + std::pair<StringRef, StringRef> Pair = Saver.save(TrimmedLine).split(' '); + StringRef NewName = Pair.second.trim(); + if (NewName.empty()) + return createStringError(errc::invalid_argument, + "%s:%zu: missing new symbol name", + Filename.str().c_str(), LineNo + 1); + SymbolsToRename.insert({Pair.first, NewName}); + } + return Error::success(); +} + +template <class T> static ErrorOr<T> getAsInteger(StringRef Val) { + T Result; + if (Val.getAsInteger(0, Result)) + return errc::invalid_argument; + return Result; } // ParseObjcopyOptions returns the config and sets the input arguments. If a // help flag is set then ParseObjcopyOptions will print the help messege and // exit. -DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { +Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { + DriverConfig DC; ObjcopyOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = @@ -250,16 +436,18 @@ DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { SmallVector<const char *, 2> Positional; for (auto Arg : InputArgs.filtered(OBJCOPY_UNKNOWN)) - error("unknown argument '" + Arg->getAsString(InputArgs) + "'"); + return createStringError(errc::invalid_argument, "unknown argument '%s'", + Arg->getAsString(InputArgs).c_str()); for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT)) Positional.push_back(Arg->getValue()); if (Positional.empty()) - error("No input file specified"); + return createStringError(errc::invalid_argument, "no input file specified"); if (Positional.size() > 2) - error("Too many positional arguments"); + return createStringError(errc::invalid_argument, + "too many positional arguments"); CopyConfig Config; Config.InputFilename = Positional[0]; @@ -267,23 +455,50 @@ DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { if (InputArgs.hasArg(OBJCOPY_target) && (InputArgs.hasArg(OBJCOPY_input_target) || InputArgs.hasArg(OBJCOPY_output_target))) - error("--target cannot be used with --input-target or --output-target"); + return createStringError( + errc::invalid_argument, + "--target cannot be used with --input-target or --output-target"); + bool UseRegex = InputArgs.hasArg(OBJCOPY_regex); + StringRef InputFormat, OutputFormat; if (InputArgs.hasArg(OBJCOPY_target)) { - Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_target); - Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target); + InputFormat = InputArgs.getLastArgValue(OBJCOPY_target); + OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target); } else { - Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target); - Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target); + InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target); + OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target); } - if (Config.InputFormat == "binary") { + + // FIXME: Currently, we ignore the target for non-binary/ihex formats + // explicitly specified by -I option (e.g. -Ielf32-x86-64) and guess the + // format by llvm::object::createBinary regardless of the option value. + Config.InputFormat = StringSwitch<FileFormat>(InputFormat) + .Case("binary", FileFormat::Binary) + .Case("ihex", FileFormat::IHex) + .Default(FileFormat::Unspecified); + if (Config.InputFormat == FileFormat::Binary) { auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture); if (BinaryArch.empty()) - error("Specified binary input without specifiying an architecture"); - Config.BinaryArch = getMachineInfo(BinaryArch); + return createStringError( + errc::invalid_argument, + "specified binary input without specifiying an architecture"); + Expected<const MachineInfo &> MI = getMachineInfo(BinaryArch); + if (!MI) + return MI.takeError(); + Config.BinaryArch = *MI; + } + + Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat) + .Case("binary", FileFormat::Binary) + .Case("ihex", FileFormat::IHex) + .Default(FileFormat::Unspecified); + if (Config.OutputFormat == FileFormat::Unspecified && !OutputFormat.empty()) { + Expected<TargetInfo> Target = getOutputTargetInfoByTargetName(OutputFormat); + if (!Target) + return Target.takeError(); + Config.OutputFormat = Target->Format; + Config.OutputArch = Target->Machine; } - if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") - Config.OutputArch = getOutputFormatMachineInfo(Config.OutputFormat); if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections, OBJCOPY_compress_debug_sections_eq)) { @@ -297,14 +512,36 @@ DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { .Case("zlib", DebugCompressionType::Z) .Default(DebugCompressionType::None); if (Config.CompressionType == DebugCompressionType::None) - error("Invalid or unsupported --compress-debug-sections format: " + - InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)); - if (!zlib::isAvailable()) - error("LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress."); + return createStringError( + errc::invalid_argument, + "invalid or unsupported --compress-debug-sections format: %s", + InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq) + .str() + .c_str()); } + if (!zlib::isAvailable()) + return createStringError( + errc::invalid_argument, + "LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress"); } Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); + // The gnu_debuglink's target is expected to not change or else its CRC would + // become invalidated and get rejected. We can avoid recalculating the + // checksum for every target file inside an archive by precomputing the CRC + // here. This prevents a significant amount of I/O. + if (!Config.AddGnuDebugLink.empty()) { + auto DebugOrErr = MemoryBuffer::getFile(Config.AddGnuDebugLink); + if (!DebugOrErr) + return createFileError(Config.AddGnuDebugLink, DebugOrErr.getError()); + auto Debug = std::move(*DebugOrErr); + JamCRC CRC; + CRC.update( + ArrayRef<char>(Debug->getBuffer().data(), Debug->getBuffer().size())); + // The CRC32 value needs to be complemented because the JamCRC doesn't + // finalize the CRC32 value. + Config.GnuDebugLinkCRC32 = ~CRC.getCRC(); + } Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); if (InputArgs.hasArg(OBJCOPY_build_id_link_input)) Config.BuildIdLinkInput = @@ -314,27 +551,72 @@ DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { InputArgs.getLastArgValue(OBJCOPY_build_id_link_output); Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols); + Config.AllocSectionsPrefix = + InputArgs.getLastArgValue(OBJCOPY_prefix_alloc_sections); + if (auto Arg = InputArgs.getLastArg(OBJCOPY_extract_partition)) + Config.ExtractPartition = Arg->getValue(); for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) { if (!StringRef(Arg->getValue()).contains('=')) - error("Bad format for --redefine-sym"); + return createStringError(errc::invalid_argument, + "bad format for --redefine-sym"); auto Old2New = StringRef(Arg->getValue()).split('='); if (!Config.SymbolsToRename.insert(Old2New).second) - error("Multiple redefinition of symbol " + Old2New.first); + return createStringError(errc::invalid_argument, + "multiple redefinition of symbol '%s'", + Old2New.first.str().c_str()); } + for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbols)) + if (Error E = addSymbolsToRenameFromFile(Config.SymbolsToRename, DC.Alloc, + Arg->getValue())) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) { - SectionRename SR = parseRenameSectionValue(StringRef(Arg->getValue())); - if (!Config.SectionsToRename.try_emplace(SR.OriginalName, SR).second) - error("Multiple renames of section " + SR.OriginalName); + Expected<SectionRename> SR = + parseRenameSectionValue(StringRef(Arg->getValue())); + if (!SR) + return SR.takeError(); + if (!Config.SectionsToRename.try_emplace(SR->OriginalName, *SR).second) + return createStringError(errc::invalid_argument, + "multiple renames of section '%s'", + SR->OriginalName.str().c_str()); + } + for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_flags)) { + Expected<SectionFlagsUpdate> SFU = + parseSetSectionFlagValue(Arg->getValue()); + if (!SFU) + return SFU.takeError(); + if (!Config.SetSectionFlags.try_emplace(SFU->Name, *SFU).second) + return createStringError( + errc::invalid_argument, + "--set-section-flags set multiple times for section '%s'", + SFU->Name.str().c_str()); + } + // Prohibit combinations of --set-section-flags when the section name is used + // by --rename-section, either as a source or a destination. + for (const auto &E : Config.SectionsToRename) { + const SectionRename &SR = E.second; + if (Config.SetSectionFlags.count(SR.OriginalName)) + return createStringError( + errc::invalid_argument, + "--set-section-flags=%s conflicts with --rename-section=%s=%s", + SR.OriginalName.str().c_str(), SR.OriginalName.str().c_str(), + SR.NewName.str().c_str()); + if (Config.SetSectionFlags.count(SR.NewName)) + return createStringError( + errc::invalid_argument, + "--set-section-flags=%s conflicts with --rename-section=%s=%s", + SR.NewName.str().c_str(), SR.OriginalName.str().c_str(), + SR.NewName.str().c_str()); } for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) - Config.ToRemove.push_back(Arg->getValue()); + Config.ToRemove.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section)) - Config.KeepSection.push_back(Arg->getValue()); + Config.KeepSection.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_only_section)) - Config.OnlySection.push_back(Arg->getValue()); + Config.OnlySection.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) Config.AddSection.push_back(Arg->getValue()); for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section)) @@ -347,27 +629,71 @@ DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc); Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded); Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo); + Config.ExtractMainPartition = + InputArgs.hasArg(OBJCOPY_extract_main_partition); Config.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden); Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken); - Config.DiscardAll = InputArgs.hasArg(OBJCOPY_discard_all); + if (InputArgs.hasArg(OBJCOPY_discard_all, OBJCOPY_discard_locals)) + Config.DiscardMode = + InputArgs.hasFlag(OBJCOPY_discard_all, OBJCOPY_discard_locals) + ? DiscardType::All + : DiscardType::Locals; Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); Config.DecompressDebugSections = InputArgs.hasArg(OBJCOPY_decompress_debug_sections); + if (Config.DiscardMode == DiscardType::All) + Config.StripDebug = true; for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) - Config.SymbolsToLocalize.push_back(Arg->getValue()); + Config.SymbolsToLocalize.emplace_back(Arg->getValue(), UseRegex); + for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbols)) + if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc, + Arg->getValue(), UseRegex)) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) - Config.SymbolsToKeepGlobal.push_back(Arg->getValue()); + Config.SymbolsToKeepGlobal.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols)) - addGlobalSymbolsFromFile(Config.SymbolsToKeepGlobal, Arg->getValue()); + if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc, + Arg->getValue(), UseRegex)) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol)) - Config.SymbolsToGlobalize.push_back(Arg->getValue()); + Config.SymbolsToGlobalize.emplace_back(Arg->getValue(), UseRegex); + for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbols)) + if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc, + Arg->getValue(), UseRegex)) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol)) - Config.SymbolsToWeaken.push_back(Arg->getValue()); + Config.SymbolsToWeaken.emplace_back(Arg->getValue(), UseRegex); + for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbols)) + if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc, + Arg->getValue(), UseRegex)) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol)) - Config.SymbolsToRemove.push_back(Arg->getValue()); + Config.SymbolsToRemove.emplace_back(Arg->getValue(), UseRegex); + for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbols)) + if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc, + Arg->getValue(), UseRegex)) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol)) + Config.UnneededSymbolsToRemove.emplace_back(Arg->getValue(), UseRegex); + for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols)) + if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc, + Arg->getValue(), UseRegex)) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) - Config.SymbolsToKeep.push_back(Arg->getValue()); + Config.SymbolsToKeep.emplace_back(Arg->getValue(), UseRegex); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbols)) + if (Error E = addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, + Arg->getValue(), UseRegex)) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) { + Expected<NewSymbolInfo> NSI = parseNewSymbolInfo(Arg->getValue()); + if (!NSI) + return NSI.takeError(); + Config.SymbolsToAdd.push_back(*NSI); + } + + Config.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links); Config.DeterministicArchives = InputArgs.hasFlag( OBJCOPY_enable_deterministic_archives, @@ -375,24 +701,60 @@ DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates); + if (Config.PreserveDates && + (Config.OutputFilename == "-" || Config.InputFilename == "-")) + return createStringError(errc::invalid_argument, + "--preserve-dates requires a file"); + + for (auto Arg : InputArgs) + if (Arg->getOption().matches(OBJCOPY_set_start)) { + auto EAddr = getAsInteger<uint64_t>(Arg->getValue()); + if (!EAddr) + return createStringError( + EAddr.getError(), "bad entry point address: '%s'", Arg->getValue()); + + Config.EntryExpr = [EAddr](uint64_t) { return *EAddr; }; + } else if (Arg->getOption().matches(OBJCOPY_change_start)) { + auto EIncr = getAsInteger<int64_t>(Arg->getValue()); + if (!EIncr) + return createStringError(EIncr.getError(), + "bad entry point increment: '%s'", + Arg->getValue()); + auto Expr = Config.EntryExpr ? std::move(Config.EntryExpr) + : [](uint64_t A) { return A; }; + Config.EntryExpr = [Expr, EIncr](uint64_t EAddr) { + return Expr(EAddr) + *EIncr; + }; + } + if (Config.DecompressDebugSections && Config.CompressionType != DebugCompressionType::None) { - error("Cannot specify --compress-debug-sections at the same time as " - "--decompress-debug-sections at the same time"); + return createStringError( + errc::invalid_argument, + "cannot specify both --compress-debug-sections and " + "--decompress-debug-sections"); } if (Config.DecompressDebugSections && !zlib::isAvailable()) - error("LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress."); + return createStringError( + errc::invalid_argument, + "LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress"); + + if (Config.ExtractPartition && Config.ExtractMainPartition) + return createStringError(errc::invalid_argument, + "cannot specify --extract-partition together with " + "--extract-main-partition"); - DriverConfig DC; DC.CopyConfigs.push_back(std::move(Config)); - return DC; + return std::move(DC); } // ParseStripOptions returns the config and sets the input arguments. If a // help flag is set then ParseStripOptions will print the help messege and // exit. -DriverConfig parseStripOptions(ArrayRef<const char *> ArgsArr) { +Expected<DriverConfig> +parseStripOptions(ArrayRef<const char *> ArgsArr, + std::function<Error(Error)> ErrorCallback) { StripOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = @@ -414,44 +776,65 @@ DriverConfig parseStripOptions(ArrayRef<const char *> ArgsArr) { exit(0); } - SmallVector<const char *, 2> Positional; + SmallVector<StringRef, 2> Positional; for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN)) - error("unknown argument '" + Arg->getAsString(InputArgs) + "'"); + return createStringError(errc::invalid_argument, "unknown argument '%s'", + Arg->getAsString(InputArgs).c_str()); for (auto Arg : InputArgs.filtered(STRIP_INPUT)) Positional.push_back(Arg->getValue()); if (Positional.empty()) - error("No input file specified"); + return createStringError(errc::invalid_argument, "no input file specified"); if (Positional.size() > 1 && InputArgs.hasArg(STRIP_output)) - error("Multiple input files cannot be used in combination with -o"); + return createStringError( + errc::invalid_argument, + "multiple input files cannot be used in combination with -o"); CopyConfig Config; + bool UseRegexp = InputArgs.hasArg(STRIP_regex); + Config.AllowBrokenLinks = InputArgs.hasArg(STRIP_allow_broken_links); Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); - Config.DiscardAll = InputArgs.hasArg(STRIP_discard_all); + if (InputArgs.hasArg(STRIP_discard_all, STRIP_discard_locals)) + Config.DiscardMode = + InputArgs.hasFlag(STRIP_discard_all, STRIP_discard_locals) + ? DiscardType::All + : DiscardType::Locals; Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded); - Config.StripAll = InputArgs.hasArg(STRIP_strip_all); + if (auto Arg = InputArgs.getLastArg(STRIP_strip_all, STRIP_no_strip_all)) + Config.StripAll = Arg->getOption().getID() == STRIP_strip_all; Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu); - - if (!Config.StripDebug && !Config.StripUnneeded && !Config.DiscardAll && - !Config.StripAllGNU) - Config.StripAll = true; + Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug); + Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); for (auto Arg : InputArgs.filtered(STRIP_keep_section)) - Config.KeepSection.push_back(Arg->getValue()); + Config.KeepSection.emplace_back(Arg->getValue(), UseRegexp); for (auto Arg : InputArgs.filtered(STRIP_remove_section)) - Config.ToRemove.push_back(Arg->getValue()); + Config.ToRemove.emplace_back(Arg->getValue(), UseRegexp); + + for (auto Arg : InputArgs.filtered(STRIP_strip_symbol)) + Config.SymbolsToRemove.emplace_back(Arg->getValue(), UseRegexp); for (auto Arg : InputArgs.filtered(STRIP_keep_symbol)) - Config.SymbolsToKeep.push_back(Arg->getValue()); + Config.SymbolsToKeep.emplace_back(Arg->getValue(), UseRegexp); + + if (!InputArgs.hasArg(STRIP_no_strip_all) && !Config.StripDebug && + !Config.StripUnneeded && Config.DiscardMode == DiscardType::None && + !Config.StripAllGNU && Config.SymbolsToRemove.empty()) + Config.StripAll = true; + + if (Config.DiscardMode == DiscardType::All) + Config.StripDebug = true; Config.DeterministicArchives = InputArgs.hasFlag(STRIP_enable_deterministic_archives, STRIP_disable_deterministic_archives, /*default=*/true); Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates); + Config.InputFormat = FileFormat::Unspecified; + Config.OutputFormat = FileFormat::Unspecified; DriverConfig DC; if (Positional.size() == 1) { @@ -460,14 +843,30 @@ DriverConfig parseStripOptions(ArrayRef<const char *> ArgsArr) { InputArgs.getLastArgValue(STRIP_output, Positional[0]); DC.CopyConfigs.push_back(std::move(Config)); } else { - for (const char *Filename : Positional) { + StringMap<unsigned> InputFiles; + for (StringRef Filename : Positional) { + if (InputFiles[Filename]++ == 1) { + if (Filename == "-") + return createStringError( + errc::invalid_argument, + "cannot specify '-' as an input file more than once"); + if (Error E = ErrorCallback(createStringError( + errc::invalid_argument, "'%s' was already specified", + Filename.str().c_str()))) + return std::move(E); + } Config.InputFilename = Filename; Config.OutputFilename = Filename; DC.CopyConfigs.push_back(Config); } } - return DC; + if (Config.PreserveDates && (is_contained(Positional, "-") || + InputArgs.getLastArgValue(STRIP_output) == "-")) + return createStringError(errc::invalid_argument, + "--preserve-dates requires a file"); + + return std::move(DC); } } // namespace objcopy diff --git a/contrib/llvm/tools/llvm-objcopy/CopyConfig.h b/contrib/llvm/tools/llvm-objcopy/CopyConfig.h index 71a2423ae1c8..aff3631a487c 100644 --- a/contrib/llvm/tools/llvm-objcopy/CopyConfig.h +++ b/contrib/llvm/tools/llvm-objcopy/CopyConfig.h @@ -1,9 +1,8 @@ //===- CopyConfig.h -------------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -11,40 +10,110 @@ #define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.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/Regex.h" // Necessary for llvm::DebugCompressionType::None #include "llvm/Target/TargetOptions.h" -#include <string> #include <vector> namespace llvm { namespace objcopy { +enum class FileFormat { + Unspecified, + ELF, + Binary, + IHex, +}; + // This type keeps track of the machine info for various architectures. This // lets us map architecture names to ELF types and the e_machine value of the // ELF file. struct MachineInfo { + MachineInfo(uint16_t EM, uint8_t ABI, bool Is64, bool IsLittle) + : EMachine(EM), OSABI(ABI), Is64Bit(Is64), IsLittleEndian(IsLittle) {} + // Alternative constructor that defaults to NONE for OSABI. + MachineInfo(uint16_t EM, bool Is64, bool IsLittle) + : MachineInfo(EM, ELF::ELFOSABI_NONE, Is64, IsLittle) {} + // Default constructor for unset fields. + MachineInfo() : MachineInfo(0, 0, false, false) {} uint16_t EMachine; + uint8_t OSABI; bool Is64Bit; bool IsLittleEndian; }; +// Flags set by --set-section-flags or --rename-section. Interpretation of these +// is format-specific and not all flags are meaningful for all object file +// formats. This is a bitmask; many section flags may be set. +enum SectionFlag { + SecNone = 0, + SecAlloc = 1 << 0, + SecLoad = 1 << 1, + SecNoload = 1 << 2, + SecReadonly = 1 << 3, + SecDebug = 1 << 4, + SecCode = 1 << 5, + SecData = 1 << 6, + SecRom = 1 << 7, + SecMerge = 1 << 8, + SecStrings = 1 << 9, + SecContents = 1 << 10, + SecShare = 1 << 11, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ SecShare) +}; + struct SectionRename { StringRef OriginalName; StringRef NewName; - Optional<uint64_t> NewFlags; + Optional<SectionFlag> NewFlags; +}; + +struct SectionFlagsUpdate { + StringRef Name; + SectionFlag NewFlags; +}; + +enum class DiscardType { + None, // Default + All, // --discard-all (-x) + Locals, // --discard-locals (-X) +}; + +class NameOrRegex { + StringRef Name; + // Regex is shared between multiple CopyConfig instances. + std::shared_ptr<Regex> R; + +public: + NameOrRegex(StringRef Pattern, bool IsRegex); + bool operator==(StringRef S) const { return R ? R->match(S) : Name == S; } + bool operator!=(StringRef S) const { return !operator==(S); } +}; + +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; }; // Configuration for copying/stripping a single file. struct CopyConfig { // Main input/output options StringRef InputFilename; - StringRef InputFormat; + FileFormat InputFormat; StringRef OutputFilename; - StringRef OutputFormat; + FileFormat OutputFormat; // Only applicable for --input-format=binary MachineInfo BinaryArch; @@ -53,33 +122,48 @@ struct CopyConfig { // Advanced options 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; // Repeated options std::vector<StringRef> AddSection; std::vector<StringRef> DumpSection; - std::vector<StringRef> KeepSection; - std::vector<StringRef> OnlySection; - std::vector<StringRef> SymbolsToGlobalize; - std::vector<StringRef> SymbolsToKeep; - std::vector<StringRef> SymbolsToLocalize; - std::vector<StringRef> SymbolsToRemove; - std::vector<StringRef> SymbolsToWeaken; - std::vector<StringRef> ToRemove; - std::vector<std::string> SymbolsToKeepGlobal; + std::vector<NewSymbolInfo> SymbolsToAdd; + std::vector<NameOrRegex> KeepSection; + std::vector<NameOrRegex> OnlySection; + std::vector<NameOrRegex> SymbolsToGlobalize; + std::vector<NameOrRegex> SymbolsToKeep; + std::vector<NameOrRegex> SymbolsToLocalize; + std::vector<NameOrRegex> SymbolsToRemove; + std::vector<NameOrRegex> UnneededSymbolsToRemove; + std::vector<NameOrRegex> SymbolsToWeaken; + std::vector<NameOrRegex> ToRemove; + std::vector<NameOrRegex> SymbolsToKeepGlobal; // Map options StringMap<SectionRename> SectionsToRename; + StringMap<SectionFlagsUpdate> SetSectionFlags; StringMap<StringRef> SymbolsToRename; + // ELF entry point address expression. The input parameter is an entry point + // address in the input ELF file. The entry address in the output file is + // calculated with EntryExpr(input_address), when either --set-start or + // --change-start is used. + std::function<uint64_t(uint64_t)> EntryExpr; + // Boolean options + bool AllowBrokenLinks = false; bool DeterministicArchives = true; - bool DiscardAll = false; bool ExtractDWO = false; + bool ExtractMainPartition = false; bool KeepFileSymbols = false; bool LocalizeHidden = false; bool OnlyKeepDebug = false; @@ -101,17 +185,21 @@ struct CopyConfig { // 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. -DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr); +Expected<DriverConfig> parseObjcopyOptions(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. -DriverConfig parseStripOptions(ArrayRef<const char *> ArgsArr); +// 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, + std::function<Error(Error)> ErrorCallback); } // namespace objcopy } // namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp index f5ab8e708267..b366c6e55987 100644 --- a/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -1,9 +1,8 @@ //===- ELFObjcopy.cpp -----------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -14,6 +13,7 @@ #include "llvm-objcopy.h" #include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" @@ -71,6 +71,44 @@ static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { return !isDWOSection(Sec); } +uint64_t getNewShfFlags(SectionFlag AllFlags) { + uint64_t NewFlags = 0; + if (AllFlags & SectionFlag::SecAlloc) + NewFlags |= ELF::SHF_ALLOC; + if (!(AllFlags & SectionFlag::SecReadonly)) + NewFlags |= ELF::SHF_WRITE; + if (AllFlags & SectionFlag::SecCode) + NewFlags |= ELF::SHF_EXECINSTR; + if (AllFlags & SectionFlag::SecMerge) + NewFlags |= ELF::SHF_MERGE; + if (AllFlags & SectionFlag::SecStrings) + NewFlags |= ELF::SHF_STRINGS; + return NewFlags; +} + +static uint64_t getSectionFlagsPreserveMask(uint64_t OldFlags, + uint64_t NewFlags) { + // Preserve some flags which should not be dropped when setting flags. + // Also, preserve anything OS/processor dependant. + const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE | + ELF::SHF_GROUP | ELF::SHF_LINK_ORDER | + ELF::SHF_MASKOS | ELF::SHF_MASKPROC | + ELF::SHF_TLS | ELF::SHF_INFO_LINK; + return (OldFlags & PreserveMask) | (NewFlags & ~PreserveMask); +} + +static void setSectionFlagsAndType(SectionBase &Sec, SectionFlag Flags) { + Sec.Flags = getSectionFlagsPreserveMask(Sec.Flags, getNewShfFlags(Flags)); + + // In GNU objcopy, certain flags promote SHT_NOBITS to SHT_PROGBITS. This rule + // may promote more non-ALLOC sections than GNU objcopy, but it is fine as + // non-ALLOC SHT_NOBITS sections do not make much sense. + if (Sec.Type == SHT_NOBITS && + (!(Sec.Flags & ELF::SHF_ALLOC) || + Flags & (SectionFlag::SecContents | SectionFlag::SecLoad))) + Sec.Type = SHT_PROGBITS; +} + static ElfType getOutputElfType(const Binary &Bin) { // Infer output ELF type from the input ELF object if (isa<ELFObjectFile<ELF32LE>>(Bin)) @@ -92,12 +130,9 @@ static ElfType getOutputElfType(const MachineInfo &MI) { return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; } -static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, - ElfType OutputElfType) { - if (Config.OutputFormat == "binary") { - return llvm::make_unique<BinaryWriter>(Obj, Buf); - } +static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config, + Object &Obj, Buffer &Buf, + ElfType OutputElfType) { // Depending on the initial ELFT and OutputFormat we need a different Writer. switch (OutputElfType) { case ELFT_ELF32LE: @@ -116,10 +151,27 @@ static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, llvm_unreachable("Invalid output format"); } +static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, + Object &Obj, Buffer &Buf, + ElfType OutputElfType) { + switch (Config.OutputFormat) { + case FileFormat::Binary: + return llvm::make_unique<BinaryWriter>(Obj, Buf); + case FileFormat::IHex: + return llvm::make_unique<IHexWriter>(Obj, Buf); + default: + return createELFWriter(Config, Obj, Buf, OutputElfType); + } +} + template <class ELFT> static Expected<ArrayRef<uint8_t>> -findBuildID(const object::ELFFile<ELFT> &In) { - for (const auto &Phdr : unwrapOrError(In.program_headers())) { +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(); @@ -127,58 +179,106 @@ findBuildID(const object::ELFFile<ELFT> &In) { if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU) return Note.getDesc(); if (Err) - return std::move(Err); + return createFileError(Config.InputFilename, std::move(Err)); } - return createStringError(llvm::errc::invalid_argument, - "Could not find build ID."); + + return createFileError( + Config.InputFilename, + createStringError(llvm::errc::invalid_argument, + "could not find build ID")); } static Expected<ArrayRef<uint8_t>> -findBuildID(const object::ELFObjectFileBase &In) { +findBuildID(const CopyConfig &Config, const object::ELFObjectFileBase &In) { if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(&In)) - return findBuildID(*O->getELFFile()); + return findBuildID(Config, *O->getELFFile()); else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(&In)) - return findBuildID(*O->getELFFile()); + return findBuildID(Config, *O->getELFFile()); else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(&In)) - return findBuildID(*O->getELFFile()); + return findBuildID(Config, *O->getELFFile()); else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(&In)) - return findBuildID(*O->getELFFile()); + return findBuildID(Config, *O->getELFFile()); llvm_unreachable("Bad file format"); } -static void linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink, - StringRef Suffix, ArrayRef<uint8_t> BuildIdBytes) { +template <class... Ts> +static Error makeStringError(std::error_code EC, const Twine &Msg, Ts &&... Args) { + std::string FullMsg = (EC.message() + ": " + Msg).str(); + 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)) - error("cannot create build ID link directory " + Path + ": " + - EC.message()); + 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; - if (auto EC = sys::fs::create_hard_link(ToLink, Path)) { - // Hard linking failed, try to remove the file first if it exists. - if (sys::fs::exists(Path)) - sys::fs::remove(Path); - EC = sys::fs::create_hard_link(ToLink, Path); - if (EC) - error("cannot link " + ToLink + " to " + Path + ": " + EC.message()); + 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 void splitDWOToFile(const CopyConfig &Config, const Reader &Reader, - StringRef File, ElfType OutputElfType) { +static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader, + StringRef File, ElfType OutputElfType) { auto DWOFile = Reader.create(); - DWOFile->removeSections( - [&](const SectionBase &Sec) { return onlyKeepDWOPred(*DWOFile, Sec); }); - if (Config.OutputArch) + 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); auto Writer = createWriter(Config, *DWOFile, FB, OutputElfType); - Writer->finalize(); - Writer->write(); + if (Error E = Writer->finalize()) + return E; + return Writer->write(); } static Error dumpSectionToFile(StringRef SecName, StringRef Filename, @@ -186,9 +286,9 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename, for (auto &Sec : Obj.sections()) { if (Sec.Name == SecName) { if (Sec.OriginalData.empty()) - return make_error<StringError>("Can't dump section \"" + SecName + - "\": it has no contents", - object_error::parse_failed); + return createStringError(object_error::parse_failed, + "cannot dump section '%s': it has no contents", + SecName.str().c_str()); Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = FileOutputBuffer::create(Filename, Sec.OriginalData.size()); if (!BufferOrErr) @@ -201,149 +301,143 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename, return Error::success(); } } - return make_error<StringError>("Section not found", - object_error::parse_failed); -} - -static bool isCompressed(const SectionBase &Section) { - const char *Magic = "ZLIB"; - return StringRef(Section.Name).startswith(".zdebug") || - (Section.OriginalData.size() > strlen(Magic) && - !strncmp(reinterpret_cast<const char *>(Section.OriginalData.data()), - Magic, strlen(Magic))) || - (Section.Flags & ELF::SHF_COMPRESSED); + return createStringError(object_error::parse_failed, "section '%s' not found", + SecName.str().c_str()); } static bool isCompressable(const SectionBase &Section) { - return !isCompressed(Section) && isDebugSection(Section) && - Section.Name != ".gdb_index"; + return !(Section.Flags & ELF::SHF_COMPRESSED) && + StringRef(Section.Name).startswith(".debug"); } static void replaceDebugSections( - const CopyConfig &Config, Object &Obj, SectionPred &RemovePred, + Object &Obj, SectionPred &RemovePred, function_ref<bool(const SectionBase &)> shouldReplace, function_ref<SectionBase *(const SectionBase *)> addSection) { + // Build a list of the debug sections we are going to replace. + // We can't call `addSection` while iterating over sections, + // because it would mutate the sections array. SmallVector<SectionBase *, 13> ToReplace; - SmallVector<RelocationSection *, 13> RelocationSections; - for (auto &Sec : Obj.sections()) { - if (RelocationSection *R = dyn_cast<RelocationSection>(&Sec)) { - if (shouldReplace(*R->getSection())) - RelocationSections.push_back(R); - continue; - } - + for (auto &Sec : Obj.sections()) if (shouldReplace(Sec)) ToReplace.push_back(&Sec); - } - for (SectionBase *S : ToReplace) { - SectionBase *NewSection = addSection(S); + // Build a mapping from original section to a new one. + DenseMap<SectionBase *, SectionBase *> FromTo; + for (SectionBase *S : ToReplace) + FromTo[S] = addSection(S); - for (RelocationSection *RS : RelocationSections) { - if (RS->getSection() == S) - RS->setSection(NewSection); - } - } + // Now we want to update the target sections of relocation + // sections. Also we will update the relocations themselves + // to update the symbol references. + for (auto &Sec : Obj.sections()) + Sec.replaceSectionReferences(FromTo); RemovePred = [shouldReplace, RemovePred](const SectionBase &Sec) { return shouldReplace(Sec) || RemovePred(Sec); }; } -// 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" -// should keep regardless of a remove. Additionally any removal should respect -// 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 void handleArgs(const CopyConfig &Config, Object &Obj, - const Reader &Reader, ElfType OutputElfType) { - - if (!Config.SplitDWO.empty()) { - splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType); - } - if (Config.OutputArch) - Obj.Machine = Config.OutputArch.getValue().EMachine; +static bool isUnneededSymbol(const Symbol &Sym) { + return !Sym.Referenced && + (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) && + Sym.Type != STT_SECTION; +} +static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { // TODO: update or remove symbols only if there is an option that affects // them. - if (Obj.SymbolTable) { - Obj.SymbolTable->updateSymbols([&](Symbol &Sym) { - if (!Sym.isCommon() && - ((Config.LocalizeHidden && - (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) || - is_contained(Config.SymbolsToLocalize, Sym.Name))) - Sym.Binding = STB_LOCAL; - - // Note: these two globalize flags have very similar names but different - // meanings: - // - // --globalize-symbol: promote a symbol to global - // --keep-global-symbol: all symbols except for these should be made local - // - // If --globalize-symbol is specified for a given symbol, it will be - // global in the output file even if it is not included via - // --keep-global-symbol. Because of that, make sure to check - // --globalize-symbol second. - if (!Config.SymbolsToKeepGlobal.empty() && - !is_contained(Config.SymbolsToKeepGlobal, Sym.Name) && - Sym.getShndx() != SHN_UNDEF) - Sym.Binding = STB_LOCAL; - - if (is_contained(Config.SymbolsToGlobalize, Sym.Name) && - Sym.getShndx() != SHN_UNDEF) - Sym.Binding = STB_GLOBAL; - - if (is_contained(Config.SymbolsToWeaken, Sym.Name) && - Sym.Binding == STB_GLOBAL) - Sym.Binding = STB_WEAK; - - if (Config.Weaken && Sym.Binding == STB_GLOBAL && - Sym.getShndx() != SHN_UNDEF) - Sym.Binding = STB_WEAK; - - const auto I = Config.SymbolsToRename.find(Sym.Name); - if (I != Config.SymbolsToRename.end()) - Sym.Name = I->getValue(); - - if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION) - Sym.Name = (Config.SymbolsPrefix + Sym.Name).str(); - }); - - // The purpose of this loop is to mark symbols referenced by sections - // (like GroupSection or RelocationSection). This way, we know which - // symbols are still 'needed' and which are not. - if (Config.StripUnneeded) { - for (auto &Section : Obj.sections()) - Section.markSymbols(); - } + if (!Obj.SymbolTable) + return Error::success(); + + Obj.SymbolTable->updateSymbols([&](Symbol &Sym) { + // Common and undefined symbols don't make sense as local symbols, and can + // even cause crashes if we localize those, so skip them. + if (!Sym.isCommon() && Sym.getShndx() != SHN_UNDEF && + ((Config.LocalizeHidden && + (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) || + is_contained(Config.SymbolsToLocalize, Sym.Name))) + Sym.Binding = STB_LOCAL; + + // Note: these two globalize flags have very similar names but different + // meanings: + // + // --globalize-symbol: promote a symbol to global + // --keep-global-symbol: all symbols except for these should be made local + // + // If --globalize-symbol is specified for a given symbol, it will be + // global in the output file even if it is not included via + // --keep-global-symbol. Because of that, make sure to check + // --globalize-symbol second. + if (!Config.SymbolsToKeepGlobal.empty() && + !is_contained(Config.SymbolsToKeepGlobal, Sym.Name) && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_LOCAL; + + if (is_contained(Config.SymbolsToGlobalize, Sym.Name) && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_GLOBAL; + + if (is_contained(Config.SymbolsToWeaken, Sym.Name) && + Sym.Binding == STB_GLOBAL) + Sym.Binding = STB_WEAK; + + if (Config.Weaken && Sym.Binding == STB_GLOBAL && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_WEAK; + + const auto I = Config.SymbolsToRename.find(Sym.Name); + if (I != Config.SymbolsToRename.end()) + Sym.Name = I->getValue(); + + if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION) + Sym.Name = (Config.SymbolsPrefix + Sym.Name).str(); + }); + + // The purpose of this loop is to mark symbols referenced by sections + // (like GroupSection or RelocationSection). This way, we know which + // symbols are still 'needed' and which are not. + if (Config.StripUnneeded || !Config.UnneededSymbolsToRemove.empty() || + !Config.OnlySection.empty()) { + for (auto &Section : Obj.sections()) + Section.markSymbols(); + } - Obj.removeSymbols([&](const Symbol &Sym) { - if (is_contained(Config.SymbolsToKeep, Sym.Name) || - (Config.KeepFileSymbols && Sym.Type == STT_FILE)) - return false; + auto RemoveSymbolsPred = [&](const Symbol &Sym) { + if (is_contained(Config.SymbolsToKeep, Sym.Name) || + (Config.KeepFileSymbols && Sym.Type == STT_FILE)) + return false; - if (Config.DiscardAll && Sym.Binding == STB_LOCAL && - Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE && - Sym.Type != STT_SECTION) - return true; + if ((Config.DiscardMode == DiscardType::All || + (Config.DiscardMode == DiscardType::Locals && + StringRef(Sym.Name).startswith(".L"))) && + Sym.Binding == STB_LOCAL && Sym.getShndx() != SHN_UNDEF && + Sym.Type != STT_FILE && Sym.Type != STT_SECTION) + return true; - if (Config.StripAll || Config.StripAllGNU) - return true; + if (Config.StripAll || Config.StripAllGNU) + return true; - if (is_contained(Config.SymbolsToRemove, Sym.Name)) - return true; + if (is_contained(Config.SymbolsToRemove, Sym.Name)) + return true; - if (Config.StripUnneeded && !Sym.Referenced && - (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) && - Sym.Type != STT_FILE && Sym.Type != STT_SECTION) - return true; + if ((Config.StripUnneeded || + is_contained(Config.UnneededSymbolsToRemove, Sym.Name)) && + isUnneededSymbol(Sym)) + return true; - return false; - }); - } + // We want to remove undefined symbols if all references have been stripped. + if (!Config.OnlySection.empty() && !Sym.Referenced && + Sym.getShndx() == SHN_UNDEF) + return true; + + return false; + }; + return Obj.removeSymbols(RemoveSymbolsPred); +} + +static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { SectionPred RemovePred = [](const SectionBase &) { return false; }; // Removes: @@ -383,7 +477,7 @@ static void handleArgs(const CopyConfig &Config, Object &Obj, if (Config.StripSections) { RemovePred = [RemovePred](const SectionBase &Sec) { - return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0; + return RemovePred(Sec) || Sec.ParentSegment == nullptr; }; } @@ -399,7 +493,7 @@ static void handleArgs(const CopyConfig &Config, Object &Obj, return true; if (&Sec == Obj.SectionNames) return false; - return (Sec.Flags & SHF_ALLOC) == 0; + return (Sec.Flags & SHF_ALLOC) == 0 && Sec.ParentSegment == nullptr; }; if (Config.StripAll) @@ -410,9 +504,21 @@ static void handleArgs(const CopyConfig &Config, Object &Obj, return false; if (StringRef(Sec.Name).startswith(".gnu.warning")) return false; + if (Sec.ParentSegment != nullptr) + return false; return (Sec.Flags & SHF_ALLOC) == 0; }; + if (Config.ExtractPartition || Config.ExtractMainPartition) { + RemovePred = [RemovePred](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if (Sec.Type == SHT_LLVM_PART_EHDR || Sec.Type == SHT_LLVM_PART_PHDR) + return true; + return (Sec.Flags & SHF_ALLOC) != 0 && !Sec.ParentSegment; + }; + } + // Explicit copies: if (!Config.OnlySection.empty()) { RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) { @@ -461,95 +567,210 @@ static void handleArgs(const CopyConfig &Config, Object &Obj, } if (Config.CompressionType != DebugCompressionType::None) - replaceDebugSections(Config, Obj, RemovePred, isCompressable, + replaceDebugSections(Obj, RemovePred, isCompressable, [&Config, &Obj](const SectionBase *S) { return &Obj.addSection<CompressedSection>( - *S, Config.CompressionType); - }); + *S, Config.CompressionType); + }); else if (Config.DecompressDebugSections) replaceDebugSections( - Config, Obj, RemovePred, + Obj, RemovePred, [](const SectionBase &S) { return isa<CompressedSection>(&S); }, [&Obj](const SectionBase *S) { auto CS = cast<CompressedSection>(S); return &Obj.addSection<DecompressedSection>(*CS); }); - Obj.removeSections(RemovePred); + return Obj.removeSections(Config.AllowBrokenLinks, RemovePred); +} - if (!Config.SectionsToRename.empty()) { +// 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" +// should keep regardless of a remove. Additionally any removal should respect +// 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.SplitDWO.empty()) + if (Error E = + splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType)) + return E; + + if (Config.OutputArch) { + Obj.Machine = Config.OutputArch.getValue().EMachine; + Obj.OSABI = Config.OutputArch.getValue().OSABI; + } + + // It is important to remove the sections first. For example, we want to + // remove the relocation sections before removing the symbols. That allows + // us to avoid reporting the inappropriate errors about removing symbols + // named in relocations. + if (Error E = replaceAndRemoveSections(Config, Obj)) + return E; + + if (Error E = updateAndRemoveSymbols(Config, Obj)) + return E; + + if (!Config.SectionsToRename.empty() || !Config.AllocSectionsPrefix.empty()) { + DenseSet<SectionBase *> PrefixedSections; for (auto &Sec : Obj.sections()) { const auto Iter = Config.SectionsToRename.find(Sec.Name); if (Iter != Config.SectionsToRename.end()) { const SectionRename &SR = Iter->second; Sec.Name = SR.NewName; - if (SR.NewFlags.hasValue()) { - // Preserve some flags which should not be dropped when setting flags. - // Also, preserve anything OS/processor dependant. - const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE | - ELF::SHF_GROUP | ELF::SHF_LINK_ORDER | - ELF::SHF_MASKOS | ELF::SHF_MASKPROC | - ELF::SHF_TLS | ELF::SHF_INFO_LINK; - Sec.Flags = (Sec.Flags & PreserveMask) | - (SR.NewFlags.getValue() & ~PreserveMask); + if (SR.NewFlags.hasValue()) + setSectionFlagsAndType(Sec, SR.NewFlags.getValue()); + } + + // Add a prefix to allocated sections and their relocation sections. This + // should be done after renaming the section by Config.SectionToRename to + // imitate the GNU objcopy behavior. + if (!Config.AllocSectionsPrefix.empty()) { + if (Sec.Flags & SHF_ALLOC) { + Sec.Name = (Config.AllocSectionsPrefix + Sec.Name).str(); + PrefixedSections.insert(&Sec); + + // Rename relocation sections associated to the allocated sections. + // For example, if we rename .text to .prefix.text, we also rename + // .rel.text to .rel.prefix.text. + // + // Dynamic relocation sections (SHT_REL[A] with SHF_ALLOC) are handled + // above, e.g., .rela.plt is renamed to .prefix.rela.plt, not + // .rela.prefix.plt since GNU objcopy does so. + } else if (auto *RelocSec = dyn_cast<RelocationSectionBase>(&Sec)) { + auto *TargetSec = RelocSec->getSection(); + if (TargetSec && (TargetSec->Flags & SHF_ALLOC)) { + StringRef prefix; + switch (Sec.Type) { + case SHT_REL: + prefix = ".rel"; + break; + case SHT_RELA: + prefix = ".rela"; + break; + default: + continue; + } + + // If the relocation section comes *after* the target section, we + // don't add Config.AllocSectionsPrefix because we've already added + // the prefix to TargetSec->Name. Otherwise, if the relocation + // section comes *before* the target section, we add the prefix. + if (PrefixedSections.count(TargetSec)) { + Sec.Name = (prefix + TargetSec->Name).str(); + } else { + const auto Iter = Config.SectionsToRename.find(TargetSec->Name); + if (Iter != Config.SectionsToRename.end()) { + // Both `--rename-section` and `--prefix-alloc-sections` are + // given but the target section is not yet renamed. + Sec.Name = + (prefix + Config.AllocSectionsPrefix + Iter->second.NewName) + .str(); + } else { + Sec.Name = + (prefix + Config.AllocSectionsPrefix + TargetSec->Name) + .str(); + } + } + } } } } } - if (!Config.AddSection.empty()) { - for (const auto &Flag : Config.AddSection) { - std::pair<StringRef, StringRef> SecPair = Flag.split("="); - StringRef SecName = SecPair.first; - StringRef File = SecPair.second; - ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = - MemoryBuffer::getFile(File); - if (!BufOrErr) - reportError(File, BufOrErr.getError()); - std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); - ArrayRef<uint8_t> Data( - reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), - Buf->getBufferSize()); - OwnedDataSection &NewSection = - Obj.addSection<OwnedDataSection>(SecName, Data); - if (SecName.startswith(".note") && SecName != ".note.GNU-stack") - NewSection.Type = SHT_NOTE; + if (!Config.SetSectionFlags.empty()) { + for (auto &Sec : Obj.sections()) { + const auto Iter = Config.SetSectionFlags.find(Sec.Name); + if (Iter != Config.SetSectionFlags.end()) { + const SectionFlagsUpdate &SFU = Iter->second; + setSectionFlagsAndType(Sec, SFU.NewFlags); + } } } - if (!Config.DumpSection.empty()) { - for (const auto &Flag : Config.DumpSection) { - std::pair<StringRef, StringRef> SecPair = Flag.split("="); - StringRef SecName = SecPair.first; - StringRef File = SecPair.second; - if (Error E = dumpSectionToFile(SecName, File, Obj)) - reportError(Config.InputFilename, std::move(E)); - } + for (const auto &Flag : Config.AddSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFile(File); + if (!BufOrErr) + return createFileError(File, errorCodeToError(BufOrErr.getError())); + std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); + ArrayRef<uint8_t> Data( + reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), + Buf->getBufferSize()); + OwnedDataSection &NewSection = + Obj.addSection<OwnedDataSection>(SecName, Data); + if (SecName.startswith(".note") && SecName != ".note.GNU-stack") + NewSection.Type = SHT_NOTE; + } + + for (const auto &Flag : Config.DumpSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + if (Error E = dumpSectionToFile(SecName, File, Obj)) + return E; } if (!Config.AddGnuDebugLink.empty()) - Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink); + Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink, + Config.GnuDebugLinkCRC32); + + for (const NewSymbolInfo &SI : Config.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); + } + + if (Config.EntryExpr) + Obj.Entry = Config.EntryExpr(Obj.Entry); + return Error::success(); } -void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { +static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out, + ElfType OutputElfType) { + std::unique_ptr<Writer> Writer = + createWriter(Config, Obj, Out, OutputElfType); + if (Error E = Writer->finalize()) + return E; + return Writer->write(); +} + +Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + IHexReader Reader(&In); + std::unique_ptr<Object> Obj = Reader.create(); + const ElfType OutputElfType = + getOutputElfType(Config.OutputArch.getValueOr(Config.BinaryArch)); + if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType)) + return E; + return writeOutput(Config, *Obj, Out, OutputElfType); +} + +Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { BinaryReader Reader(Config.BinaryArch, &In); std::unique_ptr<Object> Obj = Reader.create(); // Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch // (-B<arch>). - const ElfType OutputElfType = getOutputElfType( - Config.OutputArch ? Config.OutputArch.getValue() : Config.BinaryArch); - handleArgs(Config, *Obj, Reader, OutputElfType); - std::unique_ptr<Writer> Writer = - createWriter(Config, *Obj, Out, OutputElfType); - Writer->finalize(); - Writer->write(); + const ElfType OutputElfType = + getOutputElfType(Config.OutputArch.getValueOr(Config.BinaryArch)); + if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType)) + return E; + return writeOutput(Config, *Obj, Out, OutputElfType); } -void executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out) { - ELFReader Reader(&In); +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::ELFObjectFileBase &In, Buffer &Out) { + ELFReader Reader(&In, Config.ExtractPartition); std::unique_ptr<Object> Obj = Reader.create(); // Prefer OutputArch (-O<format>) if set, otherwise infer it from the input. const ElfType OutputElfType = @@ -558,25 +779,36 @@ void executeObjcopyOnBinary(const CopyConfig &Config, ArrayRef<uint8_t> BuildIdBytes; if (!Config.BuildIdLinkDir.empty()) { - BuildIdBytes = unwrapOrError(findBuildID(In)); + auto BuildIdBytesOrErr = findBuildID(Config, In); + if (auto E = BuildIdBytesOrErr.takeError()) + return E; + BuildIdBytes = *BuildIdBytesOrErr; + if (BuildIdBytes.size() < 2) - error("build ID in file '" + Config.InputFilename + - "' is smaller than two bytes"); + return createFileError( + Config.InputFilename, + createStringError(object_error::parse_failed, + "build ID is smaller than two bytes")); } - if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) { - linkToBuildIdDir(Config, Config.InputFilename, - Config.BuildIdLinkInput.getValue(), BuildIdBytes); - } - handleArgs(Config, *Obj, Reader, OutputElfType); - std::unique_ptr<Writer> Writer = - createWriter(Config, *Obj, Out, OutputElfType); - Writer->finalize(); - Writer->write(); - if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) { - linkToBuildIdDir(Config, Config.OutputFilename, - Config.BuildIdLinkOutput.getValue(), BuildIdBytes); - } + 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)) + 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 diff --git a/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h b/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h index 43f41c00ce5b..e13e237e29c4 100644 --- a/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h +++ b/contrib/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h @@ -1,9 +1,8 @@ //===- ELFObjcopy.h ---------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -11,6 +10,7 @@ #define LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H namespace llvm { +class Error; class MemoryBuffer; namespace object { @@ -22,10 +22,12 @@ struct CopyConfig; class Buffer; namespace elf { -void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out); -void executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out); +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); } // end namespace elf } // end namespace objcopy diff --git a/contrib/llvm/tools/llvm-objcopy/ELF/Object.cpp b/contrib/llvm/tools/llvm-objcopy/ELF/Object.cpp index 3d3e029c09eb..fa696380e17c 100644 --- a/contrib/llvm/tools/llvm-objcopy/ELF/Object.cpp +++ b/contrib/llvm/tools/llvm-objcopy/ELF/Object.cpp @@ -1,9 +1,8 @@ //===- Object.cpp ---------------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -18,6 +17,7 @@ #include "llvm/MC/MCTargetOptions.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Compression.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/Path.h" @@ -25,6 +25,7 @@ #include <cstddef> #include <cstdint> #include <iterator> +#include <unordered_set> #include <utility> #include <vector> @@ -36,8 +37,8 @@ using namespace object; using namespace ELF; template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) { - uint8_t *B = Buf.getBufferStart(); - B += Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr); + uint8_t *B = 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; @@ -49,15 +50,24 @@ template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) { Phdr.p_align = Seg.Align; } -void SectionBase::removeSectionReferences(const SectionBase *Sec) {} -void SectionBase::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) {} +Error SectionBase::removeSectionReferences( + bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove) { + return Error::success(); +} + +Error SectionBase::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { + return Error::success(); +} + void SectionBase::initialize(SectionTableRef SecTable) {} void SectionBase::finalize() {} void SectionBase::markSymbols() {} +void SectionBase::replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &) {} template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) { - uint8_t *B = Buf.getBufferStart(); - B += Sec.HeaderOffset; + uint8_t *B = Buf.getBufferStart() + Sec.HeaderOffset; Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B); Shdr.sh_name = Sec.NameIndex; Shdr.sh_type = Sec.Type; @@ -113,30 +123,270 @@ template <class ELFT> void ELFSectionSizer<ELFT>::visit(DecompressedSection &Sec) {} void BinarySectionWriter::visit(const SectionIndexSection &Sec) { - error("Cannot write symbol section index table '" + Sec.Name + "' "); + error("cannot write symbol section index table '" + Sec.Name + "' "); } void BinarySectionWriter::visit(const SymbolTableSection &Sec) { - error("Cannot write symbol table '" + Sec.Name + "' out to binary"); + error("cannot write symbol table '" + Sec.Name + "' out to binary"); } void BinarySectionWriter::visit(const RelocationSection &Sec) { - error("Cannot write relocation section '" + Sec.Name + "' out to binary"); + error("cannot write relocation section '" + Sec.Name + "' out to binary"); } void BinarySectionWriter::visit(const GnuDebugLinkSection &Sec) { - error("Cannot write '" + Sec.Name + "' out to binary"); + error("cannot write '" + Sec.Name + "' out to binary"); } void BinarySectionWriter::visit(const GroupSection &Sec) { - error("Cannot write '" + Sec.Name + "' out to binary"); + error("cannot write '" + Sec.Name + "' out to binary"); } void SectionWriter::visit(const Section &Sec) { - if (Sec.Type == SHT_NOBITS) - return; - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - llvm::copy(Sec.Contents, Buf); + if (Sec.Type != SHT_NOBITS) + llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset); +} + +static bool addressOverflows32bit(uint64_t Addr) { + // Sign extended 32 bit addresses (e.g 0xFFFFFFFF80000000) are ok + return Addr > UINT32_MAX && Addr + 0x80000000 > UINT32_MAX; +} + +template <class T> static T checkedGetHex(StringRef S) { + T Value; + bool Fail = S.getAsInteger(16, Value); + assert(!Fail); + (void)Fail; + return Value; +} + +// 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) { + // Fill range with '0' + std::fill(It, It + Len, '0'); + + for (long I = Len - 1; I >= 0; --I) { + unsigned char Mod = static_cast<unsigned char>(X) & 15; + *(It + I) = hexdigit(Mod, false); + X >>= 4; + } + assert(X == 0); + return It + Len; +} + +uint8_t IHexRecord::getChecksum(StringRef S) { + assert((S.size() & 1) == 0); + uint8_t Checksum = 0; + while (!S.empty()) { + Checksum += checkedGetHex<uint8_t>(S.take_front(2)); + S = S.drop_front(2); + } + return -Checksum; +} + +IHexLineData IHexRecord::getLine(uint8_t Type, uint16_t Addr, + ArrayRef<uint8_t> Data) { + IHexLineData Line(getLineLength(Data.size())); + assert(Line.size()); + auto Iter = Line.begin(); + *Iter++ = ':'; + Iter = utohexstr(Data.size(), Iter, 2); + Iter = utohexstr(Addr, Iter, 4); + Iter = utohexstr(Type, Iter, 2); + for (uint8_t X : Data) + Iter = utohexstr(X, Iter, 2); + StringRef S(Line.data() + 1, std::distance(Line.begin() + 1, Iter)); + Iter = utohexstr(getChecksum(S), Iter, 2); + *Iter++ = '\r'; + *Iter++ = '\n'; + assert(Iter == Line.end()); + return Line; +} + +static Error checkRecord(const IHexRecord &R) { + switch (R.Type) { + case IHexRecord::Data: + if (R.HexData.size() == 0) + return createStringError( + errc::invalid_argument, + "zero data length is not allowed for data records"); + break; + case IHexRecord::EndOfFile: + break; + case IHexRecord::SegmentAddr: + // 20-bit segment address. Data length must be 2 bytes + // (4 bytes in hex) + if (R.HexData.size() != 4) + return createStringError( + errc::invalid_argument, + "segment address data should be 2 bytes in size"); + break; + case IHexRecord::StartAddr80x86: + case IHexRecord::StartAddr: + if (R.HexData.size() != 8) + return createStringError(errc::invalid_argument, + "start address data should be 4 bytes in size"); + // According to Intel HEX specification '03' record + // only specifies the code address within the 20-bit + // segmented address space of the 8086/80186. This + // means 12 high order bits should be zeroes. + if (R.Type == IHexRecord::StartAddr80x86 && + R.HexData.take_front(3) != "000") + return createStringError(errc::invalid_argument, + "start address exceeds 20 bit for 80x86"); + break; + case IHexRecord::ExtendedAddr: + // 16-31 bits of linear base address + if (R.HexData.size() != 4) + return createStringError( + errc::invalid_argument, + "extended address data should be 2 bytes in size"); + break; + default: + // Unknown record type + return createStringError(errc::invalid_argument, "unknown record type: %u", + static_cast<unsigned>(R.Type)); + } + return Error::success(); +} + +// Checks that IHEX line contains valid characters. +// This allows converting hexadecimal data to integers +// without extra verification. +static Error checkChars(StringRef Line) { + assert(!Line.empty()); + if (Line[0] != ':') + return createStringError(errc::invalid_argument, + "missing ':' in the beginning of line."); + + for (size_t Pos = 1; Pos < Line.size(); ++Pos) + if (hexDigitValue(Line[Pos]) == -1U) + return createStringError(errc::invalid_argument, + "invalid character at position %zu.", Pos + 1); + return Error::success(); +} + +Expected<IHexRecord> IHexRecord::parse(StringRef Line) { + assert(!Line.empty()); + + // ':' + Length + Address + Type + Checksum with empty data ':LLAAAATTCC' + if (Line.size() < 11) + return createStringError(errc::invalid_argument, + "line is too short: %zu chars.", Line.size()); + + if (Error E = checkChars(Line)) + return std::move(E); + + IHexRecord Rec; + size_t DataLen = checkedGetHex<uint8_t>(Line.substr(1, 2)); + if (Line.size() != getLength(DataLen)) + return createStringError(errc::invalid_argument, + "invalid line length %zu (should be %zu)", + Line.size(), getLength(DataLen)); + + Rec.Addr = checkedGetHex<uint16_t>(Line.substr(3, 4)); + Rec.Type = checkedGetHex<uint8_t>(Line.substr(7, 2)); + Rec.HexData = Line.substr(9, DataLen * 2); + + if (getChecksum(Line.drop_front(1)) != 0) + return createStringError(errc::invalid_argument, "incorrect checksum."); + if (Error E = checkRecord(Rec)) + return std::move(E); + return Rec; +} + +static uint64_t sectionPhysicalAddr(const SectionBase *Sec) { + Segment *Seg = Sec->ParentSegment; + if (Seg && Seg->Type != ELF::PT_LOAD) + Seg = nullptr; + return Seg ? Seg->PAddr + Sec->OriginalOffset - Seg->OriginalOffset + : Sec->Addr; +} + +void IHexSectionWriterBase::writeSection(const SectionBase *Sec, + ArrayRef<uint8_t> Data) { + assert(Data.size() == Sec->Size); + const uint32_t ChunkSize = 16; + uint32_t Addr = sectionPhysicalAddr(Sec) & 0xFFFFFFFFU; + while (!Data.empty()) { + uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize); + if (Addr > SegmentAddr + BaseAddr + 0xFFFFU) { + if (Addr > 0xFFFFFU) { + // Write extended address record, zeroing segment address + // if needed. + if (SegmentAddr != 0) + SegmentAddr = writeSegmentAddr(0U); + BaseAddr = writeBaseAddr(Addr); + } else { + // We can still remain 16-bit + SegmentAddr = writeSegmentAddr(Addr); + } + } + uint64_t SegOffset = Addr - BaseAddr - SegmentAddr; + assert(SegOffset <= 0xFFFFU); + DataSize = std::min(DataSize, 0x10000U - SegOffset); + writeData(0, SegOffset, Data.take_front(DataSize)); + Addr += DataSize; + Data = Data.drop_front(DataSize); + } +} + +uint64_t IHexSectionWriterBase::writeSegmentAddr(uint64_t Addr) { + assert(Addr <= 0xFFFFFU); + uint8_t Data[] = {static_cast<uint8_t>((Addr & 0xF0000U) >> 12), 0}; + writeData(2, 0, Data); + return Addr & 0xF0000U; +} + +uint64_t IHexSectionWriterBase::writeBaseAddr(uint64_t Addr) { + assert(Addr <= 0xFFFFFFFFU); + uint64_t Base = Addr & 0xFFFF0000U; + uint8_t Data[] = {static_cast<uint8_t>(Base >> 24), + static_cast<uint8_t>((Base >> 16) & 0xFF)}; + writeData(4, 0, Data); + return Base; +} + +void IHexSectionWriterBase::writeData(uint8_t Type, uint16_t Addr, + ArrayRef<uint8_t> Data) { + Offset += IHexRecord::getLineLength(Data.size()); +} + +void IHexSectionWriterBase::visit(const Section &Sec) { + writeSection(&Sec, Sec.Contents); +} + +void IHexSectionWriterBase::visit(const OwnedDataSection &Sec) { + writeSection(&Sec, Sec.Data); +} + +void IHexSectionWriterBase::visit(const StringTableSection &Sec) { + // Check that sizer has already done its work + assert(Sec.Size == Sec.StrTabBuilder.getSize()); + // We are free to pass an invalid pointer to writeSection as long + // as we don't actually write any data. The real writer class has + // to override this method . + writeSection(&Sec, {nullptr, static_cast<size_t>(Sec.Size)}); +} + +void IHexSectionWriterBase::visit(const DynamicRelocationSection &Sec) { + writeSection(&Sec, Sec.Contents); +} + +void IHexSectionWriter::writeData(uint8_t Type, uint16_t Addr, + ArrayRef<uint8_t> Data) { + IHexLineData HexData = IHexRecord::getLine(Type, Addr, Data); + memcpy(Out.getBufferStart() + Offset, HexData.data(), HexData.size()); + Offset += HexData.size(); +} + +void IHexSectionWriter::visit(const StringTableSection &Sec) { + assert(Sec.Size == Sec.StrTabBuilder.getSize()); + std::vector<uint8_t> Data(Sec.Size); + Sec.StrTabBuilder.write(Data.data()); + writeSection(&Sec, Data); } void Section::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } @@ -144,8 +394,7 @@ void Section::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } void Section::accept(MutableSectionVisitor &Visitor) { Visitor.visit(*this); } void SectionWriter::visit(const OwnedDataSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - llvm::copy(Sec.Data, Buf); + llvm::copy(Sec.Data, Out.getBufferStart() + Sec.Offset); } static const std::vector<uint8_t> ZlibGnuMagic = {'Z', 'L', 'I', 'B'}; @@ -161,8 +410,7 @@ getDecompressedSizeAndAlignment(ArrayRef<uint8_t> Data) { const bool IsGnuDebug = isDataGnuCompressed(Data); const uint64_t DecompressedSize = IsGnuDebug - ? support::endian::read64be(reinterpret_cast<const uint64_t *>( - Data.data() + ZlibGnuMagic.size())) + ? support::endian::read64be(Data.data() + ZlibGnuMagic.size()) : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data())->ch_size; const uint64_t DecompressedAlign = IsGnuDebug ? 1 @@ -174,13 +422,6 @@ getDecompressedSizeAndAlignment(ArrayRef<uint8_t> Data) { template <class ELFT> void ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) { - uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - - if (!zlib::isAvailable()) { - std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); - return; - } - const size_t DataOffset = isDataGnuCompressed(Sec.OriginalData) ? (ZlibGnuMagic.size() + sizeof(Sec.Size)) : sizeof(Elf_Chdr_Impl<ELFT>); @@ -194,11 +435,12 @@ void ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) { static_cast<size_t>(Sec.Size))) reportError(Sec.Name, std::move(E)); + uint8_t *Buf = Out.getBufferStart() + Sec.Offset; std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); } void BinarySectionWriter::visit(const DecompressedSection &Sec) { - error("Cannot write compressed section '" + Sec.Name + "' "); + error("cannot write compressed section '" + Sec.Name + "' "); } void DecompressedSection::accept(SectionVisitor &Visitor) const { @@ -217,15 +459,22 @@ void OwnedDataSection::accept(MutableSectionVisitor &Visitor) { Visitor.visit(*this); } +void OwnedDataSection::appendHexData(StringRef HexData) { + assert((HexData.size() & 1) == 0); + while (!HexData.empty()) { + Data.push_back(checkedGetHex<uint8_t>(HexData.take_front(2))); + HexData = HexData.drop_front(2); + } + Size = Data.size(); +} + void BinarySectionWriter::visit(const CompressedSection &Sec) { - error("Cannot write compressed section '" + Sec.Name + "' "); + error("cannot write compressed section '" + Sec.Name + "' "); } template <class ELFT> void ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) { - uint8_t *Buf = Out.getBufferStart(); - Buf += Sec.Offset; - + uint8_t *Buf = Out.getBufferStart() + Sec.Offset; if (Sec.CompressionType == DebugCompressionType::None) { std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); return; @@ -255,12 +504,6 @@ CompressedSection::CompressedSection(const SectionBase &Sec, DebugCompressionType CompressionType) : SectionBase(Sec), CompressionType(CompressionType), DecompressedSize(Sec.OriginalData.size()), DecompressedAlign(Sec.Align) { - - if (!zlib::isAvailable()) { - CompressionType = DebugCompressionType::None; - return; - } - if (Error E = zlib::compress( StringRef(reinterpret_cast<const char *>(OriginalData.data()), OriginalData.size()), @@ -299,16 +542,16 @@ void CompressedSection::accept(MutableSectionVisitor &Visitor) { Visitor.visit(*this); } -void StringTableSection::addString(StringRef Name) { - StrTabBuilder.add(Name); - Size = StrTabBuilder.getSize(); -} +void StringTableSection::addString(StringRef Name) { StrTabBuilder.add(Name); } uint32_t StringTableSection::findIndex(StringRef Name) const { return StrTabBuilder.getOffset(Name); } -void StringTableSection::finalize() { StrTabBuilder.finalize(); } +void StringTableSection::prepareForLayout() { + StrTabBuilder.finalize(); + Size = StrTabBuilder.getSize(); +} void SectionWriter::visit(const StringTableSection &Sec) { Sec.StrTabBuilder.write(Out.getBufferStart() + Sec.Offset); @@ -325,8 +568,7 @@ void StringTableSection::accept(MutableSectionVisitor &Visitor) { template <class ELFT> void ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) { uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - auto *IndexesBuffer = reinterpret_cast<Elf_Word *>(Buf); - llvm::copy(Sec.Indexes, IndexesBuffer); + llvm::copy(Sec.Indexes, reinterpret_cast<Elf_Word *>(Buf)); } void SectionIndexSection::initialize(SectionTableRef SecTable) { @@ -355,6 +597,11 @@ static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) { case SHN_COMMON: return true; } + + if (Machine == EM_AMDGPU) { + return Index == SHN_AMDGPU_LDS; + } + if (Machine == EM_HEXAGON) { switch (Index) { case SHN_HEXAGON_SCOMMON: @@ -376,21 +623,17 @@ uint16_t Symbol::getShndx() const { return SHN_XINDEX; return DefinedIn->Index; } - switch (ShndxType) { - // This means that we don't have a defined section but we do need to - // output a legitimate section index. - case SYMBOL_SIMPLE_INDEX: + + if (ShndxType == SYMBOL_SIMPLE_INDEX) { + // This means that we don't have a defined section but we do need to + // output a legitimate section index. return SHN_UNDEF; - case SYMBOL_ABS: - case SYMBOL_COMMON: - case SYMBOL_HEXAGON_SCOMMON: - case SYMBOL_HEXAGON_SCOMMON_2: - case SYMBOL_HEXAGON_SCOMMON_4: - case SYMBOL_HEXAGON_SCOMMON_8: - case SYMBOL_XINDEX: - return static_cast<uint16_t>(ShndxType); } - llvm_unreachable("Symbol with invalid ShndxType encountered"); + + assert(ShndxType == SYMBOL_ABS || ShndxType == SYMBOL_COMMON || + (ShndxType >= SYMBOL_LOPROC && ShndxType <= SYMBOL_HIPROC) || + (ShndxType >= SYMBOL_LOOS && ShndxType <= SYMBOL_HIOS)); + return static_cast<uint16_t>(ShndxType); } bool Symbol::isCommon() const { return getShndx() == SHN_COMMON; } @@ -404,7 +647,7 @@ void SymbolTableSection::assignIndices() { void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type, SectionBase *DefinedIn, uint64_t Value, uint8_t Visibility, uint16_t Shndx, - uint64_t Size) { + uint64_t SymbolSize) { Symbol Sym; Sym.Name = Name.str(); Sym.Binding = Bind; @@ -420,21 +663,28 @@ void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type, } Sym.Value = Value; Sym.Visibility = Visibility; - Sym.Size = Size; + Sym.Size = SymbolSize; Sym.Index = Symbols.size(); Symbols.emplace_back(llvm::make_unique<Symbol>(Sym)); Size += this->EntrySize; } -void SymbolTableSection::removeSectionReferences(const SectionBase *Sec) { - if (SectionIndexTable == Sec) +Error SymbolTableSection::removeSectionReferences( + bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove) { + if (ToRemove(SectionIndexTable)) SectionIndexTable = nullptr; - if (SymbolNames == Sec) { - error("String table " + SymbolNames->Name + - " cannot be removed because it is referenced by the symbol table " + - this->Name); + if (ToRemove(SymbolNames)) { + if (!AllowBrokenLinks) + return createStringError( + llvm::errc::invalid_argument, + "string table '%s' cannot be removed because it is " + "referenced by the symbol table '%s'", + SymbolNames->Name.data(), this->Name.data()); + SymbolNames = nullptr; } - removeSymbols([Sec](const Symbol &Sym) { return Sym.DefinedIn == Sec; }); + return removeSymbols( + [ToRemove](const Symbol &Sym) { return ToRemove(Sym.DefinedIn); }); } void SymbolTableSection::updateSymbols(function_ref<void(Symbol &)> Callable) { @@ -446,7 +696,7 @@ void SymbolTableSection::updateSymbols(function_ref<void(Symbol &)> Callable) { assignIndices(); } -void SymbolTableSection::removeSymbols( +Error SymbolTableSection::removeSymbols( function_ref<bool(const Symbol &)> ToRemove) { Symbols.erase( std::remove_if(std::begin(Symbols) + 1, std::end(Symbols), @@ -454,6 +704,14 @@ void SymbolTableSection::removeSymbols( std::end(Symbols)); Size = Symbols.size() * EntrySize; assignIndices(); + return Error::success(); +} + +void SymbolTableSection::replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) { + for (std::unique_ptr<Symbol> &Sym : Symbols) + if (SectionBase *To = FromTo.lookup(Sym->DefinedIn)) + Sym->DefinedIn = To; } void SymbolTableSection::initialize(SectionTableRef SecTable) { @@ -467,40 +725,50 @@ void SymbolTableSection::initialize(SectionTableRef SecTable) { } void SymbolTableSection::finalize() { - // Make sure SymbolNames is finalized before getting name indexes. - SymbolNames->finalize(); - uint32_t MaxLocalIndex = 0; - for (auto &Sym : Symbols) { - Sym->NameIndex = SymbolNames->findIndex(Sym->Name); + for (std::unique_ptr<Symbol> &Sym : Symbols) { + Sym->NameIndex = + SymbolNames == nullptr ? 0 : SymbolNames->findIndex(Sym->Name); if (Sym->Binding == STB_LOCAL) MaxLocalIndex = std::max(MaxLocalIndex, Sym->Index); } // Now we need to set the Link and Info fields. - Link = SymbolNames->Index; + Link = SymbolNames == nullptr ? 0 : SymbolNames->Index; Info = MaxLocalIndex + 1; } void SymbolTableSection::prepareForLayout() { - // Add all potential section indexes before file layout so that the section - // index section has the approprite size. - if (SectionIndexTable != nullptr) { - for (const auto &Sym : Symbols) { - if (Sym->DefinedIn != nullptr && Sym->DefinedIn->Index >= SHN_LORESERVE) - SectionIndexTable->addIndex(Sym->DefinedIn->Index); - else - SectionIndexTable->addIndex(SHN_UNDEF); - } - } + // Reserve proper amount of space in section index table, so we can + // layout sections correctly. We will fill the table with correct + // indexes later in fillShdnxTable. + if (SectionIndexTable) + SectionIndexTable->reserve(Symbols.size()); + // Add all of our strings to SymbolNames so that SymbolNames has the right // size before layout is decided. - for (auto &Sym : Symbols) - SymbolNames->addString(Sym->Name); + // If the symbol names section has been removed, don't try to add strings to + // the table. + if (SymbolNames != nullptr) + for (std::unique_ptr<Symbol> &Sym : Symbols) + SymbolNames->addString(Sym->Name); +} + +void SymbolTableSection::fillShndxTable() { + if (SectionIndexTable == nullptr) + return; + // Fill section index table with real section indexes. This function must + // be called after assignOffsets. + for (const std::unique_ptr<Symbol> &Sym : Symbols) { + if (Sym->DefinedIn != nullptr && Sym->DefinedIn->Index >= SHN_LORESERVE) + SectionIndexTable->addIndex(Sym->DefinedIn->Index); + else + SectionIndexTable->addIndex(SHN_UNDEF); + } } const Symbol *SymbolTableSection::getSymbolByIndex(uint32_t Index) const { if (Symbols.size() <= Index) - error("Invalid symbol index: " + Twine(Index)); + error("invalid symbol index: " + Twine(Index)); return Symbols[Index].get(); } @@ -511,11 +779,9 @@ Symbol *SymbolTableSection::getSymbolByIndex(uint32_t Index) { template <class ELFT> void ELFSectionWriter<ELFT>::visit(const SymbolTableSection &Sec) { - uint8_t *Buf = Out.getBufferStart(); - Buf += Sec.Offset; - Elf_Sym *Sym = reinterpret_cast<Elf_Sym *>(Buf); + Elf_Sym *Sym = reinterpret_cast<Elf_Sym *>(Out.getBufferStart() + Sec.Offset); // Loop though symbols setting each entry of the symbol table. - for (auto &Symbol : Sec.Symbols) { + for (const std::unique_ptr<Symbol> &Symbol : Sec.Symbols) { Sym->st_name = Symbol->NameIndex; Sym->st_value = Symbol->Value; Sym->st_size = Symbol->Size; @@ -535,16 +801,31 @@ void SymbolTableSection::accept(MutableSectionVisitor &Visitor) { Visitor.visit(*this); } -template <class SymTabType> -void RelocSectionWithSymtabBase<SymTabType>::removeSectionReferences( - const SectionBase *Sec) { - if (Symbols == Sec) { - error("Symbol table " + Symbols->Name + - " cannot be removed because it is " - "referenced by the relocation " - "section " + - this->Name); +Error RelocationSection::removeSectionReferences( + bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove) { + if (ToRemove(Symbols)) { + if (!AllowBrokenLinks) + return createStringError( + llvm::errc::invalid_argument, + "symbol table '%s' cannot be removed because it is " + "referenced by the relocation section '%s'", + Symbols->Name.data(), this->Name.data()); + Symbols = nullptr; } + + for (const Relocation &R : Relocations) { + if (!R.RelocSymbol->DefinedIn || !ToRemove(R.RelocSymbol->DefinedIn)) + continue; + return createStringError(llvm::errc::invalid_argument, + "section '%s' cannot be removed: (%s+0x%" PRIx64 + ") has relocation against symbol '%s'", + R.RelocSymbol->DefinedIn->Name.data(), + SecToApplyRel->Name.data(), R.Offset, + R.RelocSymbol->Name.c_str()); + } + + return Error::success(); } template <class SymTabType> @@ -609,12 +890,15 @@ void RelocationSection::accept(MutableSectionVisitor &Visitor) { Visitor.visit(*this); } -void RelocationSection::removeSymbols( +Error RelocationSection::removeSymbols( function_ref<bool(const Symbol &)> ToRemove) { for (const Relocation &Reloc : Relocations) if (ToRemove(*Reloc.RelocSymbol)) - error("not stripping symbol '" + Reloc.RelocSymbol->Name + - "' because it is named in a relocation"); + return createStringError( + llvm::errc::invalid_argument, + "not stripping symbol '%s' because it is named in a relocation", + Reloc.RelocSymbol->Name.data()); + return Error::success(); } void RelocationSection::markSymbols() { @@ -622,9 +906,15 @@ void RelocationSection::markSymbols() { Reloc.RelocSymbol->Referenced = true; } +void RelocationSection::replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) { + // Update the target section if it was replaced. + if (SectionBase *To = FromTo.lookup(SecToApplyRel)) + SecToApplyRel = To; +} + void SectionWriter::visit(const DynamicRelocationSection &Sec) { - llvm::copy(Sec.Contents, - Out.getBufferStart() + Sec.Offset); + llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset); } void DynamicRelocationSection::accept(SectionVisitor &Visitor) const { @@ -635,13 +925,38 @@ void DynamicRelocationSection::accept(MutableSectionVisitor &Visitor) { Visitor.visit(*this); } -void Section::removeSectionReferences(const SectionBase *Sec) { - if (LinkSection == Sec) { - error("Section " + LinkSection->Name + - " cannot be removed because it is " - "referenced by the section " + - this->Name); +Error DynamicRelocationSection::removeSectionReferences( + bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) { + if (ToRemove(Symbols)) { + if (!AllowBrokenLinks) + return createStringError( + llvm::errc::invalid_argument, + "symbol table '%s' cannot be removed because it is " + "referenced by the relocation section '%s'", + Symbols->Name.data(), this->Name.data()); + Symbols = nullptr; + } + + // SecToApplyRel contains a section referenced by sh_info field. It keeps + // a section to which the relocation section applies. When we remove any + // sections we also remove their relocation sections. Since we do that much + // earlier, this assert should never be triggered. + assert(!SecToApplyRel || !ToRemove(SecToApplyRel)); + return Error::success(); +} + +Error Section::removeSectionReferences( + bool AllowBrokenDependency, + function_ref<bool(const SectionBase *)> ToRemove) { + if (ToRemove(LinkSection)) { + if (!AllowBrokenDependency) + return createStringError(llvm::errc::invalid_argument, + "section '%s' cannot be removed because it is " + "referenced by the section '%s'", + LinkSection->Name.data(), this->Name.data()); + LinkSection = nullptr; } + return Error::success(); } void GroupSection::finalize() { @@ -649,13 +964,13 @@ void GroupSection::finalize() { this->Link = SymTab->Index; } -void GroupSection::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { - if (ToRemove(*Sym)) { - error("Symbol " + Sym->Name + - " cannot be removed because it is " - "referenced by the section " + - this->Name + "[" + Twine(this->Index) + "]"); - } +Error GroupSection::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { + if (ToRemove(*Sym)) + return createStringError(llvm::errc::invalid_argument, + "symbol '%s' cannot be removed because it is " + "referenced by the section '%s[%d]'", + Sym->Name.data(), this->Name.data(), this->Index); + return Error::success(); } void GroupSection::markSymbols() { @@ -663,19 +978,26 @@ void GroupSection::markSymbols() { Sym->Referenced = true; } +void GroupSection::replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) { + for (SectionBase *&Sec : GroupMembers) + if (SectionBase *To = FromTo.lookup(Sec)) + Sec = To; +} + void Section::initialize(SectionTableRef SecTable) { - if (Link != ELF::SHN_UNDEF) { - LinkSection = - SecTable.getSection(Link, "Link field value " + Twine(Link) + - " in section " + Name + " is invalid"); - if (LinkSection->Type == ELF::SHT_SYMTAB) - LinkSection = nullptr; - } + if (Link == ELF::SHN_UNDEF) + return; + LinkSection = + SecTable.getSection(Link, "Link field value " + Twine(Link) + + " in section " + Name + " is invalid"); + if (LinkSection->Type == ELF::SHT_SYMTAB) + LinkSection = nullptr; } void Section::finalize() { this->Link = LinkSection ? LinkSection->Index : 0; } -void GnuDebugLinkSection::init(StringRef File, StringRef Data) { +void GnuDebugLinkSection::init(StringRef File) { FileName = sys::path::filename(File); // The format for the .gnu_debuglink starts with the file name and is // followed by a null terminator and then the CRC32 of the file. The CRC32 @@ -690,31 +1012,21 @@ void GnuDebugLinkSection::init(StringRef File, StringRef Data) { // establish the order that sections should go in. By using the maximum // possible offset we cause this section to wind up at the end. OriginalOffset = std::numeric_limits<uint64_t>::max(); - JamCRC CRC; - CRC.update(ArrayRef<char>(Data.data(), Data.size())); - // The CRC32 value needs to be complemented because the JamCRC dosn't - // finalize the CRC32 value. It also dosn't negate the initial CRC32 value - // but it starts by default at 0xFFFFFFFF which is the complement of zero. - CRC32 = ~CRC.getCRC(); } -GnuDebugLinkSection::GnuDebugLinkSection(StringRef File) : FileName(File) { - // Read in the file to compute the CRC of it. - auto DebugOrErr = MemoryBuffer::getFile(File); - if (!DebugOrErr) - error("'" + File + "': " + DebugOrErr.getError().message()); - auto Debug = std::move(*DebugOrErr); - init(File, Debug->getBuffer()); +GnuDebugLinkSection::GnuDebugLinkSection(StringRef File, + uint32_t PrecomputedCRC) + : FileName(File), CRC32(PrecomputedCRC) { + init(File); } template <class ELFT> void ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) { - auto Buf = Out.getBufferStart() + Sec.Offset; - char *File = reinterpret_cast<char *>(Buf); + unsigned char *Buf = Out.getBufferStart() + Sec.Offset; Elf_Word *CRC = reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word)); *CRC = Sec.CRC32; - llvm::copy(Sec.FileName, File); + llvm::copy(Sec.FileName, Buf); } void GnuDebugLinkSection::accept(SectionVisitor &Visitor) const { @@ -730,7 +1042,7 @@ void ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) { ELF::Elf32_Word *Buf = reinterpret_cast<ELF::Elf32_Word *>(Out.getBufferStart() + Sec.Offset); *Buf++ = Sec.FlagWord; - for (const auto *S : Sec.GroupMembers) + for (SectionBase *S : Sec.GroupMembers) support::endian::write32<ELFT::TargetEndianness>(Buf++, S->Index); } @@ -750,6 +1062,20 @@ static bool sectionWithinSegment(const SectionBase &Section, // segments and ensures that the section "belongs" to the second segment and // not the first. uint64_t SecSize = Section.Size ? Section.Size : 1; + + if (Section.Type == SHT_NOBITS) { + if (!(Section.Flags & SHF_ALLOC)) + return false; + + bool SectionIsTLS = Section.Flags & SHF_TLS; + bool SegmentIsTLS = Segment.Type == PT_TLS; + if (SectionIsTLS != SegmentIsTLS) + return false; + + return Segment.VAddr <= Section.Addr && + Segment.VAddr + Segment.MemSize >= Section.Addr + SecSize; + } + return Segment.Offset <= Section.OriginalOffset && Segment.Offset + Segment.FileSize >= Section.OriginalOffset + SecSize; } @@ -781,7 +1107,7 @@ static bool compareSegmentsByPAddr(const Segment *A, const Segment *B) { return A->Index < B->Index; } -void BinaryELFBuilder::initFileHeader() { +void BasicELFBuilder::initFileHeader() { Obj->Flags = 0x0; Obj->Type = ET_REL; Obj->OSABI = ELFOSABI_NONE; @@ -791,9 +1117,9 @@ void BinaryELFBuilder::initFileHeader() { Obj->Version = 1; } -void BinaryELFBuilder::initHeaderSegment() { Obj->ElfHdrSegment.Index = 0; } +void BasicELFBuilder::initHeaderSegment() { Obj->ElfHdrSegment.Index = 0; } -StringTableSection *BinaryELFBuilder::addStrTab() { +StringTableSection *BasicELFBuilder::addStrTab() { auto &StrTab = Obj->addSection<StringTableSection>(); StrTab.Name = ".strtab"; @@ -801,7 +1127,7 @@ StringTableSection *BinaryELFBuilder::addStrTab() { return &StrTab; } -SymbolTableSection *BinaryELFBuilder::addSymTab(StringTableSection *StrTab) { +SymbolTableSection *BasicELFBuilder::addSymTab(StringTableSection *StrTab) { auto &SymTab = Obj->addSection<SymbolTableSection>(); SymTab.Name = ".symtab"; @@ -814,6 +1140,11 @@ SymbolTableSection *BinaryELFBuilder::addSymTab(StringTableSection *StrTab) { return &SymTab; } +void BasicELFBuilder::initSections() { + for (auto &Section : Obj->sections()) + Section.initialize(Obj->sections()); +} + void BinaryELFBuilder::addData(SymbolTableSection *SymTab) { auto Data = ArrayRef<uint8_t>( reinterpret_cast<const uint8_t *>(MemBuf->getBufferStart()), @@ -837,25 +1168,75 @@ void BinaryELFBuilder::addData(SymbolTableSection *SymTab) { /*Value=*/DataSection.Size, STV_DEFAULT, SHN_ABS, 0); } -void BinaryELFBuilder::initSections() { - for (auto &Section : Obj->sections()) { - Section.initialize(Obj->sections()); +std::unique_ptr<Object> BinaryELFBuilder::build() { + initFileHeader(); + initHeaderSegment(); + + SymbolTableSection *SymTab = addSymTab(addStrTab()); + initSections(); + addData(SymTab); + + return std::move(Obj); +} + +// Adds sections from IHEX data file. Data should have been +// fully validated by this time. +void IHexELFBuilder::addDataSections() { + OwnedDataSection *Section = nullptr; + uint64_t SegmentAddr = 0, BaseAddr = 0; + uint32_t SecNo = 1; + + for (const IHexRecord &R : Records) { + uint64_t RecAddr; + switch (R.Type) { + case IHexRecord::Data: + // Ignore empty data records + if (R.HexData.empty()) + continue; + RecAddr = R.Addr + SegmentAddr + BaseAddr; + if (!Section || Section->Addr + Section->Size != RecAddr) + // OriginalOffset field is only used to sort section properly, so + // instead of keeping track of real offset in IHEX file, we use + // section number. + Section = &Obj->addSection<OwnedDataSection>( + ".sec" + std::to_string(SecNo++), RecAddr, + ELF::SHF_ALLOC | ELF::SHF_WRITE, SecNo); + Section->appendHexData(R.HexData); + break; + case IHexRecord::EndOfFile: + break; + case IHexRecord::SegmentAddr: + // 20-bit segment address. + SegmentAddr = checkedGetHex<uint16_t>(R.HexData) << 4; + break; + case IHexRecord::StartAddr80x86: + case IHexRecord::StartAddr: + Obj->Entry = checkedGetHex<uint32_t>(R.HexData); + assert(Obj->Entry <= 0xFFFFFU); + break; + case IHexRecord::ExtendedAddr: + // 16-31 bits of linear base address + BaseAddr = checkedGetHex<uint16_t>(R.HexData) << 16; + break; + default: + llvm_unreachable("unknown record type"); + } } } -std::unique_ptr<Object> BinaryELFBuilder::build() { +std::unique_ptr<Object> IHexELFBuilder::build() { initFileHeader(); initHeaderSegment(); StringTableSection *StrTab = addStrTab(); - SymbolTableSection *SymTab = addSymTab(StrTab); + addSymTab(StrTab); initSections(); - addData(SymTab); + addDataSections(); return std::move(Obj); } template <class ELFT> void ELFBuilder<ELFT>::setParentSegment(Segment &Child) { - for (auto &Parent : Obj.segments()) { + for (Segment &Parent : Obj.segments()) { // Every segment will overlap with itself but we don't want a segment to // be it's own parent so we avoid that situation. if (&Child != &Parent && segmentOverlapsSegment(Child, Parent)) { @@ -870,23 +1251,43 @@ template <class ELFT> void ELFBuilder<ELFT>::setParentSegment(Segment &Child) { } } -template <class ELFT> void ELFBuilder<ELFT>::readProgramHeaders() { +template <class ELFT> void ELFBuilder<ELFT>::findEhdrOffset() { + if (!ExtractPartition) + return; + + for (const SectionBase &Section : Obj.sections()) { + if (Section.Type == SHT_LLVM_PART_EHDR && + Section.Name == *ExtractPartition) { + EhdrOffset = Section.Offset; + return; + } + } + error("could not find partition named '" + *ExtractPartition + "'"); +} + +template <class ELFT> +void ELFBuilder<ELFT>::readProgramHeaders(const ELFFile<ELFT> &HeadersFile) { uint32_t Index = 0; - for (const auto &Phdr : unwrapOrError(ElfFile.program_headers())) { - ArrayRef<uint8_t> Data{ElfFile.base() + Phdr.p_offset, + for (const auto &Phdr : unwrapOrError(HeadersFile.program_headers())) { + if (Phdr.p_offset + Phdr.p_filesz > HeadersFile.getBufSize()) + error("program header with offset 0x" + Twine::utohexstr(Phdr.p_offset) + + " and file size 0x" + Twine::utohexstr(Phdr.p_filesz) + + " goes past the end of the file"); + + ArrayRef<uint8_t> Data{HeadersFile.base() + Phdr.p_offset, (size_t)Phdr.p_filesz}; Segment &Seg = Obj.addSegment(Data); Seg.Type = Phdr.p_type; Seg.Flags = Phdr.p_flags; - Seg.OriginalOffset = Phdr.p_offset; - Seg.Offset = Phdr.p_offset; + Seg.OriginalOffset = Phdr.p_offset + EhdrOffset; + Seg.Offset = Phdr.p_offset + EhdrOffset; Seg.VAddr = Phdr.p_vaddr; Seg.PAddr = Phdr.p_paddr; Seg.FileSize = Phdr.p_filesz; Seg.MemSize = Phdr.p_memsz; Seg.Align = Phdr.p_align; Seg.Index = Index++; - for (auto &Section : Obj.sections()) { + for (SectionBase &Section : Obj.sections()) { if (sectionWithinSegment(Section, Seg)) { Seg.addSection(&Section); if (!Section.ParentSegment || @@ -899,8 +1300,9 @@ template <class ELFT> void ELFBuilder<ELFT>::readProgramHeaders() { auto &ElfHdr = Obj.ElfHdrSegment; ElfHdr.Index = Index++; + ElfHdr.OriginalOffset = ElfHdr.Offset = EhdrOffset; - const auto &Ehdr = *ElfFile.getHeader(); + const auto &Ehdr = *HeadersFile.getHeader(); auto &PrHdr = Obj.ProgramHdrSegment; PrHdr.Type = PT_PHDR; PrHdr.Flags = 0; @@ -908,7 +1310,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readProgramHeaders() { // Whereas this works automatically for ElfHdr, here OriginalOffset is // always non-zero and to ensure the equation we assign the same value to // VAddr as well. - PrHdr.OriginalOffset = PrHdr.Offset = PrHdr.VAddr = Ehdr.e_phoff; + PrHdr.OriginalOffset = PrHdr.Offset = PrHdr.VAddr = EhdrOffset + Ehdr.e_phoff; PrHdr.PAddr = 0; PrHdr.FileSize = PrHdr.MemSize = Ehdr.e_phentsize * Ehdr.e_phnum; // The spec requires us to naturally align all the fields. @@ -917,7 +1319,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readProgramHeaders() { // Now we do an O(n^2) loop through the segments in order to match up // segments. - for (auto &Child : Obj.segments()) + for (Segment &Child : Obj.segments()) setParentSegment(Child); setParentSegment(ElfHdr); setParentSegment(PrHdr); @@ -925,22 +1327,25 @@ template <class ELFT> void ELFBuilder<ELFT>::readProgramHeaders() { template <class ELFT> void ELFBuilder<ELFT>::initGroupSection(GroupSection *GroupSec) { - auto SecTable = Obj.sections(); + if (GroupSec->Align % sizeof(ELF::Elf32_Word) != 0) + error("invalid alignment " + Twine(GroupSec->Align) + " of group section '" + + GroupSec->Name + "'"); + SectionTableRef SecTable = Obj.sections(); auto SymTab = SecTable.template getSectionOfType<SymbolTableSection>( GroupSec->Link, - "Link field value " + Twine(GroupSec->Link) + " in section " + - GroupSec->Name + " is invalid", - "Link field value " + Twine(GroupSec->Link) + " in section " + - GroupSec->Name + " is not a symbol table"); - auto Sym = SymTab->getSymbolByIndex(GroupSec->Info); + "link field value '" + Twine(GroupSec->Link) + "' in section '" + + GroupSec->Name + "' is invalid", + "link field value '" + Twine(GroupSec->Link) + "' in section '" + + GroupSec->Name + "' is not a symbol table"); + Symbol *Sym = SymTab->getSymbolByIndex(GroupSec->Info); if (!Sym) - error("Info field value " + Twine(GroupSec->Info) + " in section " + - GroupSec->Name + " is not a valid symbol index"); + error("info field value '" + Twine(GroupSec->Info) + "' in section '" + + GroupSec->Name + "' is not a valid symbol index"); GroupSec->setSymTab(SymTab); GroupSec->setSymbol(Sym); if (GroupSec->Contents.size() % sizeof(ELF::Elf32_Word) || GroupSec->Contents.empty()) - error("The content of the section " + GroupSec->Name + " is malformed"); + error("the content of the section " + GroupSec->Name + " is malformed"); const ELF::Elf32_Word *Word = reinterpret_cast<const ELF::Elf32_Word *>(GroupSec->Contents.data()); const ELF::Elf32_Word *End = @@ -949,8 +1354,8 @@ void ELFBuilder<ELFT>::initGroupSection(GroupSection *GroupSec) { for (; Word != End; ++Word) { uint32_t Index = support::endian::read32<ELFT::TargetEndianness>(Word); GroupSec->addMember(SecTable.getSection( - Index, "Group member index " + Twine(Index) + " in section " + - GroupSec->Name + " is invalid")); + Index, "group member index " + Twine(Index) + " in section '" + + GroupSec->Name + "' is invalid")); } } @@ -967,31 +1372,31 @@ void ELFBuilder<ELFT>::initSymbolTable(SymbolTableSection *SymTab) { if (Sym.st_shndx == SHN_XINDEX) { if (SymTab->getShndxTable() == nullptr) - error("Symbol '" + Name + - "' has index SHN_XINDEX but no SHT_SYMTAB_SHNDX section exists."); + error("symbol '" + Name + + "' has index SHN_XINDEX but no SHT_SYMTAB_SHNDX section exists"); if (ShndxData.data() == nullptr) { const Elf_Shdr &ShndxSec = *unwrapOrError(ElfFile.getSection(SymTab->getShndxTable()->Index)); ShndxData = unwrapOrError( ElfFile.template getSectionContentsAsArray<Elf_Word>(&ShndxSec)); if (ShndxData.size() != Symbols.size()) - error("Symbol section index table does not have the same number of " - "entries as the symbol table."); + error("symbol section index table does not have the same number of " + "entries as the symbol table"); } Elf_Word Index = ShndxData[&Sym - Symbols.begin()]; DefSection = Obj.sections().getSection( Index, - "Symbol '" + Name + "' has invalid section index " + Twine(Index)); + "symbol '" + Name + "' has invalid section index " + Twine(Index)); } else if (Sym.st_shndx >= SHN_LORESERVE) { if (!isValidReservedSectionIndex(Sym.st_shndx, Obj.Machine)) { error( - "Symbol '" + Name + + "symbol '" + Name + "' has unsupported value greater than or equal to SHN_LORESERVE: " + Twine(Sym.st_shndx)); } } else if (Sym.st_shndx != SHN_UNDEF) { DefSection = Obj.sections().getSection( - Sym.st_shndx, "Symbol '" + Name + + Sym.st_shndx, "symbol '" + Name + "' is defined has invalid section index " + Twine(Sym.st_shndx)); } @@ -1086,7 +1491,8 @@ SectionBase &ELFBuilder<ELFT>::makeSection(const Elf_Shdr &Shdr) { default: { Data = unwrapOrError(ElfFile.getSectionContents(&Shdr)); - if (isDataGnuCompressed(Data) || (Shdr.sh_flags & ELF::SHF_COMPRESSED)) { + StringRef Name = unwrapOrError(ElfFile.getSectionName(&Shdr)); + if (Name.startswith(".zdebug") || (Shdr.sh_flags & ELF::SHF_COMPRESSED)) { uint64_t DecompressedSize, DecompressedAlign; std::tie(DecompressedSize, DecompressedAlign) = getDecompressedSizeAndAlignment<ELFT>(Data); @@ -1123,7 +1529,9 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { ArrayRef<uint8_t>(ElfFile.base() + Shdr.sh_offset, (Shdr.sh_type == SHT_NOBITS) ? 0 : Shdr.sh_size); } +} +template <class ELFT> void ELFBuilder<ELFT>::readSections() { // If a section index table exists we'll need to initialize it before we // initialize the symbol table because the symbol table might need to // reference it. @@ -1157,11 +1565,34 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { initGroupSection(GroupSec); } } + + uint32_t ShstrIndex = ElfFile.getHeader()->e_shstrndx; + if (ShstrIndex == SHN_XINDEX) + ShstrIndex = unwrapOrError(ElfFile.getSection(0))->sh_link; + + if (ShstrIndex == SHN_UNDEF) + Obj.HadShdrs = false; + else + Obj.SectionNames = + Obj.sections().template getSectionOfType<StringTableSection>( + ShstrIndex, + "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " + + " is invalid", + "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " + + " is not a string table"); } template <class ELFT> void ELFBuilder<ELFT>::build() { - const auto &Ehdr = *ElfFile.getHeader(); + readSectionHeaders(); + findEhdrOffset(); + + // The ELFFile whose ELF headers and program headers are copied into the + // output file. Normally the same as ElfFile, but if we're extracting a + // loadable partition it will point to the partition's headers. + ELFFile<ELFT> HeadersFile = unwrapOrError(ELFFile<ELFT>::create(toStringRef( + {ElfFile.base() + EhdrOffset, ElfFile.getBufSize() - EhdrOffset}))); + auto &Ehdr = *HeadersFile.getHeader(); Obj.OSABI = Ehdr.e_ident[EI_OSABI]; Obj.ABIVersion = Ehdr.e_ident[EI_ABIVERSION]; Obj.Type = Ehdr.e_type; @@ -1170,25 +1601,8 @@ template <class ELFT> void ELFBuilder<ELFT>::build() { Obj.Entry = Ehdr.e_entry; Obj.Flags = Ehdr.e_flags; - readSectionHeaders(); - readProgramHeaders(); - - uint32_t ShstrIndex = Ehdr.e_shstrndx; - if (ShstrIndex == SHN_XINDEX) - ShstrIndex = unwrapOrError(ElfFile.getSection(0))->sh_link; - - Obj.SectionNames = - Obj.sections().template getSectionOfType<StringTableSection>( - ShstrIndex, - "e_shstrndx field value " + Twine(Ehdr.e_shstrndx) + - " in elf header " + " is invalid", - "e_shstrndx field value " + Twine(Ehdr.e_shstrndx) + - " in elf header " + " is not a string table"); -} - -// A generic size function which computes sizes of any random access range. -template <class R> size_t size(R &&Range) { - return static_cast<size_t>(std::end(Range) - std::begin(Range)); + readSections(); + readProgramHeaders(HeadersFile); } Writer::~Writer() {} @@ -1199,31 +1613,61 @@ std::unique_ptr<Object> BinaryReader::create() const { return BinaryELFBuilder(MInfo.EMachine, MemBuf).build(); } +Expected<std::vector<IHexRecord>> IHexReader::parse() const { + SmallVector<StringRef, 16> Lines; + std::vector<IHexRecord> Records; + bool HasSections = false; + + MemBuf->getBuffer().split(Lines, '\n'); + Records.reserve(Lines.size()); + for (size_t LineNo = 1; LineNo <= Lines.size(); ++LineNo) { + StringRef Line = Lines[LineNo - 1].trim(); + if (Line.empty()) + continue; + + Expected<IHexRecord> R = IHexRecord::parse(Line); + if (!R) + return parseError(LineNo, R.takeError()); + if (R->Type == IHexRecord::EndOfFile) + break; + HasSections |= (R->Type == IHexRecord::Data); + Records.push_back(*R); + } + if (!HasSections) + return parseError(-1U, "no sections"); + + return std::move(Records); +} + +std::unique_ptr<Object> IHexReader::create() const { + std::vector<IHexRecord> Records = unwrapOrError(parse()); + return IHexELFBuilder(Records).build(); +} + std::unique_ptr<Object> ELFReader::create() const { auto Obj = llvm::make_unique<Object>(); if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { - ELFBuilder<ELF32LE> Builder(*O, *Obj); + ELFBuilder<ELF32LE> Builder(*O, *Obj, ExtractPartition); Builder.build(); return Obj; } else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { - ELFBuilder<ELF64LE> Builder(*O, *Obj); + ELFBuilder<ELF64LE> Builder(*O, *Obj, ExtractPartition); Builder.build(); return Obj; } else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { - ELFBuilder<ELF32BE> Builder(*O, *Obj); + ELFBuilder<ELF32BE> Builder(*O, *Obj, ExtractPartition); Builder.build(); return Obj; } else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { - ELFBuilder<ELF64BE> Builder(*O, *Obj); + ELFBuilder<ELF64BE> Builder(*O, *Obj, ExtractPartition); Builder.build(); return Obj; } - error("Invalid file type"); + error("invalid file type"); } template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { - uint8_t *B = Buf.getBufferStart(); - Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(B); + 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'; @@ -1247,7 +1691,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { Ehdr.e_phentsize = (Ehdr.e_phnum != 0) ? sizeof(Elf_Phdr) : 0; Ehdr.e_flags = Obj.Flags; Ehdr.e_ehsize = sizeof(Elf_Ehdr); - if (WriteSectionHeaders && size(Obj.sections()) != 0) { + if (WriteSectionHeaders && Obj.sections().size() != 0) { Ehdr.e_shentsize = sizeof(Elf_Shdr); Ehdr.e_shoff = Obj.SHOffset; // """ @@ -1256,7 +1700,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { // number of section header table entries is contained in the sh_size field // of the section header at index 0. // """ - auto Shnum = size(Obj.sections()) + 1; + auto Shnum = Obj.sections().size() + 1; if (Shnum >= SHN_LORESERVE) Ehdr.e_shnum = 0; else @@ -1285,17 +1729,17 @@ template <class ELFT> void ELFWriter<ELFT>::writePhdrs() { } template <class ELFT> void ELFWriter<ELFT>::writeShdrs() { - uint8_t *B = Buf.getBufferStart() + Obj.SHOffset; // 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 *>(B); + Elf_Shdr &Shdr = + *reinterpret_cast<Elf_Shdr *>(Buf.getBufferStart() + Obj.SHOffset); Shdr.sh_name = 0; Shdr.sh_type = SHT_NULL; Shdr.sh_flags = 0; Shdr.sh_addr = 0; Shdr.sh_offset = 0; // See writeEhdr for why we do this. - uint64_t Shnum = size(Obj.sections()) + 1; + uint64_t Shnum = Obj.sections().size() + 1; if (Shnum >= SHN_LORESERVE) Shdr.sh_size = Shnum; else @@ -1309,16 +1753,44 @@ template <class ELFT> void ELFWriter<ELFT>::writeShdrs() { Shdr.sh_addralign = 0; Shdr.sh_entsize = 0; - for (auto &Sec : Obj.sections()) + for (SectionBase &Sec : Obj.sections()) writeShdr(Sec); } template <class ELFT> void ELFWriter<ELFT>::writeSectionData() { - for (auto &Sec : Obj.sections()) - Sec.accept(*SecWriter); + for (SectionBase &Sec : Obj.sections()) + // Segments are responsible for writing their contents, so only write the + // section data if the section is not in a segment. Note that this renders + // sections in segments effectively immutable. + if (Sec.ParentSegment == nullptr) + Sec.accept(*SecWriter); +} + +template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() { + for (Segment &Seg : Obj.segments()) { + uint8_t *B = Buf.getBufferStart() + Seg.Offset; + assert(Seg.FileSize == Seg.getContents().size() && + "Segment size must match contents size"); + std::memcpy(B, Seg.getContents().data(), Seg.FileSize); + } + + // Iterate over removed sections and overwrite their old data with zeroes. + for (auto &Sec : Obj.removedSections()) { + Segment *Parent = Sec.ParentSegment; + if (Parent == nullptr || Sec.Type == SHT_NOBITS || Sec.Size == 0) + continue; + uint64_t Offset = + Sec.OriginalOffset - Parent->OriginalOffset + Parent->Offset; + std::memset(Buf.getBufferStart() + Offset, 0, Sec.Size); + } } -void Object::removeSections(std::function<bool(const SectionBase &)> ToRemove) { +template <class ELFT> +ELFWriter<ELFT>::ELFWriter(Object &Obj, Buffer &Buf, bool WSH) + : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs) {} + +Error Object::removeSections(bool AllowBrokenLinks, + std::function<bool(const SectionBase &)> ToRemove) { auto Iter = std::stable_partition( std::begin(Sections), std::end(Sections), [=](const SecPtr &Sec) { @@ -1339,32 +1811,55 @@ void Object::removeSections(std::function<bool(const SectionBase &)> ToRemove) { // Now make sure there are no remaining references to the sections that will // be removed. Sometimes it is impossible to remove a reference so we emit // an error here instead. + std::unordered_set<const SectionBase *> RemoveSections; + RemoveSections.reserve(std::distance(Iter, std::end(Sections))); for (auto &RemoveSec : make_range(Iter, std::end(Sections))) { for (auto &Segment : Segments) Segment->removeSection(RemoveSec.get()); - for (auto &KeepSec : make_range(std::begin(Sections), Iter)) - KeepSec->removeSectionReferences(RemoveSec.get()); + RemoveSections.insert(RemoveSec.get()); } - // Now finally get rid of them all togethor. + + // For each section that remains alive, we want to remove the dead references. + // This either might update the content of the section (e.g. remove symbols + // from symbol table that belongs to removed section) or trigger an error if + // a live section critically depends on a section being removed somehow + // (e.g. the removed section is referenced by a relocation). + for (auto &KeepSec : make_range(std::begin(Sections), Iter)) { + if (Error E = KeepSec->removeSectionReferences(AllowBrokenLinks, + [&RemoveSections](const SectionBase *Sec) { + return RemoveSections.find(Sec) != RemoveSections.end(); + })) + return E; + } + + // Transfer removed sections into the Object RemovedSections container for use + // later. + std::move(Iter, Sections.end(), std::back_inserter(RemovedSections)); + // Now finally get rid of them all together. Sections.erase(Iter, std::end(Sections)); + return Error::success(); } -void Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { - if (!SymbolTable) - return; - - for (const SecPtr &Sec : Sections) - Sec->removeSymbols(ToRemove); +Error Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { + if (SymbolTable) + for (const SecPtr &Sec : Sections) + if (Error E = Sec->removeSymbols(ToRemove)) + return E; + return Error::success(); } void Object::sortSections() { - // Put all sections in offset order. Maintain the ordering as closely as - // possible while meeting that demand however. - auto CompareSections = [](const SecPtr &A, const SecPtr &B) { + // Use stable_sort to maintain the original ordering as closely as possible. + llvm::stable_sort(Sections, [](const SecPtr &A, const SecPtr &B) { + // Put SHT_GROUP sections first, since group section headers must come + // before the sections they contain. This also matches what GNU objcopy + // does. + if (A->Type != B->Type && + (A->Type == ELF::SHT_GROUP || B->Type == ELF::SHT_GROUP)) + return A->Type == ELF::SHT_GROUP; + // For all other sections, sort by offset order. return A->OriginalOffset < B->OriginalOffset; - }; - std::stable_sort(std::begin(this->Sections), std::end(this->Sections), - CompareSections); + }); } static uint64_t alignToAddr(uint64_t Offset, uint64_t Addr, uint64_t Align) { @@ -1382,14 +1877,13 @@ static uint64_t alignToAddr(uint64_t Offset, uint64_t Addr, uint64_t Align) { // Orders segments such that if x = y->ParentSegment then y comes before x. static void orderSegments(std::vector<Segment *> &Segments) { - std::stable_sort(std::begin(Segments), std::end(Segments), - compareSegmentsByOffset); + llvm::stable_sort(Segments, compareSegmentsByOffset); } // This function finds a consistent layout for a list of segments starting from // an Offset. It assumes that Segments have been sorted by OrderSegments and // returns an Offset one past the end of the last segment. -static uint64_t LayoutSegments(std::vector<Segment *> &Segments, +static uint64_t layoutSegments(std::vector<Segment *> &Segments, uint64_t Offset) { assert(std::is_sorted(std::begin(Segments), std::end(Segments), compareSegmentsByOffset)); @@ -1398,20 +1892,20 @@ static uint64_t LayoutSegments(std::vector<Segment *> &Segments, // then it's acceptable, but not ideal, to simply move it to after the // segments. So we can simply layout segments one after the other accounting // for alignment. - for (auto &Segment : Segments) { + for (Segment *Seg : Segments) { // We assume that segments have been ordered by OriginalOffset and Index // such that a parent segment will always come before a child segment in // OrderedSegments. This means that the Offset of the ParentSegment should // already be set and we can set our offset relative to it. - if (Segment->ParentSegment != nullptr) { - auto Parent = Segment->ParentSegment; - Segment->Offset = - Parent->Offset + Segment->OriginalOffset - Parent->OriginalOffset; + if (Seg->ParentSegment != nullptr) { + Segment *Parent = Seg->ParentSegment; + Seg->Offset = + Parent->Offset + Seg->OriginalOffset - Parent->OriginalOffset; } else { - Offset = alignToAddr(Offset, Segment->VAddr, Segment->Align); - Segment->Offset = Offset; + Offset = alignToAddr(Offset, Seg->VAddr, Seg->Align); + Seg->Offset = Offset; } - Offset = std::max(Offset, Segment->Offset + Segment->FileSize); + Offset = std::max(Offset, Seg->Offset + Seg->FileSize); } return Offset; } @@ -1448,10 +1942,9 @@ static uint64_t layoutSections(Range Sections, uint64_t Offset) { } template <class ELFT> void ELFWriter<ELFT>::initEhdrSegment() { - auto &ElfHdr = Obj.ElfHdrSegment; + Segment &ElfHdr = Obj.ElfHdrSegment; ElfHdr.Type = PT_PHDR; ElfHdr.Flags = 0; - ElfHdr.OriginalOffset = ElfHdr.Offset = 0; ElfHdr.VAddr = 0; ElfHdr.PAddr = 0; ElfHdr.FileSize = ElfHdr.MemSize = sizeof(Elf_Ehdr); @@ -1463,7 +1956,7 @@ template <class ELFT> void ELFWriter<ELFT>::assignOffsets() { // so that we know that anytime ->ParentSegment is set that segment has // already had its offset properly set. std::vector<Segment *> OrderedSegments; - for (auto &Segment : Obj.segments()) + for (Segment &Segment : Obj.segments()) OrderedSegments.push_back(&Segment); OrderedSegments.push_back(&Obj.ElfHdrSegment); OrderedSegments.push_back(&Obj.ProgramHdrSegment); @@ -1472,7 +1965,7 @@ template <class ELFT> void ELFWriter<ELFT>::assignOffsets() { // Since the ELF Header (ElfHdrSegment) must be at the start of the file, // we start at offset 0. uint64_t Offset = 0; - Offset = LayoutSegments(OrderedSegments, Offset); + Offset = layoutSegments(OrderedSegments, Offset); Offset = layoutSections(Obj.sections(), Offset); // If we need to write the section header table out then we need to align the // Offset so that SHOffset is valid. @@ -1484,28 +1977,32 @@ template <class ELFT> void ELFWriter<ELFT>::assignOffsets() { template <class ELFT> size_t ELFWriter<ELFT>::totalSize() const { // We already have the section header offset so we can calculate the total // size by just adding up the size of each section header. - auto NullSectionSize = WriteSectionHeaders ? sizeof(Elf_Shdr) : 0; - return Obj.SHOffset + size(Obj.sections()) * sizeof(Elf_Shdr) + - NullSectionSize; + if (!WriteSectionHeaders) + return Obj.SHOffset; + size_t ShdrCount = Obj.sections().size() + 1; // Includes null shdr. + return Obj.SHOffset + ShdrCount * sizeof(Elf_Shdr); } -template <class ELFT> void ELFWriter<ELFT>::write() { +template <class ELFT> Error ELFWriter<ELFT>::write() { + // Segment data must be written first, so that the ELF header and program + // header tables can overwrite it, if covered by a segment. + writeSegmentData(); writeEhdr(); writePhdrs(); writeSectionData(); if (WriteSectionHeaders) writeShdrs(); - if (auto E = Buf.commit()) - reportError(Buf.getName(), errorToErrorCode(std::move(E))); + return Buf.commit(); } -template <class ELFT> void ELFWriter<ELFT>::finalize() { +template <class ELFT> Error ELFWriter<ELFT>::finalize() { // It could happen that SectionNames has been removed and yet the user wants // a section header table output. We need to throw an error if a user tries // to do that. if (Obj.SectionNames == nullptr && WriteSectionHeaders) - error("Cannot write section header table because section header string " - "table was removed."); + return createStringError(llvm::errc::invalid_argument, + "cannot write section header table because " + "section header string table was removed"); Obj.sortSections(); @@ -1513,8 +2010,8 @@ template <class ELFT> void ELFWriter<ELFT>::finalize() { // if we need large indexes or not. We can assign indexes first and check as // we go to see if we will actully need large indexes. bool NeedsLargeIndexes = false; - if (size(Obj.sections()) >= SHN_LORESERVE) { - auto Sections = Obj.sections(); + if (Obj.sections().size() >= SHN_LORESERVE) { + SectionTableRef Sections = Obj.sections(); NeedsLargeIndexes = std::any_of(Sections.begin() + SHN_LORESERVE, Sections.end(), [](const SectionBase &Sec) { return Sec.HasSymbol; }); @@ -1536,9 +2033,12 @@ template <class ELFT> void ELFWriter<ELFT>::finalize() { // Since we don't need SectionIndexTable we should remove it and all // references to it. if (Obj.SectionIndexTable != nullptr) { - Obj.removeSections([this](const SectionBase &Sec) { - return &Sec == Obj.SectionIndexTable; - }); + // We do not support sections referring to the section index table. + if (Error E = Obj.removeSections(false /*AllowBrokenLinks*/, + [this](const SectionBase &Sec) { + return &Sec == Obj.SectionIndexTable; + })) + return E; } } @@ -1567,15 +2067,23 @@ template <class ELFT> void ELFWriter<ELFT>::finalize() { if (Obj.SymbolTable != nullptr) Obj.SymbolTable->prepareForLayout(); + // Now that all strings are added we want to finalize string table builders, + // because that affects section sizes which in turn affects section offsets. + for (SectionBase &Sec : Obj.sections()) + if (auto StrTab = dyn_cast<StringTableSection>(&Sec)) + StrTab->prepareForLayout(); + assignOffsets(); - // Finalize SectionNames first so that we can assign name indexes. - if (Obj.SectionNames != nullptr) - Obj.SectionNames->finalize(); + // layoutSections could have modified section indexes, so we need + // to fill the index table after assignOffsets. + if (Obj.SymbolTable != nullptr) + Obj.SymbolTable->fillShndxTable(); + // Finally now that all offsets and indexes have been set we can finalize any // remaining issues. uint64_t Offset = Obj.SHOffset + sizeof(Elf_Shdr); - for (auto &Section : Obj.sections()) { + for (SectionBase &Section : Obj.sections()) { Section.HeaderOffset = Offset; Offset += sizeof(Elf_Shdr); if (WriteSectionHeaders) @@ -1583,21 +2091,20 @@ template <class ELFT> void ELFWriter<ELFT>::finalize() { Section.finalize(); } - Buf.allocate(totalSize()); + if (Error E = Buf.allocate(totalSize())) + return E; SecWriter = llvm::make_unique<ELFSectionWriter<ELFT>>(Buf); + return Error::success(); } -void BinaryWriter::write() { - for (auto &Section : Obj.sections()) { - if ((Section.Flags & SHF_ALLOC) == 0) - continue; - Section.accept(*SecWriter); - } - if (auto E = Buf.commit()) - reportError(Buf.getName(), errorToErrorCode(std::move(E))); +Error BinaryWriter::write() { + for (auto &Section : Obj.sections()) + if (Section.Flags & SHF_ALLOC) + Section.accept(*SecWriter); + return Buf.commit(); } -void BinaryWriter::finalize() { +Error BinaryWriter::finalize() { // TODO: Create a filter range to construct OrderedSegments from so that this // code can be deduped with assignOffsets above. This should also solve the // todo below for LayoutSections. @@ -1606,11 +2113,9 @@ void BinaryWriter::finalize() { // already had it's offset properly set. We only want to consider the segments // that will affect layout of allocated sections so we only add those. std::vector<Segment *> OrderedSegments; - for (auto &Section : Obj.sections()) { - if ((Section.Flags & SHF_ALLOC) != 0 && Section.ParentSegment != nullptr) { + for (SectionBase &Section : Obj.sections()) + if ((Section.Flags & SHF_ALLOC) != 0 && Section.ParentSegment != nullptr) OrderedSegments.push_back(Section.ParentSegment); - } - } // For binary output, we're going to use physical addresses instead of // virtual addresses, since a binary output is used for cases like ROM @@ -1622,8 +2127,7 @@ void BinaryWriter::finalize() { for (Segment *Seg : OrderedSegments) Seg->PAddr = Seg->VAddr; - std::stable_sort(std::begin(OrderedSegments), std::end(OrderedSegments), - compareSegmentsByPAddr); + llvm::stable_sort(OrderedSegments, compareSegmentsByPAddr); // Because we add a ParentSegment for each section we might have duplicate // segments in OrderedSegments. If there were duplicates then LayoutSegments @@ -1638,8 +2142,8 @@ void BinaryWriter::finalize() { // our layout algorithm to proceed as expected while not writing out the gap // at the start. if (!OrderedSegments.empty()) { - auto Seg = OrderedSegments[0]; - auto Sec = Seg->firstSection(); + Segment *Seg = OrderedSegments[0]; + const SectionBase *Sec = Seg->firstSection(); auto Diff = Sec->OriginalOffset - Seg->OriginalOffset; Seg->OriginalOffset += Diff; // The size needs to be shrunk as well. @@ -1648,7 +2152,7 @@ void BinaryWriter::finalize() { // section. Seg->PAddr += Diff; uint64_t LowestPAddr = Seg->PAddr; - for (auto &Segment : OrderedSegments) { + for (Segment *Segment : OrderedSegments) { Segment->Offset = Segment->PAddr - LowestPAddr; Offset = std::max(Offset, Segment->Offset + Segment->FileSize); } @@ -1659,11 +2163,9 @@ void BinaryWriter::finalize() { // not hold. Then pass such a range to LayoutSections instead of constructing // AllocatedSections here. std::vector<SectionBase *> AllocatedSections; - for (auto &Section : Obj.sections()) { - if ((Section.Flags & SHF_ALLOC) == 0) - continue; - AllocatedSections.push_back(&Section); - } + for (SectionBase &Section : Obj.sections()) + if (Section.Flags & SHF_ALLOC) + AllocatedSections.push_back(&Section); layoutSections(make_pointee_range(AllocatedSections), Offset); // Now that every section has been laid out we just need to compute the total @@ -1671,13 +2173,117 @@ void BinaryWriter::finalize() { // LayoutSections, because we want to truncate the last segment to the end of // its last section, to match GNU objcopy's behaviour. TotalSize = 0; - for (const auto &Section : AllocatedSections) { + for (SectionBase *Section : AllocatedSections) if (Section->Type != SHT_NOBITS) TotalSize = std::max(TotalSize, Section->Offset + Section->Size); - } - Buf.allocate(TotalSize); + if (Error E = Buf.allocate(TotalSize)) + return E; SecWriter = llvm::make_unique<BinarySectionWriter>(Buf); + return Error::success(); +} + +bool IHexWriter::SectionCompare::operator()(const SectionBase *Lhs, + const SectionBase *Rhs) const { + return (sectionPhysicalAddr(Lhs) & 0xFFFFFFFFU) < + (sectionPhysicalAddr(Rhs) & 0xFFFFFFFFU); +} + +uint64_t IHexWriter::writeEntryPointRecord(uint8_t *Buf) { + IHexLineData HexData; + uint8_t Data[4] = {}; + // We don't write entry point record if entry is zero. + if (Obj.Entry == 0) + return 0; + + if (Obj.Entry <= 0xFFFFFU) { + Data[0] = ((Obj.Entry & 0xF0000U) >> 12) & 0xFF; + support::endian::write(&Data[2], static_cast<uint16_t>(Obj.Entry), + support::big); + HexData = IHexRecord::getLine(IHexRecord::StartAddr80x86, 0, Data); + } else { + support::endian::write(Data, static_cast<uint32_t>(Obj.Entry), + support::big); + HexData = IHexRecord::getLine(IHexRecord::StartAddr, 0, Data); + } + memcpy(Buf, HexData.data(), HexData.size()); + return HexData.size(); +} + +uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) { + IHexLineData HexData = IHexRecord::getLine(IHexRecord::EndOfFile, 0, {}); + memcpy(Buf, HexData.data(), HexData.size()); + return HexData.size(); +} + +Error IHexWriter::write() { + IHexSectionWriter Writer(Buf); + // Write sections. + for (const SectionBase *Sec : Sections) + Sec->accept(Writer); + + uint64_t Offset = Writer.getBufferOffset(); + // Write entry point address. + Offset += writeEntryPointRecord(Buf.getBufferStart() + Offset); + // Write EOF. + Offset += writeEndOfFileRecord(Buf.getBufferStart() + Offset); + assert(Offset == TotalSize); + return Buf.commit(); +} + +Error IHexWriter::checkSection(const SectionBase &Sec) { + uint64_t Addr = sectionPhysicalAddr(&Sec); + if (addressOverflows32bit(Addr) || addressOverflows32bit(Addr + Sec.Size - 1)) + return createStringError( + errc::invalid_argument, + "Section '%s' address range [0x%llx, 0x%llx] is not 32 bit", Sec.Name.c_str(), + Addr, Addr + Sec.Size - 1); + return Error::success(); +} + +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.", + 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 (auto &Section : Obj.sections()) + if (ShouldWrite(Section) && IsInPtLoad(Section)) { + UseSegments = true; + break; + } + + for (auto &Section : Obj.sections()) + if (ShouldWrite(Section) && (!UseSegments || IsInPtLoad(Section))) { + if (Error E = checkSection(Section)) + return E; + Sections.insert(&Section); + } + + IHexSectionWriterBase LengthCalc(Buf); + for (const SectionBase *Sec : Sections) + Sec->accept(LengthCalc); + + // We need space to write section records + StartAddress record + // (if start adress is not zero) + EndOfFile record. + TotalSize = LengthCalc.getBufferOffset() + + (Obj.Entry ? IHexRecord::getLineLength(4) : 0) + + IHexRecord::getLineLength(0); + if (Error E = Buf.allocate(TotalSize)) + return E; + return Error::success(); } template class ELFBuilder<ELF64LE>; diff --git a/contrib/llvm/tools/llvm-objcopy/ELF/Object.h b/contrib/llvm/tools/llvm-objcopy/ELF/Object.h index e5730cd543ee..f3df93b9662f 100644 --- a/contrib/llvm/tools/llvm-objcopy/ELF/Object.h +++ b/contrib/llvm/tools/llvm-objcopy/ELF/Object.h @@ -1,9 +1,8 @@ //===- Object.h -------------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -18,8 +17,8 @@ #include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/FileOutputBuffer.h" -#include "llvm/Support/JamCRC.h" #include <cstddef> #include <cstdint> #include <functional> @@ -60,6 +59,7 @@ public: iterator begin() { return iterator(Sections.data()); } iterator end() { return iterator(Sections.data() + Sections.size()); } + size_t size() const { return Sections.size(); } SectionBase *getSection(uint32_t Index, Twine ErrMsg); @@ -108,7 +108,7 @@ protected: Buffer &Out; public: - virtual ~SectionWriter(){}; + virtual ~SectionWriter() = default; void visit(const Section &Sec) override; void visit(const OwnedDataSection &Sec) override; @@ -169,6 +169,8 @@ public: #define MAKE_SEC_WRITER_FRIEND \ friend class SectionWriter; \ + friend class IHexSectionWriterBase; \ + friend class IHexSectionWriter; \ template <class ELFT> friend class ELFSectionWriter; \ template <class ELFT> friend class ELFSectionSizer; @@ -187,6 +189,118 @@ public: explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {} }; +using IHexLineData = SmallVector<char, 64>; + +struct IHexRecord { + // Memory address of the record. + uint16_t Addr; + // Record type (see below). + uint16_t Type; + // Record data in hexadecimal form. + StringRef HexData; + + // Helper method to get file length of the record + // including newline character + static size_t getLength(size_t DataSize) { + // :LLAAAATT[DD...DD]CC' + return DataSize * 2 + 11; + } + + // Gets length of line in a file (getLength + CRLF). + static size_t getLineLength(size_t DataSize) { + return getLength(DataSize) + 2; + } + + // Given type, address and data returns line which can + // be written to output file. + static IHexLineData getLine(uint8_t Type, uint16_t Addr, + ArrayRef<uint8_t> Data); + + // Parses the line and returns record if possible. + // Line should be trimmed from whitespace characters. + static Expected<IHexRecord> parse(StringRef Line); + + // Calculates checksum of stringified record representation + // S must NOT contain leading ':' and trailing whitespace + // characters + static uint8_t getChecksum(StringRef S); + + enum Type { + // Contains data and a 16-bit starting address for the data. + // The byte count specifies number of data bytes in the record. + Data = 0, + // Must occur exactly once per file in the last line of the file. + // The data field is empty (thus byte count is 00) and the address + // field is typically 0000. + EndOfFile = 1, + // The data field contains a 16-bit segment base address (thus byte + // count is always 02) compatible with 80x86 real mode addressing. + // The address field (typically 0000) is ignored. The segment address + // from the most recent 02 record is multiplied by 16 and added to each + // subsequent data record address to form the physical starting address + // for the data. This allows addressing up to one megabyte of address + // space. + SegmentAddr = 2, + // or 80x86 processors, specifies the initial content of the CS:IP + // registers. The address field is 0000, the byte count is always 04, + // the first two data bytes are the CS value, the latter two are the + // IP value. + StartAddr80x86 = 3, + // Allows for 32 bit addressing (up to 4GiB). The record's address field + // is ignored (typically 0000) and its byte count is always 02. The two + // data bytes (big endian) specify the upper 16 bits of the 32 bit + // absolute address for all subsequent type 00 records + ExtendedAddr = 4, + // The address field is 0000 (not used) and the byte count is always 04. + // The four data bytes represent a 32-bit address value. In the case of + // 80386 and higher CPUs, this address is loaded into the EIP register. + StartAddr = 5, + // We have no other valid types + InvalidType = 6 + }; +}; + +// Base class for IHexSectionWriter. This class implements writing algorithm, +// but doesn't actually write records. It is used for output buffer size +// calculation in IHexWriter::finalize. +class IHexSectionWriterBase : public BinarySectionWriter { + // 20-bit segment address + uint32_t SegmentAddr = 0; + // Extended linear address + uint32_t BaseAddr = 0; + + // Write segment address corresponding to 'Addr' + uint64_t writeSegmentAddr(uint64_t Addr); + // Write extended linear (base) address corresponding to 'Addr' + uint64_t writeBaseAddr(uint64_t Addr); + +protected: + // Offset in the output buffer + uint64_t Offset = 0; + + void writeSection(const SectionBase *Sec, ArrayRef<uint8_t> Data); + virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data); + +public: + explicit IHexSectionWriterBase(Buffer &Buf) : BinarySectionWriter(Buf) {} + + uint64_t getBufferOffset() const { return Offset; } + void visit(const Section &Sec) final; + void visit(const OwnedDataSection &Sec) final; + void visit(const StringTableSection &Sec) override; + void visit(const DynamicRelocationSection &Sec) final; + using BinarySectionWriter::visit; +}; + +// Real IHEX section writer +class IHexSectionWriter : public IHexSectionWriterBase { +public: + IHexSectionWriter(Buffer &Buf) : IHexSectionWriterBase(Buf) {} + + void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data) override; + void visit(const StringTableSection &Sec) override; +}; + class Writer { protected: Object &Obj; @@ -194,8 +308,8 @@ protected: public: virtual ~Writer(); - virtual void finalize() = 0; - virtual void write() = 0; + virtual Error finalize() = 0; + virtual Error write() = 0; Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {} }; @@ -216,6 +330,7 @@ private: void writePhdrs(); void writeShdrs(); void writeSectionData(); + void writeSegmentData(); void assignOffsets(); @@ -225,12 +340,11 @@ private: public: virtual ~ELFWriter() {} - bool WriteSectionHeaders = true; + bool WriteSectionHeaders; - void finalize() override; - void write() override; - ELFWriter(Object &Obj, Buffer &Buf, bool WSH) - : Writer(Obj, Buf), WriteSectionHeaders(WSH) {} + Error finalize() override; + Error write() override; + ELFWriter(Object &Obj, Buffer &Buf, bool WSH); }; class BinaryWriter : public Writer { @@ -241,11 +355,30 @@ private: public: ~BinaryWriter() {} - void finalize() override; - void write() override; + Error finalize() override; + Error write() override; BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} }; +class IHexWriter : public Writer { + struct SectionCompare { + bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const; + }; + + std::set<const SectionBase *, SectionCompare> Sections; + size_t TotalSize; + + Error checkSection(const SectionBase &Sec); + uint64_t writeEntryPointRecord(uint8_t *Buf); + uint64_t writeEndOfFileRecord(uint8_t *Buf); + +public: + ~IHexWriter() {} + Error finalize() override; + Error write() override; + IHexWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {} +}; + class SectionBase { public: std::string Name; @@ -274,11 +407,16 @@ public: virtual void initialize(SectionTableRef SecTable); virtual void finalize(); - virtual void removeSectionReferences(const SectionBase *Sec); - virtual void removeSymbols(function_ref<bool(const Symbol &)> ToRemove); + // Remove references to these sections. The list of sections must be sorted. + virtual Error + removeSectionReferences(bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove); + virtual Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove); virtual void accept(SectionVisitor &Visitor) const = 0; virtual void accept(MutableSectionVisitor &Visitor) = 0; virtual void markSymbols(); + virtual void + replaceSectionReferences(const DenseMap<SectionBase *, SectionBase *> &); }; class Segment { @@ -322,6 +460,8 @@ public: void removeSection(const SectionBase *Sec) { Sections.erase(Sec); } void addSection(const SectionBase *Sec) { Sections.insert(Sec); } + + ArrayRef<uint8_t> getContents() const { return Contents; } }; class Section : public SectionBase { @@ -335,7 +475,8 @@ public: void accept(SectionVisitor &Visitor) const override; void accept(MutableSectionVisitor &Visitor) override; - void removeSectionReferences(const SectionBase *Sec) override; + Error removeSectionReferences(bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove) override; void initialize(SectionTableRef SecTable) override; void finalize() override; }; @@ -354,6 +495,16 @@ public: OriginalOffset = std::numeric_limits<uint64_t>::max(); } + OwnedDataSection(const Twine &SecName, uint64_t SecAddr, uint64_t SecFlags, + uint64_t SecOff) { + Name = SecName.str(); + Type = ELF::SHT_PROGBITS; + Addr = SecAddr; + Flags = SecFlags; + OriginalOffset = SecOff; + } + + void appendHexData(StringRef HexData); void accept(SectionVisitor &Sec) const override; void accept(MutableSectionVisitor &Visitor) override; }; @@ -421,7 +572,7 @@ public: void addString(StringRef Name); uint32_t findIndex(StringRef Name) const; - void finalize() override; + void prepareForLayout(); void accept(SectionVisitor &Visitor) const override; void accept(MutableSectionVisitor &Visitor) override; @@ -440,10 +591,15 @@ enum SymbolShndxType { SYMBOL_SIMPLE_INDEX = 0, SYMBOL_ABS = ELF::SHN_ABS, SYMBOL_COMMON = ELF::SHN_COMMON, + SYMBOL_LOPROC = ELF::SHN_LOPROC, + SYMBOL_AMDGPU_LDS = ELF::SHN_AMDGPU_LDS, SYMBOL_HEXAGON_SCOMMON = ELF::SHN_HEXAGON_SCOMMON, SYMBOL_HEXAGON_SCOMMON_2 = ELF::SHN_HEXAGON_SCOMMON_2, SYMBOL_HEXAGON_SCOMMON_4 = ELF::SHN_HEXAGON_SCOMMON_4, SYMBOL_HEXAGON_SCOMMON_8 = ELF::SHN_HEXAGON_SCOMMON_8, + SYMBOL_HIPROC = ELF::SHN_HIPROC, + SYMBOL_LOOS = ELF::SHN_LOOS, + SYMBOL_HIOS = ELF::SHN_HIOS, SYMBOL_XINDEX = ELF::SHN_XINDEX, }; @@ -474,9 +630,14 @@ private: public: virtual ~SectionIndexSection() {} void addIndex(uint32_t Index) { - Indexes.push_back(Index); - Size += 4; + assert(Size > 0); + Indexes.push_back(Index); } + + void reserve(size_t NumSymbols) { + Indexes.reserve(NumSymbols); + Size = NumSymbols * 4; + } void setSymTab(SymbolTableSection *SymTab) { Symbols = SymTab; } void initialize(SectionTableRef SecTable) override; void finalize() override; @@ -509,7 +670,7 @@ public: void addSymbol(Twine Name, uint8_t Bind, uint8_t Type, SectionBase *DefinedIn, uint64_t Value, uint8_t Visibility, uint16_t Shndx, - uint64_t Size); + uint64_t SymbolSize); void prepareForLayout(); // An 'empty' symbol table still contains a null symbol. bool empty() const { return Symbols.size() == 1; } @@ -517,17 +678,21 @@ public: SectionIndexTable = ShndxTable; } const SectionIndexSection *getShndxTable() const { return SectionIndexTable; } + void fillShndxTable(); const SectionBase *getStrTab() const { return SymbolNames; } const Symbol *getSymbolByIndex(uint32_t Index) const; Symbol *getSymbolByIndex(uint32_t Index); void updateSymbols(function_ref<void(Symbol &)> Callable); - void removeSectionReferences(const SectionBase *Sec) override; + Error removeSectionReferences(bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove) override; void initialize(SectionTableRef SecTable) override; void finalize() override; void accept(SectionVisitor &Visitor) const override; void accept(MutableSectionVisitor &Visitor) override; - void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; + Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; + void replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) override; static bool classof(const SectionBase *S) { return S->Type == ELF::SHT_SYMTAB; @@ -567,14 +732,14 @@ public: // that code between the two symbol table types. template <class SymTabType> class RelocSectionWithSymtabBase : public RelocationSectionBase { - SymTabType *Symbols = nullptr; void setSymTab(SymTabType *SymTab) { Symbols = SymTab; } protected: RelocSectionWithSymtabBase() = default; + SymTabType *Symbols = nullptr; + public: - void removeSectionReferences(const SectionBase *Sec) override; void initialize(SectionTableRef SecTable) override; void finalize() override; }; @@ -589,8 +754,12 @@ public: void addRelocation(Relocation Rel) { Relocations.push_back(Rel); } void accept(SectionVisitor &Visitor) const override; void accept(MutableSectionVisitor &Visitor) override; - void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; + Error removeSectionReferences(bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove) override; + Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; void markSymbols() override; + void replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) override; static bool classof(const SectionBase *S) { if (S->Flags & ELF::SHF_ALLOC) @@ -624,8 +793,10 @@ public: void accept(SectionVisitor &) const override; void accept(MutableSectionVisitor &Visitor) override; void finalize() override; - void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; + Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; void markSymbols() override; + void replaceSectionReferences( + const DenseMap<SectionBase *, SectionBase *> &FromTo) override; static bool classof(const SectionBase *S) { return S->Type == ELF::SHT_GROUP; @@ -662,6 +833,9 @@ public: void accept(SectionVisitor &) const override; void accept(MutableSectionVisitor &Visitor) override; + Error removeSectionReferences( + bool AllowBrokenLinks, + function_ref<bool(const SectionBase *)> ToRemove) override; static bool classof(const SectionBase *S) { if (!(S->Flags & ELF::SHF_ALLOC)) @@ -677,11 +851,11 @@ private: StringRef FileName; uint32_t CRC32; - void init(StringRef File, StringRef Data); + void init(StringRef File); public: // If we add this section from an external source we can use this ctor. - explicit GnuDebugLinkSection(StringRef File); + explicit GnuDebugLinkSection(StringRef File, uint32_t PrecomputedCRC); void accept(SectionVisitor &Visitor) const override; void accept(MutableSectionVisitor &Visitor) override; }; @@ -697,21 +871,41 @@ using object::ELFFile; using object::ELFObjectFile; using object::OwningBinary; -class BinaryELFBuilder { +class BasicELFBuilder { +protected: uint16_t EMachine; - MemoryBuffer *MemBuf; std::unique_ptr<Object> Obj; void initFileHeader(); void initHeaderSegment(); StringTableSection *addStrTab(); SymbolTableSection *addSymTab(StringTableSection *StrTab); - void addData(SymbolTableSection *SymTab); void initSections(); public: + BasicELFBuilder(uint16_t EM) + : EMachine(EM), Obj(llvm::make_unique<Object>()) {} +}; + +class BinaryELFBuilder : public BasicELFBuilder { + MemoryBuffer *MemBuf; + void addData(SymbolTableSection *SymTab); + +public: BinaryELFBuilder(uint16_t EM, MemoryBuffer *MB) - : EMachine(EM), MemBuf(MB), Obj(llvm::make_unique<Object>()) {} + : BasicELFBuilder(EM), MemBuf(MB) {} + + std::unique_ptr<Object> build(); +}; + +class IHexELFBuilder : public BasicELFBuilder { + const std::vector<IHexRecord> &Records; + + void addDataSections(); + +public: + IHexELFBuilder(const std::vector<IHexRecord> &Records) + : BasicELFBuilder(ELF::EM_386), Records(Records) {} std::unique_ptr<Object> build(); }; @@ -724,17 +918,23 @@ private: const ELFFile<ELFT> &ElfFile; Object &Obj; + size_t EhdrOffset = 0; + Optional<StringRef> ExtractPartition; void setParentSegment(Segment &Child); - void readProgramHeaders(); + void readProgramHeaders(const ELFFile<ELFT> &HeadersFile); void initGroupSection(GroupSection *GroupSec); void initSymbolTable(SymbolTableSection *SymTab); void readSectionHeaders(); + void readSections(); + void findEhdrOffset(); SectionBase &makeSection(const Elf_Shdr &Shdr); public: - ELFBuilder(const ELFObjectFile<ELFT> &ElfObj, Object &Obj) - : ElfFile(*ElfObj.getELFFile()), Obj(Obj) {} + ELFBuilder(const ELFObjectFile<ELFT> &ElfObj, Object &Obj, + Optional<StringRef> ExtractPartition) + : ElfFile(*ElfObj.getELFFile()), Obj(Obj), + ExtractPartition(ExtractPartition) {} void build(); }; @@ -749,12 +949,36 @@ public: std::unique_ptr<Object> create() const override; }; +class IHexReader : public Reader { + MemoryBuffer *MemBuf; + + Expected<std::vector<IHexRecord>> parse() const; + Error parseError(size_t LineNo, Error E) const { + return LineNo == -1U + ? createFileError(MemBuf->getBufferIdentifier(), std::move(E)) + : createFileError(MemBuf->getBufferIdentifier(), LineNo, + std::move(E)); + } + template <typename... Ts> + Error parseError(size_t LineNo, char const *Fmt, const Ts &... Vals) const { + Error E = createStringError(errc::invalid_argument, Fmt, Vals...); + return parseError(LineNo, std::move(E)); + } + +public: + IHexReader(MemoryBuffer *MB) : MemBuf(MB) {} + + std::unique_ptr<Object> create() const override; +}; + class ELFReader : public Reader { Binary *Bin; + Optional<StringRef> ExtractPartition; public: std::unique_ptr<Object> create() const override; - explicit ELFReader(Binary *B) : Bin(B) {} + explicit ELFReader(Binary *B, Optional<StringRef> ExtractPartition) + : Bin(B), ExtractPartition(ExtractPartition) {} }; class Object { @@ -764,6 +988,7 @@ private: std::vector<SecPtr> Sections; std::vector<SegPtr> Segments; + std::vector<SecPtr> RemovedSections; public: template <class T> @@ -792,6 +1017,7 @@ public: uint32_t Version; uint32_t Flags; + bool HadShdrs = true; StringTableSection *SectionNames = nullptr; SymbolTableSection *SymbolTable = nullptr; SectionIndexSection *SectionIndexTable = nullptr; @@ -801,11 +1027,19 @@ public: ConstRange<SectionBase> sections() const { return make_pointee_range(Sections); } + SectionBase *findSection(StringRef Name) { + auto SecIt = + find_if(Sections, [&](const SecPtr &Sec) { return Sec->Name == Name; }); + return SecIt == Sections.end() ? nullptr : SecIt->get(); + } + SectionTableRef removedSections() { return SectionTableRef(RemovedSections); } + Range<Segment> segments() { return make_pointee_range(Segments); } ConstRange<Segment> segments() const { return make_pointee_range(Segments); } - void removeSections(std::function<bool(const SectionBase &)> ToRemove); - void removeSymbols(function_ref<bool(const Symbol &)> ToRemove); + Error removeSections(bool AllowBrokenLinks, + std::function<bool(const SectionBase &)> ToRemove); + Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove); template <class T, class... Ts> T &addSection(Ts &&... Args) { auto Sec = llvm::make_unique<T>(std::forward<Ts>(Args)...); auto Ptr = Sec.get(); diff --git a/contrib/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/contrib/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp new file mode 100644 index 000000000000..19343b65dd1e --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -0,0 +1,68 @@ +//===- MachOObjcopy.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 +// +//===----------------------------------------------------------------------===// + +#include "MachOObjcopy.h" +#include "../CopyConfig.h" +#include "MachOReader.h" +#include "MachOWriter.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { +namespace macho { + +using namespace object; + +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.AddSection.empty() || + !Config.DumpSection.empty() || !Config.KeepSection.empty() || + !Config.OnlySection.empty() || !Config.SymbolsToGlobalize.empty() || + !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || + !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || + !Config.SectionsToRename.empty() || !Config.SymbolsToRename.empty() || + !Config.UnneededSymbolsToRemove.empty() || + !Config.SetSectionFlags.empty() || !Config.ToRemove.empty() || + Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || + Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc || + Config.StripSections || Config.Weaken || Config.DecompressDebugSections || + Config.StripDebug || Config.StripNonAlloc || Config.StripSections || + Config.StripUnneeded || Config.DiscardMode != DiscardType::None || + !Config.SymbolsToAdd.empty() || Config.EntryExpr) { + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for MachO"); + } + + return Error::success(); +} + +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::MachOObjectFile &In, Buffer &Out) { + MachOReader Reader(In); + std::unique_ptr<Object> O = Reader.create(); + if (!O) + return createFileError( + Config.InputFilename, + createStringError(object_error::parse_failed, + "unable to deserialize MachO object")); + + if (Error E = handleArgs(Config, *O)) + return createFileError(Config.InputFilename, std::move(E)); + + MachOWriter Writer(*O, In.is64Bit(), In.isLittleEndian(), Out); + if (auto E = Writer.finalize()) + return E; + return Writer.write(); +} + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h b/contrib/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h new file mode 100644 index 000000000000..f34e361db7ea --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.h @@ -0,0 +1,31 @@ +//===- MachOObjcopy.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_MACHOOBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H + +namespace llvm { +class Error; + +namespace object { +class MachOObjectFile; +class MachOUniversalBinary; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace macho { +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::MachOObjectFile &In, Buffer &Out); +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_MACHOOBJCOPY_H diff --git a/contrib/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp b/contrib/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp new file mode 100644 index 000000000000..d31293034608 --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp @@ -0,0 +1,241 @@ +//===- MachOReader.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 +// +//===----------------------------------------------------------------------===// + +#include "MachOReader.h" +#include "../llvm-objcopy.h" +#include "Object.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/MachO.h" +#include <memory> + +namespace llvm { +namespace objcopy { +namespace macho { + +void MachOReader::readHeader(Object &O) const { + O.Header.Magic = MachOObj.getHeader().magic; + O.Header.CPUType = MachOObj.getHeader().cputype; + O.Header.CPUSubType = MachOObj.getHeader().cpusubtype; + O.Header.FileType = MachOObj.getHeader().filetype; + O.Header.NCmds = MachOObj.getHeader().ncmds; + O.Header.SizeOfCmds = MachOObj.getHeader().sizeofcmds; + O.Header.Flags = MachOObj.getHeader().flags; +} + +template <typename SectionType> +Section constructSectionCommon(SectionType Sec) { + Section S; + S.Sectname = + StringRef(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname))) + .str(); + S.Segname = + StringRef(Sec.segname, strnlen(Sec.segname, sizeof(Sec.sectname))).str(); + S.Addr = Sec.addr; + S.Size = Sec.size; + S.Offset = Sec.offset; + S.Align = Sec.align; + S.RelOff = Sec.reloff; + S.NReloc = Sec.nreloc; + S.Flags = Sec.flags; + S.Reserved1 = Sec.reserved1; + S.Reserved2 = Sec.reserved2; + S.Reserved3 = 0; + return S; +} + +template <typename SectionType> Section constructSection(SectionType Sec); + +template <> Section constructSection(MachO::section Sec) { + return constructSectionCommon(Sec); +} + +template <> Section constructSection(MachO::section_64 Sec) { + Section S = constructSectionCommon(Sec); + S.Reserved3 = Sec.reserved3; + return S; +} + +// TODO: get rid of reportError and make MachOReader return Expected<> instead. +template <typename SectionType, typename SegmentType> +std::vector<Section> +extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, + const object::MachOObjectFile &MachOObj, + size_t &NextSectionIndex) { + auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize; + const SectionType *Curr = + reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType)); + std::vector<Section> Sections; + for (; reinterpret_cast<const void *>(Curr) < End; Curr++) { + if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { + SectionType Sec; + memcpy((void *)&Sec, Curr, sizeof(SectionType)); + MachO::swapStruct(Sec); + Sections.push_back(constructSection(Sec)); + } else { + Sections.push_back(constructSection(*Curr)); + } + + Section &S = Sections.back(); + + Expected<object::SectionRef> SecRef = + MachOObj.getSection(NextSectionIndex++); + if (!SecRef) + reportError(MachOObj.getFileName(), SecRef.takeError()); + + if (Expected<ArrayRef<uint8_t>> E = + MachOObj.getSectionContents(SecRef->getRawDataRefImpl())) + S.Content = + StringRef(reinterpret_cast<const char *>(E->data()), E->size()); + else + reportError(MachOObj.getFileName(), E.takeError()); + + S.Relocations.reserve(S.NReloc); + for (auto RI = MachOObj.section_rel_begin(SecRef->getRawDataRefImpl()), + RE = MachOObj.section_rel_end(SecRef->getRawDataRefImpl()); + RI != RE; ++RI) { + RelocationInfo R; + R.Symbol = nullptr; // We'll fill this field later. + R.Info = MachOObj.getRelocation(RI->getRawDataRefImpl()); + R.Scattered = MachOObj.isRelocationScattered(R.Info); + S.Relocations.push_back(R); + } + + assert(S.NReloc == S.Relocations.size() && + "Incorrect number of relocations"); + } + return Sections; +} + +void MachOReader::readLoadCommands(Object &O) const { + // For MachO sections indices start from 1. + size_t NextSectionIndex = 1; + for (auto LoadCmd : MachOObj.load_commands()) { + LoadCommand LC; + switch (LoadCmd.C.cmd) { + case MachO::LC_SEGMENT: + LC.Sections = extractSections<MachO::section, MachO::segment_command>( + LoadCmd, MachOObj, NextSectionIndex); + break; + case MachO::LC_SEGMENT_64: + LC.Sections = + extractSections<MachO::section_64, MachO::segment_command_64>( + LoadCmd, MachOObj, NextSectionIndex); + break; + case MachO::LC_SYMTAB: + O.SymTabCommandIndex = O.LoadCommands.size(); + break; + case MachO::LC_DYLD_INFO: + case MachO::LC_DYLD_INFO_ONLY: + O.DyLdInfoCommandIndex = O.LoadCommands.size(); + break; + } +#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ + case MachO::LCName: \ + memcpy((void *)&(LC.MachOLoadCommand.LCStruct##_data), LoadCmd.Ptr, \ + sizeof(MachO::LCStruct)); \ + if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) \ + MachO::swapStruct(LC.MachOLoadCommand.LCStruct##_data); \ + LC.Payload = ArrayRef<uint8_t>( \ + reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + \ + sizeof(MachO::LCStruct), \ + LoadCmd.C.cmdsize - sizeof(MachO::LCStruct)); \ + break; + + switch (LoadCmd.C.cmd) { + default: + memcpy((void *)&(LC.MachOLoadCommand.load_command_data), LoadCmd.Ptr, + sizeof(MachO::load_command)); + if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(LC.MachOLoadCommand.load_command_data); + LC.Payload = ArrayRef<uint8_t>( + reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + + sizeof(MachO::load_command), + LoadCmd.C.cmdsize - sizeof(MachO::load_command)); + break; +#include "llvm/BinaryFormat/MachO.def" + } + O.LoadCommands.push_back(std::move(LC)); + } +} + +template <typename nlist_t> +SymbolEntry constructSymbolEntry(StringRef StrTable, const nlist_t &nlist) { + assert(nlist.n_strx < StrTable.size() && + "n_strx exceeds the size of the string table"); + SymbolEntry SE; + SE.Name = StringRef(StrTable.data() + nlist.n_strx).str(); + SE.n_type = nlist.n_type; + SE.n_sect = nlist.n_sect; + SE.n_desc = nlist.n_desc; + SE.n_value = nlist.n_value; + return SE; +} + +void MachOReader::readSymbolTable(Object &O) const { + StringRef StrTable = MachOObj.getStringTableData(); + for (auto Symbol : MachOObj.symbols()) { + SymbolEntry SE = + (MachOObj.is64Bit() + ? constructSymbolEntry( + StrTable, + MachOObj.getSymbol64TableEntry(Symbol.getRawDataRefImpl())) + : constructSymbolEntry( + StrTable, + MachOObj.getSymbolTableEntry(Symbol.getRawDataRefImpl()))); + + O.SymTable.Symbols.push_back(llvm::make_unique<SymbolEntry>(SE)); + } +} + +void MachOReader::setSymbolInRelocationInfo(Object &O) const { + for (auto &LC : O.LoadCommands) + for (auto &Sec : LC.Sections) + for (auto &Reloc : Sec.Relocations) + if (!Reloc.Scattered) { + auto *Info = reinterpret_cast<MachO::relocation_info *>(&Reloc.Info); + Reloc.Symbol = O.SymTable.getSymbolByIndex(Info->r_symbolnum); + } +} + +void MachOReader::readRebaseInfo(Object &O) const { + O.Rebases.Opcodes = MachOObj.getDyldInfoRebaseOpcodes(); +} + +void MachOReader::readBindInfo(Object &O) const { + O.Binds.Opcodes = MachOObj.getDyldInfoBindOpcodes(); +} + +void MachOReader::readWeakBindInfo(Object &O) const { + O.WeakBinds.Opcodes = MachOObj.getDyldInfoWeakBindOpcodes(); +} + +void MachOReader::readLazyBindInfo(Object &O) const { + O.LazyBinds.Opcodes = MachOObj.getDyldInfoLazyBindOpcodes(); +} + +void MachOReader::readExportInfo(Object &O) const { + O.Exports.Trie = MachOObj.getDyldInfoExportsTrie(); +} + +std::unique_ptr<Object> MachOReader::create() const { + auto Obj = llvm::make_unique<Object>(); + readHeader(*Obj); + readLoadCommands(*Obj); + readSymbolTable(*Obj); + setSymbolInRelocationInfo(*Obj); + readRebaseInfo(*Obj); + readBindInfo(*Obj); + readWeakBindInfo(*Obj); + readLazyBindInfo(*Obj); + readExportInfo(*Obj); + return Obj; +} + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/MachO/MachOReader.h b/contrib/llvm/tools/llvm-objcopy/MachO/MachOReader.h new file mode 100644 index 000000000000..795e5cc2363d --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/MachO/MachOReader.h @@ -0,0 +1,48 @@ +//===- MachOReader.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 +// +//===----------------------------------------------------------------------===// + +#include "MachOObjcopy.h" +#include "Object.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/MachO.h" +#include <memory> + +namespace llvm { +namespace objcopy { +namespace macho { + +// The hierarchy of readers is responsible for parsing different inputs: +// raw binaries and regular MachO object files. +class Reader { +public: + virtual ~Reader(){}; + virtual std::unique_ptr<Object> create() const = 0; +}; + +class MachOReader : public Reader { + const object::MachOObjectFile &MachOObj; + + void readHeader(Object &O) const; + void readLoadCommands(Object &O) const; + void readSymbolTable(Object &O) const; + void setSymbolInRelocationInfo(Object &O) const; + void readRebaseInfo(Object &O) const; + void readBindInfo(Object &O) const; + void readWeakBindInfo(Object &O) const; + void readLazyBindInfo(Object &O) const; + void readExportInfo(Object &O) const; + +public: + explicit MachOReader(const object::MachOObjectFile &Obj) : MachOObj(Obj) {} + + std::unique_ptr<Object> create() const override; +}; + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/contrib/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp new file mode 100644 index 000000000000..74200c5aa62a --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp @@ -0,0 +1,590 @@ +//===- MachOWriter.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 +// +//===----------------------------------------------------------------------===// + +#include "MachOWriter.h" +#include "Object.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include <memory> + +namespace llvm { +namespace objcopy { +namespace macho { + +size_t MachOWriter::headerSize() const { + return Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); +} + +size_t MachOWriter::loadCommandsSize() const { return O.Header.SizeOfCmds; } + +size_t MachOWriter::symTableSize() const { + return O.SymTable.Symbols.size() * + (Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist)); +} + +size_t MachOWriter::totalSize() const { + // Going from tail to head and looking for an appropriate "anchor" to + // calculate the total size assuming that all the offsets are either valid + // ("true") or 0 (0 indicates that the corresponding part is missing). + + SmallVector<size_t, 7> Ends; + if (O.SymTabCommandIndex) { + const MachO::symtab_command &SymTabCommand = + O.LoadCommands[*O.SymTabCommandIndex] + .MachOLoadCommand.symtab_command_data; + if (SymTabCommand.symoff) { + assert((SymTabCommand.nsyms == O.SymTable.Symbols.size()) && + "Incorrect number of symbols"); + Ends.push_back(SymTabCommand.symoff + symTableSize()); + } + if (SymTabCommand.stroff) { + assert((SymTabCommand.strsize == StrTableBuilder.getSize()) && + "Incorrect string table size"); + Ends.push_back(SymTabCommand.stroff + SymTabCommand.strsize); + } + } + if (O.DyLdInfoCommandIndex) { + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + if (DyLdInfoCommand.rebase_off) { + assert((DyLdInfoCommand.rebase_size == O.Rebases.Opcodes.size()) && + "Incorrect rebase opcodes size"); + Ends.push_back(DyLdInfoCommand.rebase_off + DyLdInfoCommand.rebase_size); + } + if (DyLdInfoCommand.bind_off) { + assert((DyLdInfoCommand.bind_size == O.Binds.Opcodes.size()) && + "Incorrect bind opcodes size"); + Ends.push_back(DyLdInfoCommand.bind_off + DyLdInfoCommand.bind_size); + } + if (DyLdInfoCommand.weak_bind_off) { + assert((DyLdInfoCommand.weak_bind_size == O.WeakBinds.Opcodes.size()) && + "Incorrect weak bind opcodes size"); + Ends.push_back(DyLdInfoCommand.weak_bind_off + + DyLdInfoCommand.weak_bind_size); + } + if (DyLdInfoCommand.lazy_bind_off) { + assert((DyLdInfoCommand.lazy_bind_size == O.LazyBinds.Opcodes.size()) && + "Incorrect lazy bind opcodes size"); + Ends.push_back(DyLdInfoCommand.lazy_bind_off + + DyLdInfoCommand.lazy_bind_size); + } + if (DyLdInfoCommand.export_off) { + assert((DyLdInfoCommand.export_size == O.Exports.Trie.size()) && + "Incorrect trie size"); + Ends.push_back(DyLdInfoCommand.export_off + DyLdInfoCommand.export_size); + } + } + + // Otherwise, use the last section / reloction. + for (const auto &LC : O.LoadCommands) + for (const auto &S : LC.Sections) { + Ends.push_back(S.Offset + S.Size); + if (S.RelOff) + Ends.push_back(S.RelOff + + S.NReloc * sizeof(MachO::any_relocation_info)); + } + + if (!Ends.empty()) + return *std::max_element(Ends.begin(), Ends.end()); + + // Otherwise, we have only Mach header and load commands. + return headerSize() + loadCommandsSize(); +} + +void MachOWriter::writeHeader() { + MachO::mach_header_64 Header; + + Header.magic = O.Header.Magic; + Header.cputype = O.Header.CPUType; + Header.cpusubtype = O.Header.CPUSubType; + Header.filetype = O.Header.FileType; + Header.ncmds = O.Header.NCmds; + Header.sizeofcmds = O.Header.SizeOfCmds; + Header.flags = O.Header.Flags; + Header.reserved = O.Header.Reserved; + + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(Header); + + auto HeaderSize = + Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); + memcpy(B.getBufferStart(), &Header, HeaderSize); +} + +void MachOWriter::updateSymbolIndexes() { + uint32_t Index = 0; + for (auto &Symbol : O.SymTable.Symbols) { + Symbol->Index = Index; + Index++; + } +} + +void MachOWriter::writeLoadCommands() { + uint8_t *Begin = B.getBufferStart() + headerSize(); + for (const auto &LC : O.LoadCommands) { + // Construct a load command. + MachO::macho_load_command MLC = LC.MachOLoadCommand; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(MLC.segment_command_data); + memcpy(Begin, &MLC.segment_command_data, sizeof(MachO::segment_command)); + Begin += sizeof(MachO::segment_command); + + for (const auto &Sec : LC.Sections) + writeSectionInLoadCommand<MachO::section>(Sec, Begin); + continue; + case MachO::LC_SEGMENT_64: + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(MLC.segment_command_64_data); + memcpy(Begin, &MLC.segment_command_64_data, + sizeof(MachO::segment_command_64)); + Begin += sizeof(MachO::segment_command_64); + + for (const auto &Sec : LC.Sections) + writeSectionInLoadCommand<MachO::section_64>(Sec, Begin); + continue; + } + +#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ + case MachO::LCName: \ + assert(sizeof(MachO::LCStruct) + LC.Payload.size() == \ + MLC.load_command_data.cmdsize); \ + if (IsLittleEndian != sys::IsLittleEndianHost) \ + MachO::swapStruct(MLC.LCStruct##_data); \ + memcpy(Begin, &MLC.LCStruct##_data, sizeof(MachO::LCStruct)); \ + Begin += sizeof(MachO::LCStruct); \ + memcpy(Begin, LC.Payload.data(), LC.Payload.size()); \ + Begin += LC.Payload.size(); \ + break; + + // Copy the load command as it is. + switch (MLC.load_command_data.cmd) { + default: + assert(sizeof(MachO::load_command) + LC.Payload.size() == + MLC.load_command_data.cmdsize); + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(MLC.load_command_data); + memcpy(Begin, &MLC.load_command_data, sizeof(MachO::load_command)); + Begin += sizeof(MachO::load_command); + memcpy(Begin, LC.Payload.data(), LC.Payload.size()); + Begin += LC.Payload.size(); + break; +#include "llvm/BinaryFormat/MachO.def" + } + } +} + +template <typename StructType> +void MachOWriter::writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out) { + StructType Temp; + assert(Sec.Segname.size() <= sizeof(Temp.segname) && "too long segment name"); + assert(Sec.Sectname.size() <= sizeof(Temp.sectname) && + "too long section name"); + memset(&Temp, 0, sizeof(StructType)); + memcpy(Temp.segname, Sec.Segname.data(), Sec.Segname.size()); + memcpy(Temp.sectname, Sec.Sectname.data(), Sec.Sectname.size()); + Temp.addr = Sec.Addr; + Temp.size = Sec.Size; + Temp.offset = Sec.Offset; + Temp.align = Sec.Align; + Temp.reloff = Sec.RelOff; + Temp.nreloc = Sec.NReloc; + Temp.flags = Sec.Flags; + Temp.reserved1 = Sec.Reserved1; + Temp.reserved2 = Sec.Reserved2; + + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(Temp); + memcpy(Out, &Temp, sizeof(StructType)); + Out += sizeof(StructType); +} + +void MachOWriter::writeSections() { + for (const auto &LC : O.LoadCommands) + for (const auto &Sec : LC.Sections) { + if (Sec.isVirtualSection()) + continue; + + 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(), + Sec.Content.size()); + for (size_t Index = 0; Index < Sec.Relocations.size(); ++Index) { + auto RelocInfo = Sec.Relocations[Index]; + if (!RelocInfo.Scattered) { + auto *Info = + reinterpret_cast<MachO::relocation_info *>(&RelocInfo.Info); + Info->r_symbolnum = RelocInfo.Symbol->Index; + } + + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct( + reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info)); + memcpy(B.getBufferStart() + Sec.RelOff + + Index * sizeof(MachO::any_relocation_info), + &RelocInfo.Info, sizeof(RelocInfo.Info)); + } + } +} + +template <typename NListType> +void writeNListEntry(const SymbolEntry &SE, bool IsLittleEndian, char *&Out, + uint32_t Nstrx) { + NListType ListEntry; + ListEntry.n_strx = Nstrx; + ListEntry.n_type = SE.n_type; + ListEntry.n_sect = SE.n_sect; + ListEntry.n_desc = SE.n_desc; + ListEntry.n_value = SE.n_value; + + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(ListEntry); + memcpy(Out, reinterpret_cast<const char *>(&ListEntry), sizeof(NListType)); + Out += sizeof(NListType); +} + +void MachOWriter::writeSymbolTable() { + if (!O.SymTabCommandIndex) + return; + const MachO::symtab_command &SymTabCommand = + O.LoadCommands[*O.SymTabCommandIndex] + .MachOLoadCommand.symtab_command_data; + + uint8_t *StrTable = (uint8_t *)B.getBufferStart() + SymTabCommand.stroff; + StrTableBuilder.write(StrTable); +} + +void MachOWriter::writeStringTable() { + if (!O.SymTabCommandIndex) + return; + const MachO::symtab_command &SymTabCommand = + O.LoadCommands[*O.SymTabCommandIndex] + .MachOLoadCommand.symtab_command_data; + + char *SymTable = (char *)B.getBufferStart() + SymTabCommand.symoff; + for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end(); + Iter != End; Iter++) { + SymbolEntry *Sym = Iter->get(); + auto Nstrx = StrTableBuilder.getOffset(Sym->Name); + + if (Is64Bit) + writeNListEntry<MachO::nlist_64>(*Sym, IsLittleEndian, SymTable, Nstrx); + else + writeNListEntry<MachO::nlist>(*Sym, IsLittleEndian, SymTable, Nstrx); + } +} + +void MachOWriter::writeRebaseInfo() { + if (!O.DyLdInfoCommandIndex) + return; + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + char *Out = (char *)B.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()); +} + +void MachOWriter::writeBindInfo() { + if (!O.DyLdInfoCommandIndex) + return; + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + char *Out = (char *)B.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()); +} + +void MachOWriter::writeWeakBindInfo() { + if (!O.DyLdInfoCommandIndex) + return; + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + char *Out = (char *)B.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()); +} + +void MachOWriter::writeLazyBindInfo() { + if (!O.DyLdInfoCommandIndex) + return; + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + char *Out = (char *)B.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()); +} + +void MachOWriter::writeExportInfo() { + if (!O.DyLdInfoCommandIndex) + return; + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + char *Out = (char *)B.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()); +} + +void MachOWriter::writeTail() { + typedef void (MachOWriter::*WriteHandlerType)(void); + typedef std::pair<uint64_t, WriteHandlerType> WriteOperation; + SmallVector<WriteOperation, 7> Queue; + + if (O.SymTabCommandIndex) { + const MachO::symtab_command &SymTabCommand = + O.LoadCommands[*O.SymTabCommandIndex] + .MachOLoadCommand.symtab_command_data; + if (SymTabCommand.symoff) + Queue.push_back({SymTabCommand.symoff, &MachOWriter::writeSymbolTable}); + if (SymTabCommand.stroff) + Queue.push_back({SymTabCommand.stroff, &MachOWriter::writeStringTable}); + } + + if (O.DyLdInfoCommandIndex) { + const MachO::dyld_info_command &DyLdInfoCommand = + O.LoadCommands[*O.DyLdInfoCommandIndex] + .MachOLoadCommand.dyld_info_command_data; + if (DyLdInfoCommand.rebase_off) + Queue.push_back( + {DyLdInfoCommand.rebase_off, &MachOWriter::writeRebaseInfo}); + if (DyLdInfoCommand.bind_off) + Queue.push_back({DyLdInfoCommand.bind_off, &MachOWriter::writeBindInfo}); + if (DyLdInfoCommand.weak_bind_off) + Queue.push_back( + {DyLdInfoCommand.weak_bind_off, &MachOWriter::writeWeakBindInfo}); + if (DyLdInfoCommand.lazy_bind_off) + Queue.push_back( + {DyLdInfoCommand.lazy_bind_off, &MachOWriter::writeLazyBindInfo}); + if (DyLdInfoCommand.export_off) + Queue.push_back( + {DyLdInfoCommand.export_off, &MachOWriter::writeExportInfo}); + } + + llvm::sort(Queue, [](const WriteOperation &LHS, const WriteOperation &RHS) { + return LHS.first < RHS.first; + }); + + for (auto WriteOp : Queue) + (this->*WriteOp.second)(); +} + +void MachOWriter::updateSizeOfCmds() { + auto Size = 0; + for (const auto &LC : O.LoadCommands) { + auto &MLC = LC.MachOLoadCommand; + auto cmd = MLC.load_command_data.cmd; + + switch (cmd) { + case MachO::LC_SEGMENT: + Size += sizeof(MachO::segment_command) + + sizeof(MachO::section) * LC.Sections.size(); + continue; + case MachO::LC_SEGMENT_64: + Size += sizeof(MachO::segment_command_64) + + sizeof(MachO::section_64) * LC.Sections.size(); + continue; + } + + switch (cmd) { +#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ + case MachO::LCName: \ + Size += sizeof(MachO::LCStruct); \ + break; +#include "llvm/BinaryFormat/MachO.def" +#undef HANDLE_LOAD_COMMAND + } + } + + O.Header.SizeOfCmds = Size; +} + +// Updates the index and the number of local/external/undefined symbols. Here we +// assume that MLC is a LC_DYSYMTAB and the nlist entries in the symbol table +// are already sorted by the those types. +void MachOWriter::updateDySymTab(MachO::macho_load_command &MLC) { + uint32_t NumLocalSymbols = 0; + auto Iter = O.SymTable.Symbols.begin(); + auto End = O.SymTable.Symbols.end(); + for (; Iter != End; Iter++) { + if ((*Iter)->n_type & (MachO::N_EXT | MachO::N_PEXT)) + break; + + NumLocalSymbols++; + } + + uint32_t NumExtDefSymbols = 0; + for (; Iter != End; Iter++) { + if (((*Iter)->n_type & MachO::N_TYPE) == MachO::N_UNDF) + break; + + NumExtDefSymbols++; + } + + MLC.dysymtab_command_data.ilocalsym = 0; + MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols; + MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols; + MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols; + MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols; + MLC.dysymtab_command_data.nundefsym = + O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols); +} + +// Recomputes and updates offset and size fields in load commands and sections +// since they could be modified. +Error MachOWriter::layout() { + auto SizeOfCmds = loadCommandsSize(); + auto Offset = headerSize() + SizeOfCmds; + O.Header.NCmds = O.LoadCommands.size(); + O.Header.SizeOfCmds = SizeOfCmds; + + // Lay out sections. + for (auto &LC : O.LoadCommands) { + uint64_t FileOff = Offset; + uint64_t VMSize = 0; + uint64_t FileOffsetInSegment = 0; + for (auto &Sec : LC.Sections) { + if (!Sec.isVirtualSection()) { + auto FilePaddingSize = + OffsetToAlignment(FileOffsetInSegment, 1ull << Sec.Align); + Sec.Offset = Offset + FileOffsetInSegment + FilePaddingSize; + Sec.Size = Sec.Content.size(); + FileOffsetInSegment += FilePaddingSize + Sec.Size; + } + + VMSize = std::max(VMSize, Sec.Addr + Sec.Size); + } + + // TODO: Handle the __PAGEZERO segment. + auto &MLC = LC.MachOLoadCommand; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + MLC.segment_command_data.cmdsize = + sizeof(MachO::segment_command) + + sizeof(MachO::section) * LC.Sections.size(); + MLC.segment_command_data.nsects = LC.Sections.size(); + MLC.segment_command_data.fileoff = FileOff; + MLC.segment_command_data.vmsize = VMSize; + MLC.segment_command_data.filesize = FileOffsetInSegment; + break; + case MachO::LC_SEGMENT_64: + MLC.segment_command_64_data.cmdsize = + sizeof(MachO::segment_command_64) + + sizeof(MachO::section_64) * LC.Sections.size(); + MLC.segment_command_64_data.nsects = LC.Sections.size(); + MLC.segment_command_64_data.fileoff = FileOff; + MLC.segment_command_64_data.vmsize = VMSize; + MLC.segment_command_64_data.filesize = FileOffsetInSegment; + break; + } + + Offset += FileOffsetInSegment; + } + + // Lay out relocations. + for (auto &LC : O.LoadCommands) + for (auto &Sec : LC.Sections) { + Sec.RelOff = Sec.Relocations.empty() ? 0 : Offset; + Sec.NReloc = Sec.Relocations.size(); + Offset += sizeof(MachO::any_relocation_info) * Sec.NReloc; + } + + // Lay out tail stuff. + auto NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); + for (auto &LC : O.LoadCommands) { + auto &MLC = LC.MachOLoadCommand; + auto cmd = MLC.load_command_data.cmd; + switch (cmd) { + case MachO::LC_SYMTAB: + MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size(); + MLC.symtab_command_data.strsize = StrTableBuilder.getSize(); + MLC.symtab_command_data.symoff = Offset; + Offset += NListSize * MLC.symtab_command_data.nsyms; + MLC.symtab_command_data.stroff = Offset; + Offset += MLC.symtab_command_data.strsize; + break; + case MachO::LC_DYSYMTAB: { + if (MLC.dysymtab_command_data.ntoc != 0 || + MLC.dysymtab_command_data.nmodtab != 0 || + MLC.dysymtab_command_data.nextrefsyms != 0 || + MLC.dysymtab_command_data.nlocrel != 0 || + MLC.dysymtab_command_data.nextrel != 0) + return createStringError(llvm::errc::not_supported, + "shared library is not yet supported"); + + if (MLC.dysymtab_command_data.nindirectsyms != 0) + return createStringError(llvm::errc::not_supported, + "indirect symbol table is not yet supported"); + + updateDySymTab(MLC); + break; + } + case MachO::LC_SEGMENT: + case MachO::LC_SEGMENT_64: + case MachO::LC_VERSION_MIN_MACOSX: + case MachO::LC_BUILD_VERSION: + case MachO::LC_ID_DYLIB: + case MachO::LC_LOAD_DYLIB: + case MachO::LC_UUID: + case MachO::LC_SOURCE_VERSION: + // Nothing to update. + break; + default: + // Abort if it's unsupported in order to prevent corrupting the object. + return createStringError(llvm::errc::not_supported, + "unsupported load command (cmd=0x%x)", cmd); + } + } + + return Error::success(); +} + +void MachOWriter::constructStringTable() { + for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols) + StrTableBuilder.add(Sym->Name); + StrTableBuilder.finalize(); +} + +Error MachOWriter::finalize() { + updateSizeOfCmds(); + constructStringTable(); + + if (auto E = layout()) + return E; + + return Error::success(); +} + +Error MachOWriter::write() { + if (Error E = B.allocate(totalSize())) + return E; + memset(B.getBufferStart(), 0, totalSize()); + writeHeader(); + updateSymbolIndexes(); + writeLoadCommands(); + writeSections(); + writeTail(); + return B.commit(); +} + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/MachO/MachOWriter.h b/contrib/llvm/tools/llvm-objcopy/MachO/MachOWriter.h new file mode 100644 index 000000000000..ecf12d62de2c --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/MachO/MachOWriter.h @@ -0,0 +1,64 @@ +//===- MachOWriter.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 +// +//===----------------------------------------------------------------------===// + +#include "../Buffer.h" +#include "MachOObjcopy.h" +#include "Object.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/Object/MachO.h" + +namespace llvm { +class Error; + +namespace objcopy { +namespace macho { + +class MachOWriter { + Object &O; + bool Is64Bit; + bool IsLittleEndian; + Buffer &B; + StringTableBuilder StrTableBuilder{StringTableBuilder::MachO}; + + size_t headerSize() const; + size_t loadCommandsSize() const; + size_t symTableSize() const; + size_t strTableSize() const; + + void updateDySymTab(MachO::macho_load_command &MLC); + void updateSizeOfCmds(); + void updateSymbolIndexes(); + void constructStringTable(); + Error layout(); + + void writeHeader(); + void writeLoadCommands(); + template <typename StructType> + void writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out); + void writeSections(); + void writeSymbolTable(); + void writeStringTable(); + void writeRebaseInfo(); + void writeBindInfo(); + void writeWeakBindInfo(); + void writeLazyBindInfo(); + void writeExportInfo(); + void writeTail(); + +public: + MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, Buffer &B) + : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), B(B) {} + + size_t totalSize() const; + Error finalize(); + Error write(); +}; + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/MachO/Object.cpp b/contrib/llvm/tools/llvm-objcopy/MachO/Object.cpp new file mode 100644 index 000000000000..264f39c28ed2 --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/MachO/Object.cpp @@ -0,0 +1,15 @@ +#include "Object.h" +#include "../llvm-objcopy.h" + +namespace llvm { +namespace objcopy { +namespace macho { + +const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const { + assert(Index < Symbols.size() && "invalid symbol index"); + return Symbols[Index].get(); +} + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/contrib/llvm/tools/llvm-objcopy/MachO/Object.h b/contrib/llvm/tools/llvm-objcopy/MachO/Object.h new file mode 100644 index 000000000000..ed85fcbc47f7 --- /dev/null +++ b/contrib/llvm/tools/llvm-objcopy/MachO/Object.h @@ -0,0 +1,232 @@ +//===- Object.h - Mach-O object file model ----------------------*- 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_OBJCOPY_MACHO_OBJECT_H +#define LLVM_OBJCOPY_MACHO_OBJECT_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/ObjectYAML/DWARFYAML.h" +#include "llvm/Support/YAMLTraits.h" +#include <cstdint> +#include <string> +#include <vector> + +namespace llvm { +namespace objcopy { +namespace macho { + +struct MachHeader { + uint32_t Magic; + uint32_t CPUType; + uint32_t CPUSubType; + uint32_t FileType; + uint32_t NCmds; + uint32_t SizeOfCmds; + uint32_t Flags; + uint32_t Reserved = 0; +}; + +struct RelocationInfo; +struct Section { + std::string Sectname; + std::string Segname; + uint64_t Addr; + uint64_t Size; + uint32_t Offset; + uint32_t Align; + uint32_t RelOff; + uint32_t NReloc; + uint32_t Flags; + uint32_t Reserved1; + uint32_t Reserved2; + uint32_t Reserved3; + + StringRef Content; + std::vector<RelocationInfo> Relocations; + + MachO::SectionType getType() const { + return static_cast<MachO::SectionType>(Flags & MachO::SECTION_TYPE); + } + + bool isVirtualSection() const { + return (getType() == MachO::S_ZEROFILL || + getType() == MachO::S_GB_ZEROFILL || + getType() == MachO::S_THREAD_LOCAL_ZEROFILL); + } +}; + +struct LoadCommand { + // The type MachO::macho_load_command is defined in llvm/BinaryFormat/MachO.h + // and it is a union of all the structs corresponding to various load + // commands. + MachO::macho_load_command MachOLoadCommand; + + // The raw content of the payload of the load command (located right after the + // corresponding struct). In some cases it is either empty or can be + // copied-over without digging into its structure. + ArrayRef<uint8_t> Payload; + + // Some load commands can contain (inside the payload) an array of sections, + // though the contents of the sections are stored separately. The struct + // Section describes only sections' metadata and where to find the + // corresponding content inside the binary. + std::vector<Section> Sections; +}; + +// A symbol information. Fields which starts with "n_" are same as them in the +// nlist. +struct SymbolEntry { + std::string Name; + uint32_t Index; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; + uint64_t n_value; +}; + +/// The location of the symbol table inside the binary is described by LC_SYMTAB +/// load command. +struct SymbolTable { + std::vector<std::unique_ptr<SymbolEntry>> Symbols; + + const SymbolEntry *getSymbolByIndex(uint32_t Index) const; +}; + +/// The location of the string table inside the binary is described by LC_SYMTAB +/// load command. +struct StringTable { + std::vector<std::string> Strings; +}; + +struct RelocationInfo { + const SymbolEntry *Symbol; + // True if Info is a scattered_relocation_info. + bool Scattered; + MachO::any_relocation_info Info; +}; + +/// The location of the rebase info inside the binary is described by +/// LC_DYLD_INFO load command. Dyld rebases an image whenever dyld loads it at +/// an address different from its preferred address. The rebase information is +/// a stream of byte sized opcodes whose symbolic names start with +/// REBASE_OPCODE_. Conceptually the rebase information is a table of tuples: +/// <seg-index, seg-offset, type> +/// The opcodes are a compressed way to encode the table by only +/// encoding when a column changes. In addition simple patterns +/// like "every n'th offset for m times" can be encoded in a few +/// bytes. +struct RebaseInfo { + // At the moment we do not parse this info (and it is simply copied over), + // but the proper support will be added later. + ArrayRef<uint8_t> Opcodes; +}; + +/// The location of the bind info inside the binary is described by +/// LC_DYLD_INFO load command. Dyld binds an image during the loading process, +/// if the image requires any pointers to be initialized to symbols in other +/// images. The bind information is a stream of byte sized opcodes whose +/// symbolic names start with BIND_OPCODE_. Conceptually the bind information is +/// a table of tuples: <seg-index, seg-offset, type, symbol-library-ordinal, +/// symbol-name, addend> The opcodes are a compressed way to encode the table by +/// only encoding when a column changes. In addition simple patterns like for +/// runs of pointers initialized to the same value can be encoded in a few +/// bytes. +struct BindInfo { + // At the moment we do not parse this info (and it is simply copied over), + // but the proper support will be added later. + ArrayRef<uint8_t> Opcodes; +}; + +/// The location of the weak bind info inside the binary is described by +/// LC_DYLD_INFO load command. Some C++ programs require dyld to unique symbols +/// so that all images in the process use the same copy of some code/data. This +/// step is done after binding. The content of the weak_bind info is an opcode +/// stream like the bind_info. But it is sorted alphabetically by symbol name. +/// This enable dyld to walk all images with weak binding information in order +/// and look for collisions. If there are no collisions, dyld does no updating. +/// That means that some fixups are also encoded in the bind_info. For +/// instance, all calls to "operator new" are first bound to libstdc++.dylib +/// using the information in bind_info. Then if some image overrides operator +/// new that is detected when the weak_bind information is processed and the +/// call to operator new is then rebound. +struct WeakBindInfo { + // At the moment we do not parse this info (and it is simply copied over), + // but the proper support will be added later. + ArrayRef<uint8_t> Opcodes; +}; + +/// The location of the lazy bind info inside the binary is described by +/// LC_DYLD_INFO load command. Some uses of external symbols do not need to be +/// bound immediately. Instead they can be lazily bound on first use. The +/// lazy_bind contains a stream of BIND opcodes to bind all lazy symbols. Normal +/// use is that dyld ignores the lazy_bind section when loading an image. +/// Instead the static linker arranged for the lazy pointer to initially point +/// to a helper function which pushes the offset into the lazy_bind area for the +/// symbol needing to be bound, then jumps to dyld which simply adds the offset +/// to lazy_bind_off to get the information on what to bind. +struct LazyBindInfo { + ArrayRef<uint8_t> Opcodes; +}; + +/// The location of the export info inside the binary is described by +/// LC_DYLD_INFO load command. The symbols exported by a dylib are encoded in a +/// trie. This is a compact representation that factors out common prefixes. It +/// also reduces LINKEDIT pages in RAM because it encodes all information (name, +/// address, flags) in one small, contiguous range. The export area is a stream +/// of nodes. The first node sequentially is the start node for the trie. Nodes +/// for a symbol start with a uleb128 that is the length of the exported symbol +/// information for the string so far. If there is no exported symbol, the node +/// starts with a zero byte. If there is exported info, it follows the length. +/// First is a uleb128 containing flags. Normally, it is followed by +/// a uleb128 encoded offset which is location of the content named +/// by the symbol from the mach_header for the image. If the flags +/// is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is +/// a uleb128 encoded library ordinal, then a zero terminated +/// UTF8 string. If the string is zero length, then the symbol +/// is re-export from the specified dylib with the same name. +/// If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following +/// the flags is two uleb128s: the stub offset and the resolver offset. +/// The stub is used by non-lazy pointers. The resolver is used +/// by lazy pointers and must be called to get the actual address to use. +/// After the optional exported symbol information is a byte of +/// how many edges (0-255) that this node has leaving it, +/// followed by each edge. +/// Each edge is a zero terminated UTF8 of the addition chars +/// in the symbol, followed by a uleb128 offset for the node that +/// edge points to. +struct ExportInfo { + ArrayRef<uint8_t> Trie; +}; + +struct Object { + MachHeader Header; + std::vector<LoadCommand> LoadCommands; + + SymbolTable SymTable; + StringTable StrTable; + + RebaseInfo Rebases; + BindInfo Binds; + WeakBindInfo WeakBinds; + LazyBindInfo LazyBinds; + ExportInfo Exports; + + /// The index of LC_SYMTAB load command if present. + Optional<size_t> SymTabCommandIndex; + /// The index of LC_DYLD_INFO or LC_DYLD_INFO_ONLY load command if present. + Optional<size_t> DyLdInfoCommandIndex; +}; + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_OBJCOPY_MACHO_OBJECT_H diff --git a/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td index 1f7e64e4091c..5fce4fbde539 100644 --- a/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -1,13 +1,20 @@ include "llvm/Option/OptParser.td" multiclass Eq<string name, string help> { - def NAME : Separate<["--", "-"], name>; - def NAME #_eq : Joined<["--", "-"], name #"=">, + def NAME : Separate<["--"], name>; + def NAME #_eq : Joined<["--"], name #"=">, Alias<!cast<Separate>(NAME)>, HelpText<help>; } -def help : Flag<["-", "--"], "help">; +def help : Flag<["--"], "help">; +def h : Flag<["-"], "h">, Alias<help>; + +def allow_broken_links + : Flag<["--"], "allow-broken-links">, + HelpText<"Allow llvm-objcopy to remove sections even if it would leave " + "invalid section references. The appropriate sh_link fields " + "will be set to zero.">; defm binary_architecture : Eq<"binary-architecture", "Used when transforming an architecture-less " @@ -26,13 +33,13 @@ defm output_target : Eq<"output-target", "Format of the output file">, Values<"binary">; def O : JoinedOrSeparate<["-"], "O">, Alias<output_target>; -def compress_debug_sections : Flag<["--", "-"], "compress-debug-sections">; +def compress_debug_sections : Flag<["--"], "compress-debug-sections">; def compress_debug_sections_eq - : Joined<["--", "-"], "compress-debug-sections=">, + : Joined<["--"], "compress-debug-sections=">, MetaVarName<"[ zlib | zlib-gnu ]">, HelpText<"Compress DWARF debug sections using specified style. Supported " "styles: 'zlib-gnu' and 'zlib'">; -def decompress_debug_sections : Flag<["-", "--"], "decompress-debug-sections">, +def decompress_debug_sections : Flag<["--"], "decompress-debug-sections">, HelpText<"Decompress DWARF debug sections.">; defm split_dwo : Eq<"split-dwo", "Equivalent to extract-dwo on the input file to " @@ -40,7 +47,7 @@ defm split_dwo MetaVarName<"dwo-file">; def enable_deterministic_archives - : Flag<["-", "--"], "enable-deterministic-archives">, + : Flag<["--"], "enable-deterministic-archives">, HelpText<"Enable deterministic mode when copying archives (use zero for " "UIDs, GIDs, and timestamps).">; def D : Flag<["-"], "D">, @@ -48,14 +55,14 @@ def D : Flag<["-"], "D">, HelpText<"Alias for --enable-deterministic-archives">; def disable_deterministic_archives - : Flag<["-", "--"], "disable-deterministic-archives">, + : Flag<["--"], "disable-deterministic-archives">, HelpText<"Disable deterministic mode when copying archives (use real " "values for UIDs, GIDs, and timestamps).">; def U : Flag<["-"], "U">, Alias<disable_deterministic_archives>, HelpText<"Alias for --disable-deterministic-archives">; -def preserve_dates : Flag<["-", "--"], "preserve-dates">, +def preserve_dates : Flag<["--"], "preserve-dates">, HelpText<"Preserve access and modification timestamps">; def p : Flag<["-"], "p">, Alias<preserve_dates>; @@ -76,6 +83,16 @@ defm rename_section defm redefine_symbol : Eq<"redefine-sym", "Change the name of a symbol old to new">, MetaVarName<"old=new">; +defm redefine_symbols + : Eq<"redefine-syms", + "Reads a list of symbol pairs from <filename> and runs as if " + "--redefine-sym=<old>=<new> is set for each one. <filename> " + "contains two symbols per line separated with whitespace and may " + "contain comments beginning with '#'. Leading and trailing " + "whitespace is stripped from each line. May be repeated to read " + "symbols from many files.">, + MetaVarName<"filename">; + defm keep_section : Eq<"keep-section", "Keep <section>">, MetaVarName<"section">; defm only_section : Eq<"only-section", "Remove all but <section>">, @@ -86,39 +103,76 @@ defm add_section "Make a section named <section> with the contents of <file>.">, MetaVarName<"section=file">; -def strip_all - : Flag<["-", "--"], "strip-all">, - HelpText< - "Remove non-allocated sections other than .gnu.warning* sections">; +defm set_section_flags + : Eq<"set-section-flags", + "Set section flags for a given section. Flags supported for GNU " + "compatibility: alloc, load, noload, readonly, debug, code, data, " + "rom, share, contents, merge, strings.">, + MetaVarName<"section=flag1[,flag2,...]">; + +def strip_all : Flag<["--"], "strip-all">, + HelpText<"Remove non-allocated sections outside segments. " + ".gnu.warning* sections are not removed">; def S : Flag<["-"], "S">, Alias<strip_all>; -def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">, +def strip_all_gnu : Flag<["--"], "strip-all-gnu">, HelpText<"Compatible with GNU objcopy's --strip-all">; -def strip_debug : Flag<["-", "--"], "strip-debug">, +def strip_debug : Flag<["--"], "strip-debug">, HelpText<"Remove all debug information">; -def strip_dwo : Flag<["-", "--"], "strip-dwo">, +def g : Flag<["-"], "g">, Alias<strip_debug>, + HelpText<"Alias for --strip-debug">; +def strip_dwo : Flag<["--"], "strip-dwo">, HelpText<"Remove all DWARF .dwo sections from file">; -def strip_sections : Flag<["-", "--"], "strip-sections">, - HelpText<"Remove all section headers">; -def strip_non_alloc : Flag<["-", "--"], "strip-non-alloc">, - HelpText<"Remove all non-allocated sections">; -def strip_unneeded : Flag<["-", "--"], "strip-unneeded">, +def strip_sections + : Flag<["--"], "strip-sections">, + HelpText<"Remove all section headers and all sections not in segments">; +def strip_non_alloc + : Flag<["--"], "strip-non-alloc">, + HelpText<"Remove all non-allocated sections outside segments">; +def strip_unneeded : Flag<["--"], "strip-unneeded">, HelpText<"Remove all symbols not needed by relocations">; +defm strip_unneeded_symbol + : Eq<"strip-unneeded-symbol", + "Remove symbol <symbol> if it is not needed by relocations">, + MetaVarName<"symbol">; +defm strip_unneeded_symbols + : Eq<"strip-unneeded-symbols", + "Reads a list of symbols from <filename> and removes them " + "if they are not needed by relocations">, + MetaVarName<"filename">; def extract_dwo - : Flag<["-", "--"], "extract-dwo">, + : Flag<["--"], "extract-dwo">, HelpText< "Remove all sections that are not DWARF .dwo sections from file">; +defm extract_partition + : Eq<"extract-partition", "Extract named partition from input file">, + MetaVarName<"name">; +def extract_main_partition + : Flag<["--"], "extract-main-partition">, + HelpText<"Extract main partition from the input file">; + def localize_hidden - : Flag<["-", "--"], "localize-hidden">, + : Flag<["--"], "localize-hidden">, HelpText< "Mark all symbols that have hidden or internal visibility as local">; defm localize_symbol : Eq<"localize-symbol", "Mark <symbol> as local">, MetaVarName<"symbol">; +defm localize_symbols + : Eq<"localize-symbols", + "Reads a list of symbols from <filename> and marks them local.">, + MetaVarName<"filename">; + def L : JoinedOrSeparate<["-"], "L">, Alias<localize_symbol>; defm globalize_symbol : Eq<"globalize-symbol", "Mark <symbol> as global">, MetaVarName<"symbol">; + +defm globalize_symbols + : Eq<"globalize-symbols", + "Reads a list of symbols from <filename> and marks them global.">, + MetaVarName<"filename">; + defm keep_global_symbol : Eq<"keep-global-symbol", "Convert all symbols except <symbol> to local. May be repeated to " @@ -137,23 +191,51 @@ defm keep_global_symbols defm weaken_symbol : Eq<"weaken-symbol", "Mark <symbol> as weak">, MetaVarName<"symbol">; +defm weaken_symbols + : Eq<"weaken-symbols", + "Reads a list of symbols from <filename> and marks them weak.">, + MetaVarName<"filename">; + def W : JoinedOrSeparate<["-"], "W">, Alias<weaken_symbol>; -def weaken : Flag<["-", "--"], "weaken">, +def weaken : Flag<["--"], "weaken">, HelpText<"Mark all global symbols as weak">; + +def discard_locals : Flag<["--"], "discard-locals">, + HelpText<"Remove compiler-generated local symbols, (e.g. " + "symbols starting with .L)">; +def X : Flag<["-"], "X">, Alias<discard_locals>; + def discard_all - : Flag<["-", "--"], "discard-all">, + : Flag<["--"], "discard-all">, HelpText<"Remove all local symbols except file and section symbols">; def x : Flag<["-"], "x">, Alias<discard_all>; defm strip_symbol : Eq<"strip-symbol", "Remove symbol <symbol>">, MetaVarName<"symbol">; +defm strip_symbols + : Eq<"strip-symbols", + "Reads a list of symbols from <filename> and removes them.">, + MetaVarName<"filename">; + def N : JoinedOrSeparate<["-"], "N">, Alias<strip_symbol>; defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, MetaVarName<"symbol">; def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>; + +defm keep_symbols + : Eq<"keep-symbols", + "Reads a list of symbols from <filename> and runs as if " + "--keep-symbol=<symbol> is set for each one. <filename> " + "contains one symbol per line and may contain comments beginning with " + "'#'. Leading and trailing whitespace is stripped from each line. May " + "be repeated to read symbols from many files.">, + MetaVarName<"filename">; + def only_keep_debug - : Flag<["-", "--"], "only-keep-debug">, - HelpText<"Currently ignored. Only for compatibility with GNU objcopy.">; -def keep_file_symbols : Flag<["-", "--"], "keep-file-symbols">, + : Flag<["--"], "only-keep-debug">, + HelpText<"Clear sections that would not be stripped by --strip-debug. " + "Currently only implemented for COFF.">; + +def keep_file_symbols : Flag<["--"], "keep-file-symbols">, HelpText<"Do not remove file symbols">; defm dump_section : Eq<"dump-section", @@ -163,7 +245,11 @@ defm prefix_symbols : Eq<"prefix-symbols", "Add <prefix> to the start of every symbol name">, MetaVarName<"prefix">; -def version : Flag<["-", "--"], "version">, +defm prefix_alloc_sections + : Eq<"prefix-alloc-sections", "Add <prefix> to the start of every allocated section name">, + MetaVarName<"prefix">; + +def version : Flag<["--"], "version">, HelpText<"Print the version and exit.">; def V : Flag<["-"], "V">, Alias<version>; defm build_id_link_dir @@ -178,3 +264,25 @@ 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">; + +def regex + : Flag<["--"], "regex">, + HelpText<"Permit regular expressions in name comparison">; + +defm set_start : Eq<"set-start", "Set the start address to <addr>. Overrides " + "any previous --change-start or --adjust-start values.">, + MetaVarName<"addr">; +defm change_start : Eq<"change-start", "Add <incr> to the start address. Can be " + "specified multiple times, all values will be applied " + "cumulatively.">, + MetaVarName<"incr">; +def adjust_start : JoinedOrSeparate<["--"], "adjust-start">, + Alias<change_start>; + +defm add_symbol + : Eq<"add-symbol", "Add new symbol <name> to .symtab. Accepted flags: " + "global, local, weak, default, hidden, file, section, object, " + "function, indirect-function. Accepted but ignored for " + "compatibility: debug, constructor, warning, indirect, synthetic, " + "unique-object, before.">, + MetaVarName<"name=[section:]value[,flags]">; diff --git a/contrib/llvm/tools/llvm-objcopy/StripOpts.td b/contrib/llvm/tools/llvm-objcopy/StripOpts.td index fa98e27e9321..1d06bb3dfb38 100644 --- a/contrib/llvm/tools/llvm-objcopy/StripOpts.td +++ b/contrib/llvm/tools/llvm-objcopy/StripOpts.td @@ -1,16 +1,23 @@ include "llvm/Option/OptParser.td" multiclass Eq<string name, string help> { - def NAME : Separate<["--", "-"], name>; - def NAME #_eq : Joined<["--", "-"], name #"=">, + def NAME : Separate<["--"], name>; + def NAME #_eq : Joined<["--"], name #"=">, Alias<!cast<Separate>(NAME)>, HelpText<help>; } -def help : Flag<["-", "--"], "help">; +def help : Flag<["--"], "help">; +def h : Flag<["-"], "h">, Alias<help>; + +def allow_broken_links + : Flag<["--"], "allow-broken-links">, + HelpText<"Allow llvm-strip to remove sections even if it would leave " + "invalid section references. The appropriate sh_link fields " + "will be set to zero.">; def enable_deterministic_archives - : Flag<["-", "--"], "enable-deterministic-archives">, + : Flag<["--"], "enable-deterministic-archives">, HelpText<"Enable deterministic mode when stripping archives (use zero " "for UIDs, GIDs, and timestamps).">; def D : Flag<["-"], "D">, @@ -18,50 +25,72 @@ def D : Flag<["-"], "D">, HelpText<"Alias for --enable-deterministic-archives">; def disable_deterministic_archives - : Flag<["-", "--"], "disable-deterministic-archives">, + : Flag<["--"], "disable-deterministic-archives">, HelpText<"Disable deterministic mode when stripping archives (use real " "values for UIDs, GIDs, and timestamps).">; def U : Flag<["-"], "U">, Alias<disable_deterministic_archives>, HelpText<"Alias for --disable-deterministic-archives">; -defm output : Eq<"o", "Write output to <file>">, MetaVarName<"output">; +def output : JoinedOrSeparate<["-"], "o">, HelpText<"Write output to <file>">; -def preserve_dates : Flag<["-", "--"], "preserve-dates">, +def preserve_dates : Flag<["--"], "preserve-dates">, HelpText<"Preserve access and modification timestamps">; def p : Flag<["-"], "p">, Alias<preserve_dates>; -def strip_all - : Flag<["-", "--"], "strip-all">, - HelpText< - "Remove non-allocated sections other than .gnu.warning* sections">; +def strip_all : Flag<["--"], "strip-all">, + HelpText<"Remove non-allocated sections outside segments. " + ".gnu.warning* sections are not removed">; def s : Flag<["-"], "s">, Alias<strip_all>; +def no_strip_all : Flag<["--"], "no-strip-all">, + HelpText<"Disable --strip-all">; -def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">, +def strip_all_gnu : Flag<["--"], "strip-all-gnu">, HelpText<"Compatible with GNU strip's --strip-all">; -def strip_debug : Flag<["-", "--"], "strip-debug">, +def strip_debug : Flag<["--"], "strip-debug">, HelpText<"Remove debugging symbols only">; def d : Flag<["-"], "d">, Alias<strip_debug>; def g : Flag<["-"], "g">, Alias<strip_debug>; def S : Flag<["-"], "S">, Alias<strip_debug>; -def strip_unneeded : Flag<["-", "--"], "strip-unneeded">, +def strip_unneeded : Flag<["--"], "strip-unneeded">, HelpText<"Remove all symbols not needed by relocations">; defm remove_section : Eq<"remove-section", "Remove <section>">, MetaVarName<"section">; def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>; +defm strip_symbol : Eq<"strip-symbol", "Strip <symbol>">, + MetaVarName<"symbol">; +def N : JoinedOrSeparate<["-"], "N">, Alias<strip_symbol>; + defm keep_section : Eq<"keep-section", "Keep <section>">, MetaVarName<"section">; defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, MetaVarName<"symbol">; +def keep_file_symbols : Flag<["--"], "keep-file-symbols">, + HelpText<"Do not remove file symbols">; + def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>; +def only_keep_debug + : Flag<["--"], "only-keep-debug">, + HelpText<"Clear sections that would not be stripped by --strip-debug. " + "Currently only implemented for COFF.">; + +def discard_locals : Flag<["--"], "discard-locals">, + HelpText<"Remove compiler-generated local symbols, (e.g. " + "symbols starting with .L)">; +def X : Flag<["-"], "X">, Alias<discard_locals>; + def discard_all - : Flag<["-", "--"], "discard-all">, + : Flag<["--"], "discard-all">, HelpText<"Remove all local symbols except file and section symbols">; def x : Flag<["-"], "x">, Alias<discard_all>; -def version : Flag<["-", "--"], "version">, +def regex + : Flag<["--"], "regex">, + HelpText<"Permit regular expressions in name comparison">; + +def version : Flag<["--"], "version">, HelpText<"Print the version and exit.">; def V : Flag<["-"], "V">, Alias<version>; diff --git a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp index fb1ff18b015b..e9372176e43b 100644 --- a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -1,17 +1,17 @@ //===- llvm-objcopy.cpp ---------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 "llvm-objcopy.h" #include "Buffer.h" -#include "COFF/COFFObjcopy.h" #include "CopyConfig.h" #include "ELF/ELFObjcopy.h" +#include "COFF/COFFObjcopy.h" +#include "MachO/MachOObjcopy.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" @@ -24,6 +24,7 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ELFTypes.h" #include "llvm/Object/Error.h" +#include "llvm/Object/MachO.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" @@ -52,16 +53,23 @@ namespace objcopy { StringRef ToolName; LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { - WithColor::error(errs(), ToolName) << Message << ".\n"; - errs().flush(); + WithColor::error(errs(), ToolName) << Message << "\n"; + exit(1); +} + +LLVM_ATTRIBUTE_NORETURN void error(Error E) { + assert(E); + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(std::move(E), OS); + OS.flush(); + WithColor::error(errs(), ToolName) << Buf; exit(1); } LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) { assert(EC); - WithColor::error(errs(), ToolName) - << "'" << File << "': " << EC.message() << ".\n"; - exit(1); + error(createFileError(File, EC)); } LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { @@ -74,6 +82,12 @@ LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { exit(1); } +ErrorSuccess reportWarning(Error E) { + assert(E); + WithColor::warning(errs(), ToolName) << toString(std::move(E)); + return Error::success(); +} + } // end namespace objcopy } // end namespace llvm @@ -87,10 +101,13 @@ static Error deepWriteArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers, bool WriteSymtab, object::Archive::Kind Kind, bool Deterministic, bool Thin) { - Error E = - writeArchive(ArcName, NewMembers, WriteSymtab, Kind, Deterministic, Thin); - if (!Thin || E) - return E; + if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind, + Deterministic, Thin)) + return createFileError(ArcName, std::move(E)); + + if (!Thin) + 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 @@ -101,132 +118,212 @@ static Error deepWriteArchive(StringRef ArcName, // NewArchiveMember still requires them even though writeArchive does not // write them on disk. FileBuffer FB(Member.MemberName); - FB.allocate(Member.Buf->getBufferSize()); + if (Error E = FB.allocate(Member.Buf->getBufferSize())) + return E; std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), FB.getBufferStart()); - if (auto E = FB.commit()) + if (Error E = FB.commit()) return E; } return Error::success(); } +/// The function executeObjcopyOnIHex does the dispatch based on the format +/// of the output specified by the command line options. +static Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + // TODO: support output formats other than ELF. + return elf::executeObjcopyOnIHex(Config, In, Out); +} + /// The function executeObjcopyOnRawBinary does the dispatch based on the format /// of the output specified by the command line options. -static void executeObjcopyOnRawBinary(const CopyConfig &Config, - MemoryBuffer &In, Buffer &Out) { - // TODO: llvm-objcopy should parse CopyConfig.OutputFormat to recognize - // formats other than ELF / "binary" and invoke - // elf::executeObjcopyOnRawBinary, macho::executeObjcopyOnRawBinary or - // coff::executeObjcopyOnRawBinary accordingly. - return elf::executeObjcopyOnRawBinary(Config, In, Out); +static Error executeObjcopyOnRawBinary(const CopyConfig &Config, + MemoryBuffer &In, Buffer &Out) { + switch (Config.OutputFormat) { + case FileFormat::ELF: + // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the + // output format is binary/ihex or it's not given. This behavior differs from + // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details. + case FileFormat::Binary: + case FileFormat::IHex: + case FileFormat::Unspecified: + return elf::executeObjcopyOnRawBinary(Config, In, Out); + } + + llvm_unreachable("unsupported output format"); } /// The function executeObjcopyOnBinary does the dispatch based on the format /// of the input binary (ELF, MachO or COFF). -static void executeObjcopyOnBinary(const CopyConfig &Config, object::Binary &In, - Buffer &Out) { +static Error executeObjcopyOnBinary(const CopyConfig &Config, + object::Binary &In, Buffer &Out) { if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) 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 - error("Unsupported object file format"); + return createStringError(object_error::invalid_file_type, + "unsupported object file format"); } -static void executeObjcopyOnArchive(const CopyConfig &Config, - const Archive &Ar) { +static Error executeObjcopyOnArchive(const CopyConfig &Config, + const Archive &Ar) { std::vector<NewArchiveMember> NewArchiveMembers; Error Err = Error::success(); for (const Archive::Child &Child : Ar.children(Err)) { - Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); - if (!ChildOrErr) - reportError(Ar.getFileName(), ChildOrErr.takeError()); - Binary *Bin = ChildOrErr->get(); - Expected<StringRef> ChildNameOrErr = Child.getName(); if (!ChildNameOrErr) - reportError(Ar.getFileName(), ChildNameOrErr.takeError()); + return createFileError(Ar.getFileName(), ChildNameOrErr.takeError()); + + Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); + if (!ChildOrErr) + return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", + ChildOrErr.takeError()); MemBuffer MB(ChildNameOrErr.get()); - executeObjcopyOnBinary(Config, *Bin, MB); + if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) + return E; Expected<NewArchiveMember> Member = NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); if (!Member) - reportError(Ar.getFileName(), Member.takeError()); + return createFileError(Ar.getFileName(), Member.takeError()); Member->Buf = MB.releaseMemoryBuffer(); Member->MemberName = Member->Buf->getBufferIdentifier(); NewArchiveMembers.push_back(std::move(*Member)); } - if (Err) - reportError(Config.InputFilename, std::move(Err)); - if (Error E = deepWriteArchive(Config.OutputFilename, NewArchiveMembers, - Ar.hasSymbolTable(), Ar.kind(), - Config.DeterministicArchives, Ar.isThin())) - reportError(Config.OutputFilename, std::move(E)); + return createFileError(Config.InputFilename, std::move(Err)); + + return deepWriteArchive(Config.OutputFilename, NewArchiveMembers, + Ar.hasSymbolTable(), Ar.kind(), + Config.DeterministicArchives, Ar.isThin()); } -static void restoreDateOnFile(StringRef Filename, - const sys::fs::file_status &Stat) { +static Error restoreStatOnFile(StringRef Filename, + const sys::fs::file_status &Stat, + bool PreserveDates) { int FD; + // Writing to stdout should not be treated as an error here, just + // do not set access/modification times or permissions. + if (Filename == "-") + return Error::success(); + if (auto EC = sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) - reportError(Filename, EC); + return createFileError(Filename, EC); + + if (PreserveDates) + if (auto EC = sys::fs::setLastAccessAndModificationTime( + FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) + return createFileError(Filename, EC); - if (auto EC = sys::fs::setLastAccessAndModificationTime( - FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) - reportError(Filename, EC); + 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) +#ifdef _WIN32 + if (auto EC = sys::fs::setPermissions( + Filename, static_cast<sys::fs::perms>(Stat.permissions() & + ~sys::fs::getUmask()))) +#else + if (auto EC = sys::fs::setPermissions( + FD, static_cast<sys::fs::perms>(Stat.permissions() & + ~sys::fs::getUmask()))) +#endif + return createFileError(Filename, EC); if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) - reportError(Filename, EC); + return createFileError(Filename, EC); + + return Error::success(); } /// 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 void executeObjcopy(const CopyConfig &Config) { +static Error executeObjcopy(const CopyConfig &Config) { sys::fs::file_status Stat; - if (Config.PreserveDates) + if (Config.InputFilename != "-") { if (auto EC = sys::fs::status(Config.InputFilename, Stat)) - reportError(Config.InputFilename, EC); + return createFileError(Config.InputFilename, EC); + } else { + Stat.permissions(static_cast<sys::fs::perms>(0777)); + } - if (Config.InputFormat == "binary") { - auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename); + typedef Error (*ProcessRawFn)(const CopyConfig &, MemoryBuffer &, Buffer &); + ProcessRawFn ProcessRaw; + switch (Config.InputFormat) { + case FileFormat::Binary: + ProcessRaw = executeObjcopyOnRawBinary; + break; + case FileFormat::IHex: + ProcessRaw = executeObjcopyOnIHex; + break; + default: + ProcessRaw = nullptr; + } + + if (ProcessRaw) { + auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); if (!BufOrErr) - reportError(Config.InputFilename, BufOrErr.getError()); + return createFileError(Config.InputFilename, BufOrErr.getError()); FileBuffer FB(Config.OutputFilename); - executeObjcopyOnRawBinary(Config, *BufOrErr->get(), FB); + if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) + return E; } else { Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = createBinary(Config.InputFilename); if (!BinaryOrErr) - reportError(Config.InputFilename, BinaryOrErr.takeError()); + return createFileError(Config.InputFilename, BinaryOrErr.takeError()); if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { - executeObjcopyOnArchive(Config, *Ar); + if (Error E = executeObjcopyOnArchive(Config, *Ar)) + return E; } else { FileBuffer FB(Config.OutputFilename); - executeObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB); + if (Error E = executeObjcopyOnBinary(Config, + *BinaryOrErr.get().getBinary(), FB)) + return E; } } - if (Config.PreserveDates) { - restoreDateOnFile(Config.OutputFilename, Stat); - if (!Config.SplitDWO.empty()) - restoreDateOnFile(Config.SplitDWO, Stat); + if (Error E = + restoreStatOnFile(Config.OutputFilename, Stat, Config.PreserveDates)) + return E; + + if (!Config.SplitDWO.empty()) { + Stat.permissions(static_cast<sys::fs::perms>(0666)); + if (Error E = + restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates)) + return E; } + + return Error::success(); } int main(int argc, char **argv) { InitLLVM X(argc, argv); ToolName = argv[0]; - DriverConfig DriverConfig; - if (sys::path::stem(ToolName).contains("strip")) - DriverConfig = parseStripOptions(makeArrayRef(argv + 1, argc)); - else - DriverConfig = parseObjcopyOptions(makeArrayRef(argv + 1, argc)); - for (const CopyConfig &CopyConfig : DriverConfig.CopyConfigs) - executeObjcopy(CopyConfig); + bool IsStrip = sys::path::stem(ToolName).contains("strip"); + Expected<DriverConfig> DriverConfig = + IsStrip ? parseStripOptions(makeArrayRef(argv + 1, argc), reportWarning) + : parseObjcopyOptions(makeArrayRef(argv + 1, argc)); + if (!DriverConfig) { + logAllUnhandledErrors(DriverConfig.takeError(), + WithColor::error(errs(), ToolName)); + return 1; + } + for (const CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { + if (Error E = executeObjcopy(CopyConfig)) { + logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); + return 1; + } + } + + return 0; } diff --git a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h index d8edf3e29ee0..18a789ca1f83 100644 --- a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h +++ b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h @@ -1,9 +1,8 @@ //===- llvm-objcopy.h -------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -20,6 +19,7 @@ namespace llvm { namespace objcopy { LLVM_ATTRIBUTE_NORETURN extern void error(Twine Message); +LLVM_ATTRIBUTE_NORETURN extern void error(Error E); LLVM_ATTRIBUTE_NORETURN extern void reportError(StringRef File, Error E); LLVM_ATTRIBUTE_NORETURN extern void reportError(StringRef File, std::error_code EC); diff --git a/contrib/llvm/tools/llvm-objdump/COFFDump.cpp b/contrib/llvm/tools/llvm-objdump/COFFDump.cpp index 55607ec299be..1ba0a68902c9 100644 --- a/contrib/llvm/tools/llvm-objdump/COFFDump.cpp +++ b/contrib/llvm/tools/llvm-objdump/COFFDump.cpp @@ -1,9 +1,8 @@ //===-- COFFDump.cpp - COFF-specific dumper ---------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -25,10 +24,10 @@ #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" -using namespace llvm; -using namespace object; +using namespace llvm::object; using namespace llvm::Win64EH; +namespace llvm { // Returns the name of the unwind code. static StringRef getUnwindCodeTypeName(uint8_t Code) { switch(Code) { @@ -156,70 +155,68 @@ static void printAllUnwindCodes(ArrayRef<UnwindCode> UCs) { } // Given a symbol sym this functions returns the address and section of it. -static std::error_code -resolveSectionAndAddress(const COFFObjectFile *Obj, const SymbolRef &Sym, - const coff_section *&ResolvedSection, - uint64_t &ResolvedAddr) { +static Error resolveSectionAndAddress(const COFFObjectFile *Obj, + const SymbolRef &Sym, + const coff_section *&ResolvedSection, + uint64_t &ResolvedAddr) { Expected<uint64_t> ResolvedAddrOrErr = Sym.getAddress(); if (!ResolvedAddrOrErr) - return errorToErrorCode(ResolvedAddrOrErr.takeError()); + return ResolvedAddrOrErr.takeError(); ResolvedAddr = *ResolvedAddrOrErr; Expected<section_iterator> Iter = Sym.getSection(); if (!Iter) - return errorToErrorCode(Iter.takeError()); + return Iter.takeError(); ResolvedSection = Obj->getCOFFSection(**Iter); - return std::error_code(); + return Error::success(); } // Given a vector of relocations for a section and an offset into this section // the function returns the symbol used for the relocation at the offset. -static std::error_code resolveSymbol(const std::vector<RelocationRef> &Rels, +static Error resolveSymbol(const std::vector<RelocationRef> &Rels, uint64_t Offset, SymbolRef &Sym) { for (auto &R : Rels) { uint64_t Ofs = R.getOffset(); if (Ofs == Offset) { Sym = *R.getSymbol(); - return std::error_code(); + return Error::success(); } } - return object_error::parse_failed; + return make_error<BinaryError>(); } // Given a vector of relocations for a section and an offset into this section // the function resolves the symbol used for the relocation at the offset and // returns the section content and the address inside the content pointed to // by the symbol. -static std::error_code +static Error getSectionContents(const COFFObjectFile *Obj, const std::vector<RelocationRef> &Rels, uint64_t Offset, ArrayRef<uint8_t> &Contents, uint64_t &Addr) { SymbolRef Sym; - if (std::error_code EC = resolveSymbol(Rels, Offset, Sym)) - return EC; + if (Error E = resolveSymbol(Rels, Offset, Sym)) + return E; const coff_section *Section; - if (std::error_code EC = resolveSectionAndAddress(Obj, Sym, Section, Addr)) - return EC; - if (std::error_code EC = Obj->getSectionContents(Section, Contents)) - return EC; - return std::error_code(); + if (Error E = resolveSectionAndAddress(Obj, Sym, Section, Addr)) + return E; + return Obj->getSectionContents(Section, Contents); } // Given a vector of relocations for a section and an offset into this section // the function returns the name of the symbol used for the relocation at the // offset. -static std::error_code resolveSymbolName(const std::vector<RelocationRef> &Rels, - uint64_t Offset, StringRef &Name) { +static Error resolveSymbolName(const std::vector<RelocationRef> &Rels, + uint64_t Offset, StringRef &Name) { SymbolRef Sym; - if (std::error_code EC = resolveSymbol(Rels, Offset, Sym)) + if (Error EC = resolveSymbol(Rels, Offset, Sym)) return EC; Expected<StringRef> NameOrErr = Sym.getName(); if (!NameOrErr) - return errorToErrorCode(NameOrErr.takeError()); + return NameOrErr.takeError(); Name = *NameOrErr; - return std::error_code(); + return Error::success(); } -static void printCOFFSymbolAddress(llvm::raw_ostream &Out, +static void printCOFFSymbolAddress(raw_ostream &Out, const std::vector<RelocationRef> &Rels, uint64_t Offset, uint32_t Disp) { StringRef Sym; @@ -469,6 +466,18 @@ static bool getPDataSection(const COFFObjectFile *Obj, return false; } +Error getCOFFRelocationValueString(const COFFObjectFile *Obj, + const RelocationRef &Rel, + SmallVectorImpl<char> &Result) { + symbol_iterator SymI = Rel.getSymbol(); + Expected<StringRef> SymNameOrErr = SymI->getName(); + if (!SymNameOrErr) + return SymNameOrErr.takeError(); + StringRef SymName = *SymNameOrErr; + Result.append(SymName.begin(), SymName.end()); + return Error::success(); +} + static void printWin64EHUnwindInfo(const Win64EH::UnwindInfo *UI) { // The casts to int are required in order to output the value as number. // Without the casts the value would be interpreted as char data (which @@ -578,7 +587,7 @@ static void printRuntimeFunctionRels(const COFFObjectFile *Obj, printWin64EHUnwindInfo(UI); } -void llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) { +void printCOFFUnwindInfo(const COFFObjectFile *Obj) { if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_AMD64) { WithColor::error(errs(), "llvm-objdump") << "unsupported image machine type " @@ -607,7 +616,7 @@ void llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) { } } -void llvm::printCOFFFileHeader(const object::ObjectFile *Obj) { +void printCOFFFileHeader(const object::ObjectFile *Obj) { const COFFObjectFile *file = dyn_cast<const COFFObjectFile>(Obj); printTLSDirectory(file); printLoadConfiguration(file); @@ -615,7 +624,7 @@ void llvm::printCOFFFileHeader(const object::ObjectFile *Obj) { printExportTable(file); } -void llvm::printCOFFSymbolTable(const object::COFFImportFile *i) { +void printCOFFSymbolTable(const object::COFFImportFile *i) { unsigned Index = 0; bool IsCode = i->getCOFFImportHeader()->getType() == COFF::IMPORT_CODE; @@ -623,7 +632,7 @@ void llvm::printCOFFSymbolTable(const object::COFFImportFile *i) { std::string Name; raw_string_ostream NS(Name); - Sym.printName(NS); + cantFail(Sym.printName(NS)); NS.flush(); outs() << "[" << format("%2d", Index) << "]" @@ -638,11 +647,11 @@ void llvm::printCOFFSymbolTable(const object::COFFImportFile *i) { } } -void llvm::printCOFFSymbolTable(const COFFObjectFile *coff) { +void printCOFFSymbolTable(const COFFObjectFile *coff) { for (unsigned SI = 0, SE = coff->getNumberOfSymbols(); SI != SE; ++SI) { Expected<COFFSymbolRef> Symbol = coff->getSymbol(SI); StringRef Name; - error(errorToErrorCode(Symbol.takeError())); + error(Symbol.takeError()); error(coff->getSymbolName(*Symbol, Name)); outs() << "[" << format("%2d", SI) << "]" @@ -709,3 +718,4 @@ void llvm::printCOFFSymbolTable(const COFFObjectFile *coff) { } } } +} // namespace llvm diff --git a/contrib/llvm/tools/llvm-objdump/ELFDump.cpp b/contrib/llvm/tools/llvm-objdump/ELFDump.cpp index b17a15a0d8fc..9c4d67d0f1bd 100644 --- a/contrib/llvm/tools/llvm-objdump/ELFDump.cpp +++ b/contrib/llvm/tools/llvm-objdump/ELFDump.cpp @@ -1,9 +1,8 @@ //===-- ELFDump.cpp - ELF-specific dumper -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -13,23 +12,22 @@ //===----------------------------------------------------------------------===// #include "llvm-objdump.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Format.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" -using namespace llvm; using namespace llvm::object; +namespace llvm { template <class ELFT> -Expected<StringRef> getDynamicStrTab(const ELFFile<ELFT> *Elf) { - typedef ELFFile<ELFT> ELFO; - +static Expected<StringRef> getDynamicStrTab(const ELFFile<ELFT> *Elf) { auto DynamicEntriesOrError = Elf->dynamicEntries(); if (!DynamicEntriesOrError) return DynamicEntriesOrError.takeError(); - for (const typename ELFO::Elf_Dyn &Dyn : *DynamicEntriesOrError) { + for (const typename ELFT::Dyn &Dyn : *DynamicEntriesOrError) { if (Dyn.d_tag == ELF::DT_STRTAB) { auto MappedAddrOrError = Elf->toMappedAddr(Dyn.getPtr()); if (!MappedAddrOrError) @@ -43,7 +41,7 @@ Expected<StringRef> getDynamicStrTab(const ELFFile<ELFT> *Elf) { if (!SectionsOrError) return SectionsOrError.takeError(); - for (const typename ELFO::Elf_Shdr &Sec : *SectionsOrError) { + for (const typename ELFT::Shdr &Sec : *SectionsOrError) { if (Sec.sh_type == ELF::SHT_DYNSYM) return Elf->getStringTableForSymtab(Sec); } @@ -52,40 +50,135 @@ Expected<StringRef> getDynamicStrTab(const ELFFile<ELFT> *Elf) { } template <class ELFT> -void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) { - auto ProgramHeaderOrError = Elf->program_headers(); - if (!ProgramHeaderOrError) - report_error(Filename, ProgramHeaderOrError.takeError()); +static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj, + const RelocationRef &RelRef, + SmallVectorImpl<char> &Result) { + const ELFFile<ELFT> &EF = *Obj->getELFFile(); + DataRefImpl Rel = RelRef.getRawDataRefImpl(); + auto SecOrErr = EF.getSection(Rel.d.a); + if (!SecOrErr) + return SecOrErr.takeError(); - auto DynamicEntriesOrError = Elf->dynamicEntries(); - if (!DynamicEntriesOrError) - report_error(Filename, DynamicEntriesOrError.takeError()); + int64_t Addend = 0; + // If there is no Symbol associated with the relocation, we set the undef + // boolean value to 'true'. This will prevent us from calling functions that + // requires the relocation to be associated with a symbol. + // + // In SHT_REL case we would need to read the addend from section data. + // GNU objdump does not do that and we just follow for simplicity atm. + bool Undef = false; + if ((*SecOrErr)->sh_type == ELF::SHT_RELA) { + 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) { + return make_error<BinaryError>(); + } + + // Default scheme is to print Target, as well as "+ <addend>" for nonzero + // addend. Should be acceptable for all normal purposes. + std::string FmtBuf; + raw_string_ostream Fmt(FmtBuf); + + if (!Undef) { + symbol_iterator SI = RelRef.getSymbol(); + const typename ELFT::Sym *Sym = Obj->getSymbol(SI->getRawDataRefImpl()); + if (Sym->getType() == ELF::STT_SECTION) { + Expected<section_iterator> SymSI = SI->getSection(); + if (!SymSI) + return SymSI.takeError(); + const typename ELFT::Shdr *SymSec = + Obj->getSection((*SymSI)->getRawDataRefImpl()); + auto SecName = EF.getSectionName(SymSec); + if (!SecName) + return SecName.takeError(); + Fmt << *SecName; + } else { + Expected<StringRef> SymName = SI->getName(); + if (!SymName) + return SymName.takeError(); + if (Demangle) + Fmt << demangle(*SymName); + else + Fmt << *SymName; + } + } else { + Fmt << "*ABS*"; + } + + if (Addend != 0) + Fmt << (Addend < 0 ? "" : "+") << Addend; + Fmt.flush(); + Result.append(FmtBuf.begin(), FmtBuf.end()); + return Error::success(); +} +Error getELFRelocationValueString(const ELFObjectFileBase *Obj, + const RelocationRef &Rel, + SmallVectorImpl<char> &Result) { + if (auto *ELF32LE = dyn_cast<ELF32LEObjectFile>(Obj)) + return getRelocationValueString(ELF32LE, Rel, Result); + if (auto *ELF64LE = dyn_cast<ELF64LEObjectFile>(Obj)) + return getRelocationValueString(ELF64LE, Rel, Result); + if (auto *ELF32BE = dyn_cast<ELF32BEObjectFile>(Obj)) + return getRelocationValueString(ELF32BE, Rel, Result); + auto *ELF64BE = cast<ELF64BEObjectFile>(Obj); + return getRelocationValueString(ELF64BE, Rel, Result); +} + +template <class ELFT> +static uint64_t getSectionLMA(const ELFFile<ELFT> *Obj, + const object::ELFSectionRef &Sec) { + auto PhdrRangeOrErr = Obj->program_headers(); + if (!PhdrRangeOrErr) + report_fatal_error(toString(PhdrRangeOrErr.takeError())); + + // Search for a PT_LOAD segment containing the requested section. Use this + // segment's p_addr to calculate the section's LMA. + for (const typename ELFT::Phdr &Phdr : *PhdrRangeOrErr) + if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_vaddr <= Sec.getAddress()) && + (Phdr.p_vaddr + Phdr.p_memsz > Sec.getAddress())) + return Sec.getAddress() - Phdr.p_vaddr + Phdr.p_paddr; + + // Return section's VMA if it isn't in a PT_LOAD segment. + return Sec.getAddress(); +} + +uint64_t getELFSectionLMA(const object::ELFSectionRef &Sec) { + if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Sec.getObject())) + return getSectionLMA(ELFObj->getELFFile(), Sec); + else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Sec.getObject())) + return getSectionLMA(ELFObj->getELFFile(), Sec); + else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Sec.getObject())) + return getSectionLMA(ELFObj->getELFFile(), Sec); + const auto *ELFObj = cast<ELF64BEObjectFile>(Sec.getObject()); + return getSectionLMA(ELFObj->getELFFile(), Sec); +} + +template <class ELFT> +void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) { + ArrayRef<typename ELFT::Dyn> DynamicEntries = + unwrapOrError(Elf->dynamicEntries(), Filename); outs() << "Dynamic Section:\n"; - for (const auto &Dyn : *DynamicEntriesOrError) { + for (const typename ELFT::Dyn &Dyn : DynamicEntries) { if (Dyn.d_tag == ELF::DT_NULL) continue; - StringRef Str = StringRef(Elf->getDynamicTagAsString(Dyn.d_tag)); - - if (Str.empty()) { - std::string HexStr = utohexstr(static_cast<uint64_t>(Dyn.d_tag), true); - outs() << format(" 0x%-19s", HexStr.c_str()); - } else { - // We use "-21" in order to match GNU objdump's output. - outs() << format(" %-21s", Str.data()); - } + std::string Str = Elf->getDynamicTagAsString(Dyn.d_tag); + outs() << format(" %-21s", Str.c_str()); const char *Fmt = ELFT::Is64Bits ? "0x%016" PRIx64 "\n" : "0x%08" PRIx64 "\n"; - if (Dyn.d_tag == ELF::DT_NEEDED) { + if (Dyn.d_tag == ELF::DT_NEEDED || Dyn.d_tag == ELF::DT_RPATH || + Dyn.d_tag == ELF::DT_RUNPATH || Dyn.d_tag == ELF::DT_SONAME || + Dyn.d_tag == ELF::DT_AUXILIARY || Dyn.d_tag == ELF::DT_FILTER) { Expected<StringRef> StrTabOrErr = getDynamicStrTab(Elf); if (StrTabOrErr) { const char *Data = StrTabOrErr.get().data(); outs() << (Data + Dyn.d_un.d_val) << "\n"; continue; } - warn(errorToErrorCode(StrTabOrErr.takeError()).message()); + warn(toString(StrTabOrErr.takeError())); consumeError(StrTabOrErr.takeError()); } outs() << format(Fmt, (uint64_t)Dyn.d_un.d_val); @@ -93,13 +186,11 @@ void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) { } template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) { - typedef ELFFile<ELFT> ELFO; outs() << "Program Header:\n"; auto ProgramHeaderOrError = o->program_headers(); if (!ProgramHeaderOrError) - report_fatal_error( - errorToErrorCode(ProgramHeaderOrError.takeError()).message()); - for (const typename ELFO::Elf_Phdr &Phdr : *ProgramHeaderOrError) { + report_fatal_error(toString(ProgramHeaderOrError.takeError())); + for (const typename ELFT::Phdr &Phdr : *ProgramHeaderOrError) { switch (Phdr.p_type) { case ELF::PT_DYNAMIC: outs() << " DYNAMIC "; @@ -157,7 +248,86 @@ template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) { outs() << "\n"; } -void llvm::printELFFileHeader(const object::ObjectFile *Obj) { +template <class ELFT> +void printSymbolVersionDependency(ArrayRef<uint8_t> Contents, + StringRef StrTab) { + outs() << "Version References:\n"; + + const uint8_t *Buf = Contents.data(); + while (Buf) { + auto *Verneed = reinterpret_cast<const typename ELFT::Verneed *>(Buf); + outs() << " required from " + << StringRef(StrTab.drop_front(Verneed->vn_file).data()) << ":\n"; + + const uint8_t *BufAux = Buf + Verneed->vn_aux; + while (BufAux) { + auto *Vernaux = reinterpret_cast<const typename ELFT::Vernaux *>(BufAux); + outs() << " " + << format("0x%08" PRIx32 " ", (uint32_t)Vernaux->vna_hash) + << format("0x%02" PRIx16 " ", (uint16_t)Vernaux->vna_flags) + << format("%02" PRIu16 " ", (uint16_t)Vernaux->vna_other) + << StringRef(StrTab.drop_front(Vernaux->vna_name).data()) << '\n'; + BufAux = Vernaux->vna_next ? BufAux + Vernaux->vna_next : nullptr; + } + Buf = Verneed->vn_next ? Buf + Verneed->vn_next : nullptr; + } +} + +template <class ELFT> +void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr, + ArrayRef<uint8_t> Contents, + StringRef StrTab) { + outs() << "Version definitions:\n"; + + const uint8_t *Buf = Contents.data(); + uint32_t VerdefIndex = 1; + // sh_info contains the number of entries in the SHT_GNU_verdef section. To + // make the index column have consistent width, we should insert blank spaces + // according to sh_info. + uint16_t VerdefIndexWidth = std::to_string(Shdr.sh_info).size(); + while (Buf) { + auto *Verdef = reinterpret_cast<const typename ELFT::Verdef *>(Buf); + outs() << format_decimal(VerdefIndex++, VerdefIndexWidth) << " " + << format("0x%02" PRIx16 " ", (uint16_t)Verdef->vd_flags) + << format("0x%08" PRIx32 " ", (uint32_t)Verdef->vd_hash); + + const uint8_t *BufAux = Buf + Verdef->vd_aux; + uint16_t VerdauxIndex = 0; + while (BufAux) { + auto *Verdaux = reinterpret_cast<const typename ELFT::Verdaux *>(BufAux); + if (VerdauxIndex) + outs() << std::string(VerdefIndexWidth + 17, ' '); + outs() << StringRef(StrTab.drop_front(Verdaux->vda_name).data()) << '\n'; + BufAux = Verdaux->vda_next ? BufAux + Verdaux->vda_next : nullptr; + ++VerdauxIndex; + } + Buf = Verdef->vd_next ? Buf + Verdef->vd_next : nullptr; + } +} + +template <class ELFT> +void printSymbolVersionInfo(const ELFFile<ELFT> *Elf, StringRef FileName) { + ArrayRef<typename ELFT::Shdr> Sections = + unwrapOrError(Elf->sections(), FileName); + for (const typename ELFT::Shdr &Shdr : Sections) { + if (Shdr.sh_type != ELF::SHT_GNU_verneed && + Shdr.sh_type != ELF::SHT_GNU_verdef) + continue; + + ArrayRef<uint8_t> Contents = + unwrapOrError(Elf->getSectionContents(&Shdr), FileName); + const typename ELFT::Shdr *StrTabSec = + unwrapOrError(Elf->getSection(Shdr.sh_link), FileName); + StringRef StrTab = unwrapOrError(Elf->getStringTable(StrTabSec), FileName); + + if (Shdr.sh_type == ELF::SHT_GNU_verneed) + printSymbolVersionDependency<ELFT>(Contents, StrTab); + else + printSymbolVersionDefinition<ELFT>(Shdr, Contents, StrTab); + } +} + +void printELFFileHeader(const object::ObjectFile *Obj) { if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) printProgramHeaders(ELFObj->getELFFile()); else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) @@ -168,7 +338,7 @@ void llvm::printELFFileHeader(const object::ObjectFile *Obj) { printProgramHeaders(ELFObj->getELFFile()); } -void llvm::printELFDynamicSection(const object::ObjectFile *Obj) { +void printELFDynamicSection(const object::ObjectFile *Obj) { if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) printDynamicSection(ELFObj->getELFFile(), Obj->getFileName()); else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) @@ -178,3 +348,15 @@ void llvm::printELFDynamicSection(const object::ObjectFile *Obj) { else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj)) printDynamicSection(ELFObj->getELFFile(), Obj->getFileName()); } + +void printELFSymbolVersionInfo(const object::ObjectFile *Obj) { + if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) + printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName()); + else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) + printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName()); + else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj)) + printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName()); + else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj)) + printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName()); +} +} // namespace llvm diff --git a/contrib/llvm/tools/llvm-objdump/MachODump.cpp b/contrib/llvm/tools/llvm-objdump/MachODump.cpp index 5ef7058ec9da..58ff7be4543c 100644 --- a/contrib/llvm/tools/llvm-objdump/MachODump.cpp +++ b/contrib/llvm/tools/llvm-objdump/MachODump.cpp @@ -1,9 +1,8 @@ //===-- MachODump.cpp - Object file dumping utility for llvm --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -56,83 +55,140 @@ extern "C" { } #endif -using namespace llvm; -using namespace object; +using namespace llvm::object; + +namespace llvm { + +cl::OptionCategory MachOCat("llvm-objdump MachO Specific Options"); + +extern cl::opt<bool> ArchiveHeaders; +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<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> SymbolTable; +extern cl::opt<std::string> TripleName; +extern cl::opt<bool> UnwindInfo; + +cl::opt<bool> + FirstPrivateHeader("private-header", + cl::desc("Display only the first format specific file " + "header"), + cl::cat(MachOCat)); + +cl::opt<bool> ExportsTrie("exports-trie", + cl::desc("Display mach-o exported symbols"), + cl::cat(MachOCat)); + +cl::opt<bool> Rebase("rebase", cl::desc("Display mach-o rebasing info"), + cl::cat(MachOCat)); + +cl::opt<bool> Bind("bind", cl::desc("Display mach-o binding info"), + cl::cat(MachOCat)); + +cl::opt<bool> LazyBind("lazy-bind", + cl::desc("Display mach-o lazy binding info"), + cl::cat(MachOCat)); + +cl::opt<bool> WeakBind("weak-bind", + cl::desc("Display mach-o weak binding info"), + cl::cat(MachOCat)); static cl::opt<bool> - UseDbg("g", - cl::desc("Print line information from debug info if available")); + 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::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::desc("Print full leading address"), + cl::cat(MachOCat)); static cl::opt<bool> NoLeadingHeaders("no-leading-headers", - cl::desc("Print no leading headers")); + cl::desc("Print no leading headers"), + cl::cat(MachOCat)); -cl::opt<bool> llvm::UniversalHeaders("universal-headers", - cl::desc("Print Mach-O universal headers " - "(requires -macho)")); +cl::opt<bool> UniversalHeaders("universal-headers", + cl::desc("Print Mach-O universal headers " + "(requires -macho)"), + cl::cat(MachOCat)); 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::opt<bool> - llvm::IndirectSymbols("indirect-symbols", - cl::desc("Print indirect symbol table for Mach-O " - "objects (requires -macho)")); + "-archive-headers)"), + cl::cat(MachOCat)); -cl::opt<bool> - llvm::DataInCode("data-in-code", - cl::desc("Print the data in code table for Mach-O objects " - "(requires -macho)")); - -cl::opt<bool> - llvm::LinkOptHints("link-opt-hints", - cl::desc("Print the linker optimization hints for " - "Mach-O objects (requires -macho)")); - -cl::opt<bool> - llvm::InfoPlist("info-plist", - cl::desc("Print the info plist section as strings for " - "Mach-O objects (requires -macho)")); +cl::opt<bool> IndirectSymbols("indirect-symbols", + cl::desc("Print indirect symbol table for Mach-O " + "objects (requires -macho)"), + cl::cat(MachOCat)); cl::opt<bool> - llvm::DylibsUsed("dylibs-used", - cl::desc("Print the shared libraries used for linked " - "Mach-O files (requires -macho)")); + DataInCode("data-in-code", + cl::desc("Print the data in code table for Mach-O objects " + "(requires -macho)"), + cl::cat(MachOCat)); + +cl::opt<bool> LinkOptHints("link-opt-hints", + cl::desc("Print the linker optimization hints for " + "Mach-O objects (requires -macho)"), + cl::cat(MachOCat)); + +cl::opt<bool> InfoPlist("info-plist", + cl::desc("Print the info plist section as strings for " + "Mach-O objects (requires -macho)"), + cl::cat(MachOCat)); + +cl::opt<bool> DylibsUsed("dylibs-used", + cl::desc("Print the shared libraries used for linked " + "Mach-O files (requires -macho)"), + cl::cat(MachOCat)); cl::opt<bool> - llvm::DylibId("dylib-id", - cl::desc("Print the shared library's id for the dylib Mach-O " - "file (requires -macho)")); + DylibId("dylib-id", + cl::desc("Print the shared library's id for the dylib Mach-O " + "file (requires -macho)"), + cl::cat(MachOCat)); cl::opt<bool> - llvm::NonVerbose("non-verbose", - cl::desc("Print the info for Mach-O objects in " - "non-verbose or numeric form (requires -macho)")); + 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> - llvm::ObjcMetaData("objc-meta-data", - cl::desc("Print the Objective-C runtime meta data for " - "Mach-O files (requires -macho)")); + ObjcMetaData("objc-meta-data", + cl::desc("Print the Objective-C runtime meta data for " + "Mach-O files (requires -macho)"), + cl::cat(MachOCat)); -cl::opt<std::string> llvm::DisSymName( +cl::opt<std::string> DisSymName( "dis-symname", - cl::desc("disassemble just this symbol's instructions (requires -macho)")); + 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::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::ZeroOrMore, cl::cat(MachOCat)); bool ArchAll = false; @@ -142,7 +198,7 @@ static const Target *GetTarget(const MachOObjectFile *MachOObj, const char **McpuDefault, const Target **ThumbTarget) { // Figure out the target triple. - llvm::Triple TT(TripleName); + Triple TT(TripleName); if (TripleName.empty()) { TT = MachOObj->getArchTriple(McpuDefault); TripleName = TT.str(); @@ -151,7 +207,7 @@ static const Target *GetTarget(const MachOObjectFile *MachOObj, if (TT.getArch() == Triple::arm) { // We've inferred a 32-bit ARM target from the object file. All MachO CPUs // that support ARM are also capable of Thumb mode. - llvm::Triple ThumbTriple = TT; + Triple ThumbTriple = TT; std::string ThumbName = (Twine("thumb") + TT.getArchName().substr(3)).str(); ThumbTriple.setArchName(ThumbName); ThumbTripleName = ThumbTriple.str(); @@ -180,11 +236,11 @@ struct SymbolSorter { bool operator()(const SymbolRef &A, const SymbolRef &B) { Expected<SymbolRef::Type> ATypeOrErr = A.getType(); if (!ATypeOrErr) - report_error(A.getObject()->getFileName(), ATypeOrErr.takeError()); + report_error(ATypeOrErr.takeError(), A.getObject()->getFileName()); SymbolRef::Type AType = *ATypeOrErr; Expected<SymbolRef::Type> BTypeOrErr = B.getType(); if (!BTypeOrErr) - report_error(B.getObject()->getFileName(), BTypeOrErr.takeError()); + report_error(BTypeOrErr.takeError(), B.getObject()->getFileName()); SymbolRef::Type BType = *BTypeOrErr; uint64_t AAddr = (AType != SymbolRef::ST_Function) ? 0 : A.getValue(); uint64_t BAddr = (BType != SymbolRef::ST_Function) ? 0 : B.getValue(); @@ -308,11 +364,10 @@ static void getSectionsAndSymbols(MachOObjectFile *MachOObj, std::vector<SymbolRef> &Symbols, SmallVectorImpl<uint64_t> &FoundFns, uint64_t &BaseSegmentAddress) { + const StringRef FileName = MachOObj->getFileName(); for (const SymbolRef &Symbol : MachOObj->symbols()) { - Expected<StringRef> SymName = Symbol.getName(); - if (!SymName) - report_error(MachOObj->getFileName(), SymName.takeError()); - if (!SymName->startswith("ltmp")) + StringRef SymName = unwrapOrError(Symbol.getName(), FileName); + if (!SymName.startswith("ltmp")) Symbols.push_back(Symbol); } @@ -342,6 +397,254 @@ static void getSectionsAndSymbols(MachOObjectFile *MachOObj, } } +static void printRelocationTargetName(const MachOObjectFile *O, + const MachO::any_relocation_info &RE, + raw_string_ostream &Fmt) { + // Target of a scattered relocation is an address. In the interest of + // generating pretty output, scan through the symbol table looking for a + // symbol that aligns with that address. If we find one, print it. + // Otherwise, we just print the hex address of the target. + const StringRef FileName = O->getFileName(); + if (O->isRelocationScattered(RE)) { + uint32_t Val = O->getPlainRelocationSymbolNum(RE); + + for (const SymbolRef &Symbol : O->symbols()) { + uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName); + if (Addr != Val) + continue; + Fmt << unwrapOrError(Symbol.getName(), FileName); + return; + } + + // If we couldn't find a symbol that this relocation refers to, try + // to find a section beginning instead. + for (const SectionRef &Section : ToolSectionFilter(*O)) { + StringRef Name; + uint64_t Addr = Section.getAddress(); + if (Addr != Val) + continue; + if (std::error_code EC = Section.getName(Name)) + report_error(errorCodeToError(EC), O->getFileName()); + Fmt << Name; + return; + } + + Fmt << format("0x%x", Val); + return; + } + + StringRef S; + bool isExtern = O->getPlainRelocationExternal(RE); + uint64_t Val = O->getPlainRelocationSymbolNum(RE); + + if (O->getAnyRelocationType(RE) == MachO::ARM64_RELOC_ADDEND) { + Fmt << format("0x%0" PRIx64, Val); + return; + } + + if (isExtern) { + symbol_iterator SI = O->symbol_begin(); + advance(SI, Val); + S = unwrapOrError(SI->getName(), FileName); + } else { + section_iterator SI = O->section_begin(); + // Adjust for the fact that sections are 1-indexed. + if (Val == 0) { + Fmt << "0 (?,?)"; + return; + } + uint32_t I = Val - 1; + while (I != 0 && SI != O->section_end()) { + --I; + advance(SI, 1); + } + if (SI == O->section_end()) + Fmt << Val << " (?,?)"; + else + SI->getName(S); + } + + Fmt << S; +} + +Error getMachORelocationValueString(const MachOObjectFile *Obj, + const RelocationRef &RelRef, + SmallVectorImpl<char> &Result) { + DataRefImpl Rel = RelRef.getRawDataRefImpl(); + MachO::any_relocation_info RE = Obj->getRelocation(Rel); + + unsigned Arch = Obj->getArch(); + + std::string FmtBuf; + raw_string_ostream Fmt(FmtBuf); + unsigned Type = Obj->getAnyRelocationType(RE); + bool IsPCRel = Obj->getAnyRelocationPCRel(RE); + + // Determine any addends that should be displayed with the relocation. + // These require decoding the relocation type, which is triple-specific. + + // X86_64 has entirely custom relocation types. + if (Arch == Triple::x86_64) { + switch (Type) { + case MachO::X86_64_RELOC_GOT_LOAD: + case MachO::X86_64_RELOC_GOT: { + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "@GOT"; + if (IsPCRel) + Fmt << "PCREL"; + break; + } + case MachO::X86_64_RELOC_SUBTRACTOR: { + DataRefImpl RelNext = Rel; + Obj->moveRelocationNext(RelNext); + MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); + + // X86_64_RELOC_SUBTRACTOR must be followed by a relocation of type + // X86_64_RELOC_UNSIGNED. + // NOTE: Scattered relocations don't exist on x86_64. + unsigned RType = Obj->getAnyRelocationType(RENext); + if (RType != MachO::X86_64_RELOC_UNSIGNED) + report_error(Obj->getFileName(), "Expected X86_64_RELOC_UNSIGNED after " + "X86_64_RELOC_SUBTRACTOR."); + + // The X86_64_RELOC_UNSIGNED contains the minuend symbol; + // X86_64_RELOC_SUBTRACTOR contains the subtrahend. + printRelocationTargetName(Obj, RENext, Fmt); + Fmt << "-"; + printRelocationTargetName(Obj, RE, Fmt); + break; + } + case MachO::X86_64_RELOC_TLV: + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "@TLV"; + if (IsPCRel) + Fmt << "P"; + break; + case MachO::X86_64_RELOC_SIGNED_1: + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "-1"; + break; + case MachO::X86_64_RELOC_SIGNED_2: + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "-2"; + break; + case MachO::X86_64_RELOC_SIGNED_4: + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "-4"; + break; + default: + printRelocationTargetName(Obj, RE, Fmt); + break; + } + // X86 and ARM share some relocation types in common. + } else if (Arch == Triple::x86 || Arch == Triple::arm || + Arch == Triple::ppc) { + // Generic relocation types... + switch (Type) { + case MachO::GENERIC_RELOC_PAIR: // prints no info + return Error::success(); + case MachO::GENERIC_RELOC_SECTDIFF: { + DataRefImpl RelNext = Rel; + Obj->moveRelocationNext(RelNext); + MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); + + // X86 sect diff's must be followed by a relocation of type + // GENERIC_RELOC_PAIR. + unsigned RType = Obj->getAnyRelocationType(RENext); + + if (RType != MachO::GENERIC_RELOC_PAIR) + report_error(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " + "GENERIC_RELOC_SECTDIFF."); + + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "-"; + printRelocationTargetName(Obj, RENext, Fmt); + break; + } + } + + if (Arch == Triple::x86 || Arch == Triple::ppc) { + switch (Type) { + case MachO::GENERIC_RELOC_LOCAL_SECTDIFF: { + DataRefImpl RelNext = Rel; + Obj->moveRelocationNext(RelNext); + MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); + + // X86 sect diff's must be followed by a relocation of type + // GENERIC_RELOC_PAIR. + unsigned RType = Obj->getAnyRelocationType(RENext); + if (RType != MachO::GENERIC_RELOC_PAIR) + report_error(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " + "GENERIC_RELOC_LOCAL_SECTDIFF."); + + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "-"; + printRelocationTargetName(Obj, RENext, Fmt); + break; + } + case MachO::GENERIC_RELOC_TLV: { + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "@TLV"; + if (IsPCRel) + Fmt << "P"; + break; + } + default: + printRelocationTargetName(Obj, RE, Fmt); + } + } else { // ARM-specific relocations + switch (Type) { + case MachO::ARM_RELOC_HALF: + case MachO::ARM_RELOC_HALF_SECTDIFF: { + // Half relocations steal a bit from the length field to encode + // whether this is an upper16 or a lower16 relocation. + bool isUpper = (Obj->getAnyRelocationLength(RE) & 0x1) == 1; + + if (isUpper) + Fmt << ":upper16:("; + else + Fmt << ":lower16:("; + printRelocationTargetName(Obj, RE, Fmt); + + DataRefImpl RelNext = Rel; + Obj->moveRelocationNext(RelNext); + MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); + + // ARM half relocs must be followed by a relocation of type + // ARM_RELOC_PAIR. + unsigned RType = Obj->getAnyRelocationType(RENext); + if (RType != MachO::ARM_RELOC_PAIR) + report_error(Obj->getFileName(), "Expected ARM_RELOC_PAIR after " + "ARM_RELOC_HALF"); + + // NOTE: The half of the target virtual address is stashed in the + // address field of the secondary relocation, but we can't reverse + // engineer the constant offset from it without decoding the movw/movt + // instruction to find the other half in its immediate field. + + // ARM_RELOC_HALF_SECTDIFF encodes the second section in the + // symbol/section pointer of the follow-on relocation. + if (Type == MachO::ARM_RELOC_HALF_SECTDIFF) { + Fmt << "-"; + printRelocationTargetName(Obj, RENext, Fmt); + } + + Fmt << ")"; + break; + } + default: { + printRelocationTargetName(Obj, RE, Fmt); + } + } + } + } else + printRelocationTargetName(Obj, RE, Fmt); + + Fmt.flush(); + Result.append(FmtBuf.begin(), FmtBuf.end()); + return Error::success(); +} + static void PrintIndirectSymbolTable(MachOObjectFile *O, bool verbose, uint32_t n, uint32_t count, uint32_t stride, uint64_t addr) { @@ -389,10 +692,7 @@ static void PrintIndirectSymbolTable(MachOObjectFile *O, bool verbose, if (indirect_symbol < Symtab.nsyms) { symbol_iterator Sym = O->getSymbolByIndex(indirect_symbol); SymbolRef Symbol = *Sym; - Expected<StringRef> SymName = Symbol.getName(); - if (!SymName) - report_error(O->getFileName(), SymName.takeError()); - outs() << *SymName; + outs() << unwrapOrError(Symbol.getName(), O->getFileName()); } else { outs() << "?"; } @@ -500,6 +800,7 @@ static void PrintRType(const uint64_t cputype, const unsigned r_type) { outs() << arm_r_types[r_type]; break; case MachO::CPU_TYPE_ARM64: + case MachO::CPU_TYPE_ARM64_32: outs() << arm64_r_types[r_type]; break; default: @@ -510,9 +811,8 @@ static void PrintRType(const uint64_t cputype, const unsigned r_type) { static void PrintRLength(const uint64_t cputype, const unsigned r_type, const unsigned r_length, const bool previous_arm_half){ if (cputype == MachO::CPU_TYPE_ARM && - (r_type == llvm::MachO::ARM_RELOC_HALF || - r_type == llvm::MachO::ARM_RELOC_HALF_SECTDIFF || - previous_arm_half == true)) { + (r_type == MachO::ARM_RELOC_HALF || + r_type == MachO::ARM_RELOC_HALF_SECTDIFF || previous_arm_half == true)) { if ((r_length & 0x1) == 0) outs() << "lo/"; else @@ -573,9 +873,8 @@ static void PrintRelocationEntries(const MachOObjectFile *O, if (verbose) { // scattered: address if ((cputype == MachO::CPU_TYPE_I386 && - r_type == llvm::MachO::GENERIC_RELOC_PAIR) || - (cputype == MachO::CPU_TYPE_ARM && - r_type == llvm::MachO::ARM_RELOC_PAIR)) + r_type == MachO::GENERIC_RELOC_PAIR) || + (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR)) outs() << " "; else outs() << format("%08x ", (unsigned int)r_address); @@ -597,29 +896,27 @@ static void PrintRelocationEntries(const MachOObjectFile *O, outs() << format("True 0x%08x", (unsigned int)r_value); if (previous_sectdiff == false) { if ((cputype == MachO::CPU_TYPE_ARM && - r_type == llvm::MachO::ARM_RELOC_PAIR)) + r_type == MachO::ARM_RELOC_PAIR)) outs() << format(" half = 0x%04x ", (unsigned int)r_address); - } - else if (cputype == MachO::CPU_TYPE_ARM && - sectdiff_r_type == llvm::MachO::ARM_RELOC_HALF_SECTDIFF) + } else if (cputype == MachO::CPU_TYPE_ARM && + sectdiff_r_type == MachO::ARM_RELOC_HALF_SECTDIFF) outs() << format(" other_half = 0x%04x ", (unsigned int)r_address); if ((cputype == MachO::CPU_TYPE_I386 && - (r_type == llvm::MachO::GENERIC_RELOC_SECTDIFF || - r_type == llvm::MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) || + (r_type == MachO::GENERIC_RELOC_SECTDIFF || + r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) || (cputype == MachO::CPU_TYPE_ARM && - (sectdiff_r_type == llvm::MachO::ARM_RELOC_SECTDIFF || - sectdiff_r_type == llvm::MachO::ARM_RELOC_LOCAL_SECTDIFF || - sectdiff_r_type == llvm::MachO::ARM_RELOC_HALF_SECTDIFF))) { - previous_sectdiff = true; - sectdiff_r_type = r_type; - } - else { + (sectdiff_r_type == MachO::ARM_RELOC_SECTDIFF || + sectdiff_r_type == MachO::ARM_RELOC_LOCAL_SECTDIFF || + sectdiff_r_type == MachO::ARM_RELOC_HALF_SECTDIFF))) { + previous_sectdiff = true; + sectdiff_r_type = r_type; + } else { previous_sectdiff = false; sectdiff_r_type = 0; } if (cputype == MachO::CPU_TYPE_ARM && - (r_type == llvm::MachO::ARM_RELOC_HALF || - r_type == llvm::MachO::ARM_RELOC_HALF_SECTDIFF)) + (r_type == MachO::ARM_RELOC_HALF || + r_type == MachO::ARM_RELOC_HALF_SECTDIFF)) previous_arm_half = true; else previous_arm_half = false; @@ -635,8 +932,7 @@ static void PrintRelocationEntries(const MachOObjectFile *O, else { if (verbose) { // plain: address - if (cputype == MachO::CPU_TYPE_ARM && - r_type == llvm::MachO::ARM_RELOC_PAIR) + if (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR) outs() << " "; else outs() << format("%08x ", (unsigned int)r_address); @@ -678,28 +974,27 @@ static void PrintRelocationEntries(const MachOObjectFile *O, outs() << "False "; // plain: symbolnum/value - if (cputype == MachO::CPU_TYPE_ARM && - r_type == llvm::MachO::ARM_RELOC_PAIR) + if (cputype == MachO::CPU_TYPE_ARM && r_type == MachO::ARM_RELOC_PAIR) outs() << format("other_half = 0x%04x\n", (unsigned int)r_address); - else if (cputype == MachO::CPU_TYPE_ARM64 && - r_type == llvm::MachO::ARM64_RELOC_ADDEND) + else if ((cputype == MachO::CPU_TYPE_ARM64 || + cputype == MachO::CPU_TYPE_ARM64_32) && + r_type == MachO::ARM64_RELOC_ADDEND) outs() << format("addend = 0x%06x\n", (unsigned int)r_symbolnum); else { outs() << format("%d ", r_symbolnum); - if (r_symbolnum == llvm::MachO::R_ABS) + if (r_symbolnum == MachO::R_ABS) outs() << "R_ABS\n"; else { // in this case, r_symbolnum is actually a 1-based section number uint32_t nsects = O->section_end()->getRawDataRefImpl().d.a; if (r_symbolnum > 0 && r_symbolnum <= nsects) { - llvm::object::DataRefImpl DRI; + object::DataRefImpl DRI; DRI.d.a = r_symbolnum-1; StringRef SegName = O->getSectionFinalSegmentName(DRI); - StringRef SectName; - if (O->getSectionName(DRI, SectName)) - outs() << "(?,?)\n"; + if (Expected<StringRef> NameOrErr = O->getSectionName(DRI)) + outs() << "(" << SegName << "," << *NameOrErr << ")\n"; else - outs() << "(" << SegName << "," << SectName << ")\n"; + outs() << "(?,?)\n"; } else { outs() << "(?,?)\n"; @@ -708,8 +1003,8 @@ static void PrintRelocationEntries(const MachOObjectFile *O, } } if (cputype == MachO::CPU_TYPE_ARM && - (r_type == llvm::MachO::ARM_RELOC_HALF || - r_type == llvm::MachO::ARM_RELOC_HALF_SECTDIFF)) + (r_type == MachO::ARM_RELOC_HALF || + r_type == MachO::ARM_RELOC_HALF_SECTDIFF)) previous_arm_half = true; else previous_arm_half = false; @@ -752,13 +1047,12 @@ static void PrintRelocations(const MachOObjectFile *O, const bool verbose) { DataRefImpl DRI; DRI.d.a = J; const StringRef SegName = O->getSectionFinalSegmentName(DRI); - StringRef SectName; - if (O->getSectionName(DRI, SectName)) + if (Expected<StringRef> NameOrErr = O->getSectionName(DRI)) + outs() << "Relocation information (" << SegName << "," << *NameOrErr + << format(") %u entries", Sec.nreloc); + else outs() << "Relocation information (" << SegName << ",?) " << format("%u entries", Sec.nreloc); - else - outs() << "Relocation information (" << SegName << "," - << SectName << format(") %u entries", Sec.nreloc); outs() << "\naddress pcrel length extern type scattered " "symbolnum/value\n"; PrintRelocationEntries(O, O->section_rel_begin(DRI), @@ -773,13 +1067,12 @@ static void PrintRelocations(const MachOObjectFile *O, const bool verbose) { DataRefImpl DRI; DRI.d.a = J; const StringRef SegName = O->getSectionFinalSegmentName(DRI); - StringRef SectName; - if (O->getSectionName(DRI, SectName)) + if (Expected<StringRef> NameOrErr = O->getSectionName(DRI)) + outs() << "Relocation information (" << SegName << "," << *NameOrErr + << format(") %u entries", Sec.nreloc); + else outs() << "Relocation information (" << SegName << ",?) " << format("%u entries", Sec.nreloc); - else - outs() << "Relocation information (" << SegName << "," - << SectName << format(") %u entries", Sec.nreloc); outs() << "\naddress pcrel length extern type scattered " "symbolnum/value\n"; PrintRelocationEntries(O, O->section_rel_begin(DRI), @@ -913,7 +1206,16 @@ static void PrintDylibs(MachOObjectFile *O, bool JustId) { outs() << " current version " << ((dl.dylib.current_version >> 16) & 0xffff) << "." << ((dl.dylib.current_version >> 8) & 0xff) << "." - << (dl.dylib.current_version & 0xff) << ")\n"; + << (dl.dylib.current_version & 0xff); + if (Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB) + outs() << ", weak"; + if (Load.C.cmd == MachO::LC_REEXPORT_DYLIB) + outs() << ", reexport"; + if (Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) + outs() << ", upward"; + if (Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB) + outs() << ", lazy"; + outs() << ")\n"; } } else { outs() << "\tBad offset (" << dl.dylib.name << ") for name of "; @@ -942,18 +1244,13 @@ typedef DenseMap<uint64_t, StringRef> SymbolAddressMap; static void CreateSymbolAddressMap(MachOObjectFile *O, SymbolAddressMap *AddrMap) { // Create a map of symbol addresses to symbol names. + const StringRef FileName = O->getFileName(); for (const SymbolRef &Symbol : O->symbols()) { - Expected<SymbolRef::Type> STOrErr = Symbol.getType(); - if (!STOrErr) - report_error(O->getFileName(), STOrErr.takeError()); - SymbolRef::Type ST = *STOrErr; + SymbolRef::Type ST = unwrapOrError(Symbol.getType(), FileName); if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data || ST == SymbolRef::ST_Other) { uint64_t Address = Symbol.getValue(); - Expected<StringRef> SymNameOrErr = Symbol.getName(); - if (!SymNameOrErr) - report_error(O->getFileName(), SymNameOrErr.takeError()); - StringRef SymName = *SymNameOrErr; + StringRef SymName = unwrapOrError(Symbol.getName(), FileName); if (!SymName.startswith(".objc")) (*AddrMap)[Address] = SymName; } @@ -1186,10 +1483,8 @@ static void DumpLiteralPointerSection(MachOObjectFile *O, }); if (Reloc != Relocs.end()) { symbol_iterator RelocSym = Reloc->second; - Expected<StringRef> SymName = RelocSym->getName(); - if (!SymName) - report_error(O->getFileName(), SymName.takeError()); - outs() << "external relocation entry for symbol:" << *SymName << "\n"; + StringRef SymName = unwrapOrError(RelocSym->getName(), O->getFileName()); + outs() << "external relocation entry for symbol:" << SymName << "\n"; continue; } @@ -1220,8 +1515,8 @@ static void DumpLiteralPointerSection(MachOObjectFile *O, section_type = Sec.flags & MachO::SECTION_TYPE; } - StringRef BytesStr; - Sect->getContents(BytesStr); + StringRef BytesStr = unwrapOrError(Sect->getContents(), O->getFileName()); + const char *Contents = reinterpret_cast<const char *>(BytesStr.data()); switch (section_type) { @@ -1333,10 +1628,7 @@ static void DumpInitTermPointerSection(MachOObjectFile *O, }); if (Reloc != Relocs.end()) { symbol_iterator RelocSym = Reloc->second; - Expected<StringRef> SymName = RelocSym->getName(); - if (!SymName) - report_error(O->getFileName(), SymName.takeError()); - outs() << " " << *SymName; + outs() << " " << unwrapOrError(RelocSym->getName(), O->getFileName()); } else { SymbolName = GuessSymbolName(p, AddrMap); if (SymbolName) @@ -1438,8 +1730,8 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, } uint32_t section_type = section_flags & MachO::SECTION_TYPE; - StringRef BytesStr; - Section.getContents(BytesStr); + StringRef BytesStr = + unwrapOrError(Section.getContents(), O->getFileName()); const char *sect = reinterpret_cast<const char *>(BytesStr.data()); uint32_t sect_size = BytesStr.size(); uint64_t sect_addr = Section.getAddress(); @@ -1523,8 +1815,8 @@ static void DumpInfoPlistSectionContents(StringRef Filename, if (SegName == "__TEXT" && SectName == "__info_plist") { if (!NoLeadingHeaders) outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; - StringRef BytesStr; - Section.getContents(BytesStr); + StringRef BytesStr = + unwrapOrError(Section.getContents(), O->getFileName()); const char *sect = reinterpret_cast<const char *>(BytesStr.data()); outs() << format("%.*s", BytesStr.size(), sect) << "\n"; return; @@ -1609,8 +1901,8 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, // the error message. if (Disassemble || IndirectSymbols || !FilterSections.empty() || UnwindInfo) if (Error Err = MachOOF->checkSymbolTable()) - report_error(ArchiveName, FileName, std::move(Err), ArchitectureName); - + report_error(std::move(Err), ArchiveName, FileName, ArchitectureName); + if (DisassembleAll) { for (const SectionRef &Section : MachOOF->sections()) { StringRef SectName; @@ -1774,6 +2066,21 @@ static void printCPUType(uint32_t cputype, uint32_t cpusubtype) { outs() << " cputype CPU_TYPE_ARM64\n"; outs() << " cpusubtype CPU_SUBTYPE_ARM64_ALL\n"; break; + case MachO::CPU_SUBTYPE_ARM64E: + outs() << " cputype CPU_TYPE_ARM64\n"; + outs() << " cpusubtype CPU_SUBTYPE_ARM64E\n"; + break; + default: + printUnknownCPUType(cputype, cpusubtype); + break; + } + break; + case MachO::CPU_TYPE_ARM64_32: + switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_ARM64_32_V8: + outs() << " cputype CPU_TYPE_ARM64_32\n"; + outs() << " cpusubtype CPU_SUBTYPE_ARM64_32_V8\n"; + break; default: printUnknownCPUType(cputype, cpusubtype); break; @@ -1862,10 +2169,8 @@ static void printArchiveChild(StringRef Filename, const Archive::Child &C, StringRef ArchitectureName = StringRef()) { if (print_offset) outs() << C.getChildOffset() << "\t"; - Expected<sys::fs::perms> ModeOrErr = C.getAccessMode(); - if (!ModeOrErr) - report_error(Filename, C, ModeOrErr.takeError(), ArchitectureName); - sys::fs::perms Mode = ModeOrErr.get(); + sys::fs::perms Mode = + unwrapOrError(C.getAccessMode(), Filename, C, ArchitectureName); if (verbose) { // FIXME: this first dash, "-", is for (Mode & S_IFMT) == S_IFREG. // But there is nothing in sys::fs::perms for S_IFMT or S_IFREG. @@ -1883,20 +2188,11 @@ static void printArchiveChild(StringRef Filename, const Archive::Child &C, outs() << format("0%o ", Mode); } - Expected<unsigned> UIDOrErr = C.getUID(); - if (!UIDOrErr) - report_error(Filename, C, UIDOrErr.takeError(), ArchitectureName); - unsigned UID = UIDOrErr.get(); - outs() << format("%3d/", UID); - Expected<unsigned> GIDOrErr = C.getGID(); - if (!GIDOrErr) - report_error(Filename, C, GIDOrErr.takeError(), ArchitectureName); - unsigned GID = GIDOrErr.get(); - outs() << format("%-3d ", GID); - Expected<uint64_t> Size = C.getRawSize(); - if (!Size) - report_error(Filename, C, Size.takeError(), ArchitectureName); - outs() << format("%5" PRId64, Size.get()) << " "; + outs() << format( + "%3d/%-3d %5" PRId64 " ", + unwrapOrError(C.getUID(), Filename, C, ArchitectureName), + unwrapOrError(C.getGID(), Filename, C, ArchitectureName), + unwrapOrError(C.getRawSize(), Filename, C, ArchitectureName)); StringRef RawLastModified = C.getRawLastModified(); if (verbose) { @@ -1919,21 +2215,15 @@ static void printArchiveChild(StringRef Filename, const Archive::Child &C, Expected<StringRef> NameOrErr = C.getName(); if (!NameOrErr) { consumeError(NameOrErr.takeError()); - Expected<StringRef> NameOrErr = C.getRawName(); - if (!NameOrErr) - report_error(Filename, C, NameOrErr.takeError(), ArchitectureName); - StringRef RawName = NameOrErr.get(); - outs() << RawName << "\n"; + outs() << unwrapOrError(C.getRawName(), Filename, C, ArchitectureName) + << "\n"; } else { StringRef Name = NameOrErr.get(); outs() << Name << "\n"; } } else { - Expected<StringRef> NameOrErr = C.getRawName(); - if (!NameOrErr) - report_error(Filename, C, NameOrErr.takeError(), ArchitectureName); - StringRef RawName = NameOrErr.get(); - outs() << RawName << "\n"; + outs() << unwrapOrError(C.getRawName(), Filename, C, ArchitectureName) + << "\n"; } } @@ -1941,12 +2231,11 @@ static void printArchiveHeaders(StringRef Filename, Archive *A, bool verbose, bool print_offset, StringRef ArchitectureName = StringRef()) { Error Err = Error::success(); - ; for (const auto &C : A->children(Err, false)) printArchiveChild(Filename, C, verbose, print_offset, ArchitectureName); if (Err) - report_error(StringRef(), Filename, std::move(Err), ArchitectureName); + report_error(std::move(Err), StringRef(), Filename, ArchitectureName); } static bool ValidateArchFlags() { @@ -1970,15 +2259,15 @@ static bool ValidateArchFlags() { // -arch flags selecting just those slices as specified by them and also parses // archive files. Then for each individual Mach-O file ProcessMachO() is // called to process the file based on the command line options. -void llvm::parseInputMachO(StringRef Filename) { +void parseInputMachO(StringRef Filename) { if (!ValidateArchFlags()) return; // Attempt to open the binary. Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Filename); if (!BinaryOrErr) { - if (auto E = isNotObjectErrorInvalidFileType(BinaryOrErr.takeError())) - report_error(Filename, std::move(E)); + if (Error E = isNotObjectErrorInvalidFileType(BinaryOrErr.takeError())) + report_error(std::move(E), Filename); else outs() << Filename << ": is not an object file\n"; return; @@ -1994,8 +2283,8 @@ void llvm::parseInputMachO(StringRef Filename) { for (auto &C : A->children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { - if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(Filename, C, std::move(E)); + if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + report_error(std::move(E), Filename, C); continue; } if (MachOObjectFile *O = dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) { @@ -2005,7 +2294,7 @@ void llvm::parseInputMachO(StringRef Filename) { } } if (Err) - report_error(Filename, std::move(Err)); + report_error(std::move(Err), Filename); return; } if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) { @@ -2026,7 +2315,7 @@ void llvm::parseInputMachO(StringRef Filename) { llvm_unreachable("Input object can't be invalid at this point"); } -void llvm::parseInputMachO(MachOUniversalBinary *UB) { +void parseInputMachO(MachOUniversalBinary *UB) { if (!ValidateArchFlags()) return; @@ -2055,13 +2344,12 @@ void llvm::parseInputMachO(MachOUniversalBinary *UB) { ObjectFile &O = *ObjOrErr.get(); if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O)) ProcessMachO(Filename, MachOOF, "", ArchitectureName); - } else if (auto E = isNotObjectErrorInvalidFileType( - ObjOrErr.takeError())) { - report_error(Filename, StringRef(), std::move(E), - ArchitectureName); + } else if (Error E = isNotObjectErrorInvalidFileType( + ObjOrErr.takeError())) { + report_error(std::move(E), Filename, StringRef(), ArchitectureName); continue; } else if (Expected<std::unique_ptr<Archive>> AOrErr = - I->getAsArchive()) { + I->getAsArchive()) { std::unique_ptr<Archive> &A = *AOrErr; outs() << "Archive : " << Filename; if (!ArchitectureName.empty()) @@ -2074,8 +2362,8 @@ void llvm::parseInputMachO(MachOUniversalBinary *UB) { for (auto &C : A->children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { - if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(Filename, C, std::move(E), ArchitectureName); + if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + report_error(std::move(E), Filename, C, ArchitectureName); continue; } if (MachOObjectFile *O = @@ -2083,7 +2371,7 @@ void llvm::parseInputMachO(MachOUniversalBinary *UB) { ProcessMachO(Filename, O, O->getFileName(), ArchitectureName); } if (Err) - report_error(Filename, std::move(Err)); + report_error(std::move(Err), Filename); } else { consumeError(AOrErr.takeError()); error("Mach-O universal file: " + Filename + " for " + @@ -2116,11 +2404,11 @@ void llvm::parseInputMachO(MachOUniversalBinary *UB) { ObjectFile &O = *ObjOrErr.get(); if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O)) ProcessMachO(Filename, MachOOF); - } else if (auto E = isNotObjectErrorInvalidFileType( - ObjOrErr.takeError())) { - report_error(Filename, std::move(E)); + } else if (Error E = + isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { + report_error(std::move(E), Filename); } else if (Expected<std::unique_ptr<Archive>> AOrErr = - I->getAsArchive()) { + I->getAsArchive()) { std::unique_ptr<Archive> &A = *AOrErr; outs() << "Archive : " << Filename << "\n"; if (ArchiveHeaders) @@ -2130,8 +2418,9 @@ void llvm::parseInputMachO(MachOUniversalBinary *UB) { for (auto &C : A->children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { - if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(Filename, C, std::move(E)); + if (Error E = + isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + report_error(std::move(E), Filename, C); continue; } if (MachOObjectFile *O = @@ -2139,7 +2428,7 @@ void llvm::parseInputMachO(MachOUniversalBinary *UB) { ProcessMachO(Filename, O, O->getFileName()); } if (Err) - report_error(Filename, std::move(Err)); + report_error(std::move(Err), Filename); } else { consumeError(AOrErr.takeError()); error("Mach-O universal file: " + Filename + " for architecture " + @@ -2164,11 +2453,10 @@ void llvm::parseInputMachO(MachOUniversalBinary *UB) { ObjectFile &Obj = *ObjOrErr.get(); if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&Obj)) ProcessMachO(Filename, MachOOF, "", ArchitectureName); - } else if (auto E = isNotObjectErrorInvalidFileType( - ObjOrErr.takeError())) { - report_error(StringRef(), Filename, std::move(E), ArchitectureName); - } else if (Expected<std::unique_ptr<Archive>> AOrErr = - I->getAsArchive()) { + } else if (Error E = + isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { + report_error(std::move(E), StringRef(), Filename, ArchitectureName); + } else if (Expected<std::unique_ptr<Archive>> AOrErr = I->getAsArchive()) { std::unique_ptr<Archive> &A = *AOrErr; outs() << "Archive : " << Filename; if (!ArchitectureName.empty()) @@ -2181,8 +2469,8 @@ void llvm::parseInputMachO(MachOUniversalBinary *UB) { for (auto &C : A->children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { - if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(Filename, C, std::move(E), ArchitectureName); + if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + report_error(std::move(E), Filename, C, ArchitectureName); continue; } if (MachOObjectFile *O = @@ -2193,7 +2481,7 @@ void llvm::parseInputMachO(MachOUniversalBinary *UB) { } } if (Err) - report_error(Filename, std::move(Err)); + report_error(std::move(Err), Filename); } else { consumeError(AOrErr.takeError()); error("Mach-O universal file: " + Filename + " for architecture " + @@ -2308,12 +2596,9 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, } } if (reloc_found && isExtern) { - Expected<StringRef> SymName = Symbol.getName(); - if (!SymName) - report_error(info->O->getFileName(), SymName.takeError()); - const char *name = SymName->data(); op_info->AddSymbol.Present = 1; - op_info->AddSymbol.Name = name; + op_info->AddSymbol.Name = + unwrapOrError(Symbol.getName(), info->O->getFileName()).data(); // For i386 extern relocation entries the value in the instruction is // the offset from the symbol, and value is already set in op_info->Value. return 1; @@ -2372,10 +2657,8 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, // is the offset from the external symbol. if (info->O->getAnyRelocationPCRel(RE)) op_info->Value -= Pc + Offset + Size; - Expected<StringRef> SymName = Symbol.getName(); - if (!SymName) - report_error(info->O->getFileName(), SymName.takeError()); - const char *name = SymName->data(); + const char *name = + unwrapOrError(Symbol.getName(), info->O->getFileName()).data(); op_info->AddSymbol.Present = 1; op_info->AddSymbol.Name = name; return 1; @@ -2412,10 +2695,8 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, // is the offset from the external symbol. if (info->O->getAnyRelocationPCRel(RE)) op_info->Value -= Pc + Offset + Size; - Expected<StringRef> SymName = Symbol.getName(); - if (!SymName) - report_error(info->O->getFileName(), SymName.takeError()); - const char *name = SymName->data(); + const char *name = + unwrapOrError(Symbol.getName(), info->O->getFileName()).data(); unsigned Type = info->O->getAnyRelocationType(RE); if (Type == MachO::X86_64_RELOC_SUBTRACTOR) { DataRefImpl RelNext = Rel; @@ -2429,10 +2710,7 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, op_info->SubtractSymbol.Name = name; symbol_iterator RelocSymNext = info->O->getSymbolByIndex(SymbolNum); Symbol = *RelocSymNext; - Expected<StringRef> SymNameNext = Symbol.getName(); - if (!SymNameNext) - report_error(info->O->getFileName(), SymNameNext.takeError()); - name = SymNameNext->data(); + name = unwrapOrError(Symbol.getName(), info->O->getFileName()).data(); } } // TODO: add the VariantKinds to op_info->VariantKind for relocation types @@ -2501,10 +2779,8 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, } if (isExtern) { - Expected<StringRef> SymName = Symbol.getName(); - if (!SymName) - report_error(info->O->getFileName(), SymName.takeError()); - const char *name = SymName->data(); + const char *name = + unwrapOrError(Symbol.getName(), info->O->getFileName()).data(); op_info->AddSymbol.Present = 1; op_info->AddSymbol.Name = name; switch (r_type) { @@ -2620,10 +2896,9 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, // NOTE: Scattered relocations don't exist on arm64. if (!info->O->getPlainRelocationExternal(RE)) return 0; - Expected<StringRef> SymName = Reloc->getSymbol()->getName(); - if (!SymName) - report_error(info->O->getFileName(), SymName.takeError()); - const char *name = SymName->data(); + const char *name = + unwrapOrError(Reloc->getSymbol()->getName(), info->O->getFileName()) + .data(); op_info->AddSymbol.Present = 1; op_info->AddSymbol.Name = name; @@ -2749,12 +3024,8 @@ static const char *GuessIndirectSymbol(uint64_t ReferenceValue, info->O->getIndirectSymbolTableEntry(Dysymtab, index); if (indirect_symbol < Symtab.nsyms) { symbol_iterator Sym = info->O->getSymbolByIndex(indirect_symbol); - SymbolRef Symbol = *Sym; - Expected<StringRef> SymName = Symbol.getName(); - if (!SymName) - report_error(info->O->getFileName(), SymName.takeError()); - const char *name = SymName->data(); - return name; + return unwrapOrError(Sym->getName(), info->O->getFileName()) + .data(); } } } @@ -2784,12 +3055,8 @@ static const char *GuessIndirectSymbol(uint64_t ReferenceValue, info->O->getIndirectSymbolTableEntry(Dysymtab, index); if (indirect_symbol < Symtab.nsyms) { symbol_iterator Sym = info->O->getSymbolByIndex(indirect_symbol); - SymbolRef Symbol = *Sym; - Expected<StringRef> SymName = Symbol.getName(); - if (!SymName) - report_error(info->O->getFileName(), SymName.takeError()); - const char *name = SymName->data(); - return name; + return unwrapOrError(Sym->getName(), info->O->getFileName()) + .data(); } } } @@ -2960,8 +3227,8 @@ static const char *get_pointer_64(uint64_t Address, uint32_t &offset, S = (*(info->Sections))[SectIdx]; offset = Address - SectAddress; left = SectSize - offset; - StringRef SectContents; - ((*(info->Sections))[SectIdx]).getContents(SectContents); + StringRef SectContents = unwrapOrError( + ((*(info->Sections))[SectIdx]).getContents(), info->O->getFileName()); return SectContents.data() + offset; } } @@ -3015,10 +3282,7 @@ static const char *get_symbol_64(uint32_t sect_offset, SectionRef S, const char *SymbolName = nullptr; if (reloc_found && isExtern) { n_value = Symbol.getValue(); - Expected<StringRef> NameOrError = Symbol.getName(); - if (!NameOrError) - report_error(info->O->getFileName(), NameOrError.takeError()); - StringRef Name = *NameOrError; + StringRef Name = unwrapOrError(Symbol.getName(), info->O->getFileName()); if (!Name.empty()) { SymbolName = Name.data(); return SymbolName; @@ -3767,8 +4031,7 @@ walk_pointer_list_64(const char *listname, const SectionRef S, StringRef SegName = O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; - StringRef BytesStr; - S.getContents(BytesStr); + StringRef BytesStr = unwrapOrError(S.getContents(), O->getFileName()); const char *Contents = reinterpret_cast<const char *>(BytesStr.data()); for (uint32_t i = 0; i < S.getSize(); i += sizeof(uint64_t)) { @@ -3818,8 +4081,7 @@ walk_pointer_list_32(const char *listname, const SectionRef S, StringRef SegName = O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; - StringRef BytesStr; - S.getContents(BytesStr); + StringRef BytesStr = unwrapOrError(S.getContents(), O->getFileName()); const char *Contents = reinterpret_cast<const char *>(BytesStr.data()); for (uint32_t i = 0; i < S.getSize(); i += sizeof(uint32_t)) { @@ -6970,32 +7232,78 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, raw_ostream &DebugOut = nulls(); #endif + // Try to find debug info and set up the DIContext for it. std::unique_ptr<DIContext> diContext; - ObjectFile *DbgObj = MachOOF; + std::unique_ptr<Binary> DSYMBinary; std::unique_ptr<MemoryBuffer> DSYMBuf; - // Try to find debug info and set up the DIContext for it. if (UseDbg) { + ObjectFile *DbgObj = MachOOF; + // A separate DSym file path was specified, parse it as a macho file, // get the sections and supply it to the section name parsing machinery. if (!DSYMFile.empty()) { ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = MemoryBuffer::getFileOrSTDIN(DSYMFile); if (std::error_code EC = BufOrErr.getError()) { - report_error(DSYMFile, errorCodeToError(EC)); + report_error(errorCodeToError(EC), DSYMFile); return; } - Expected<std::unique_ptr<MachOObjectFile>> DbgObjCheck = - ObjectFile::createMachOObjectFile(BufOrErr.get()->getMemBufferRef()); + // We need to keep the file alive, because we're replacing DbgObj with it. + DSYMBuf = std::move(BufOrErr.get()); - if (Error E = DbgObjCheck.takeError()) { - report_error(DSYMFile, std::move(E)); + Expected<std::unique_ptr<Binary>> BinaryOrErr = + createBinary(DSYMBuf.get()->getMemBufferRef()); + if (!BinaryOrErr) { + report_error(BinaryOrErr.takeError(), DSYMFile); return; } - DbgObj = DbgObjCheck.get().release(); - // We need to keep the file alive, because we're replacing DbgObj with it. - DSYMBuf = std::move(BufOrErr.get()); + // We need to keep the Binary elive with the buffer + DSYMBinary = std::move(BinaryOrErr.get()); + + if (ObjectFile *O = dyn_cast<ObjectFile>(DSYMBinary.get())) { + // this is a Mach-O object file, use it + if (MachOObjectFile *MachDSYM = dyn_cast<MachOObjectFile>(&*O)) { + DbgObj = MachDSYM; + } + else { + WithColor::error(errs(), "llvm-objdump") + << DSYMFile << " is not a Mach-O file type.\n"; + return; + } + } + else if (auto UB = dyn_cast<MachOUniversalBinary>(DSYMBinary.get())){ + // this is a Universal Binary, find a Mach-O for this architecture + uint32_t CPUType, CPUSubType; + const char *ArchFlag; + if (MachOOF->is64Bit()) { + const MachO::mach_header_64 H_64 = MachOOF->getHeader64(); + CPUType = H_64.cputype; + CPUSubType = H_64.cpusubtype; + } else { + const MachO::mach_header H = MachOOF->getHeader(); + CPUType = H.cputype; + CPUSubType = H.cpusubtype; + } + Triple T = MachOObjectFile::getArchTriple(CPUType, CPUSubType, nullptr, + &ArchFlag); + Expected<std::unique_ptr<MachOObjectFile>> MachDSYM = + UB->getObjectForArch(ArchFlag); + if (!MachDSYM) { + report_error(MachDSYM.takeError(), DSYMFile); + return; + } + + // We need to keep the Binary elive with the buffer + DbgObj = &*MachDSYM.get(); + DSYMBinary = std::move(*MachDSYM); + } + else { + WithColor::error(errs(), "llvm-objdump") + << DSYMFile << " is not a Mach-O or Universal file type.\n"; + return; + } } // Setup the DIContext @@ -7016,10 +7324,9 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, if (SegmentName != DisSegName) continue; - StringRef BytesStr; - Sections[SectIdx].getContents(BytesStr); - ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()), - BytesStr.size()); + StringRef BytesStr = + unwrapOrError(Sections[SectIdx].getContents(), Filename); + ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(BytesStr); uint64_t SectAddress = Sections[SectIdx].getAddress(); bool symbolTableWorked = false; @@ -7029,17 +7336,13 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, SymbolAddressMap AddrMap; bool DisSymNameFound = false; for (const SymbolRef &Symbol : MachOOF->symbols()) { - Expected<SymbolRef::Type> STOrErr = Symbol.getType(); - if (!STOrErr) - report_error(MachOOF->getFileName(), STOrErr.takeError()); - SymbolRef::Type ST = *STOrErr; + SymbolRef::Type ST = + unwrapOrError(Symbol.getType(), MachOOF->getFileName()); if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data || ST == SymbolRef::ST_Other) { uint64_t Address = Symbol.getValue(); - Expected<StringRef> SymNameOrErr = Symbol.getName(); - if (!SymNameOrErr) - report_error(MachOOF->getFileName(), SymNameOrErr.takeError()); - StringRef SymName = *SymNameOrErr; + StringRef SymName = + unwrapOrError(Symbol.getName(), MachOOF->getFileName()); AddrMap[Address] = SymName; if (!DisSymName.empty() && DisSymName == SymName) DisSymNameFound = true; @@ -7076,15 +7379,10 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, // Disassemble symbol by symbol. for (unsigned SymIdx = 0; SymIdx != Symbols.size(); SymIdx++) { - Expected<StringRef> SymNameOrErr = Symbols[SymIdx].getName(); - if (!SymNameOrErr) - report_error(MachOOF->getFileName(), SymNameOrErr.takeError()); - StringRef SymName = *SymNameOrErr; - - Expected<SymbolRef::Type> STOrErr = Symbols[SymIdx].getType(); - if (!STOrErr) - report_error(MachOOF->getFileName(), STOrErr.takeError()); - SymbolRef::Type ST = *STOrErr; + StringRef SymName = + unwrapOrError(Symbols[SymIdx].getName(), MachOOF->getFileName()); + SymbolRef::Type ST = + unwrapOrError(Symbols[SymIdx].getType(), MachOOF->getFileName()); if (ST != SymbolRef::ST_Function && ST != SymbolRef::ST_Data) continue; @@ -7137,10 +7435,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, uint64_t NextSym = 0; uint64_t NextSymIdx = SymIdx + 1; while (Symbols.size() > NextSymIdx) { - Expected<SymbolRef::Type> STOrErr = Symbols[NextSymIdx].getType(); - if (!STOrErr) - report_error(MachOOF->getFileName(), STOrErr.takeError()); - SymbolRef::Type NextSymType = *STOrErr; + SymbolRef::Type NextSymType = unwrapOrError( + Symbols[NextSymIdx].getType(), MachOOF->getFileName()); if (NextSymType == SymbolRef::ST_Function) { containsNextSym = Sections[SectIdx].containsSymbol(Symbols[NextSymIdx]); @@ -7243,7 +7539,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, // Print debug info. if (diContext) { - DILineInfo dli = diContext->getLineInfoForAddress(PC); + DILineInfo dli = diContext->getLineInfoForAddress({PC, SectIdx}); // Print valid line info if it changed. if (dli != lastLine && dli.Line != 0) outs() << "\t## " << dli.FileName << ':' << dli.Line << ':' @@ -7415,10 +7711,7 @@ static void findUnwindRelocNameAddend(const MachOObjectFile *Obj, const RelocationRef &Reloc, uint64_t Addr, StringRef &Name, uint64_t &Addend) { if (Reloc.getSymbol() != Obj->symbol_end()) { - Expected<StringRef> NameOrErr = Reloc.getSymbol()->getName(); - if (!NameOrErr) - report_error(Obj->getFileName(), NameOrErr.takeError()); - Name = *NameOrErr; + Name = unwrapOrError(Reloc.getSymbol()->getName(), Obj->getFileName()); Addend = Addr; return; } @@ -7440,16 +7733,11 @@ static void findUnwindRelocNameAddend(const MachOObjectFile *Obj, // Go back one so that SymbolAddress <= Addr. --Sym; - auto SectOrErr = Sym->second.getSection(); - if (!SectOrErr) - report_error(Obj->getFileName(), SectOrErr.takeError()); - section_iterator SymSection = *SectOrErr; + section_iterator SymSection = + unwrapOrError(Sym->second.getSection(), Obj->getFileName()); if (RelocSection == *SymSection) { // There's a valid symbol in the same section before this reference. - Expected<StringRef> NameOrErr = Sym->second.getName(); - if (!NameOrErr) - report_error(Obj->getFileName(), NameOrErr.takeError()); - Name = *NameOrErr; + Name = unwrapOrError(Sym->second.getName(), Obj->getFileName()); Addend = Addr - Sym->first; return; } @@ -7490,9 +7778,8 @@ printMachOCompactUnwindSection(const MachOObjectFile *Obj, uint32_t PointerSize = Is64 ? sizeof(uint64_t) : sizeof(uint32_t); uint32_t EntrySize = 3 * PointerSize + 2 * sizeof(uint32_t); - StringRef Contents; - CompactUnwind.getContents(Contents); - + StringRef Contents = + unwrapOrError(CompactUnwind.getContents(), Obj->getFileName()); SmallVector<CompactUnwindEntry, 4> CompactUnwinds; // First populate the initial raw offsets, encodings and so on from the entry. @@ -7633,8 +7920,8 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, outs() << "Contents of __unwind_info section:\n"; - StringRef Contents; - UnwindInfo.getContents(Contents); + StringRef Contents = + unwrapOrError(UnwindInfo.getContents(), Obj->getFileName()); ptrdiff_t Pos = 0; //===---------------------------------- @@ -7801,7 +8088,7 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, } } -void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) { +void printMachOUnwindInfo(const MachOObjectFile *Obj) { std::map<uint64_t, SymbolRef> Symbols; for (const SymbolRef &SymRef : Obj->symbols()) { // Discard any undefined or absolute symbols. They're not going to take part @@ -7917,6 +8204,20 @@ static void PrintMachHeader(uint32_t magic, uint32_t cputype, case MachO::CPU_SUBTYPE_ARM64_ALL: outs() << " ALL"; break; + case MachO::CPU_SUBTYPE_ARM64E: + outs() << " E"; + break; + default: + outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); + break; + } + break; + case MachO::CPU_TYPE_ARM64_32: + outs() << " ARM64_32"; + switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_ARM64_32_V8: + outs() << " V8"; + break; default: outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); break; @@ -9485,7 +9786,8 @@ static void PrintThreadCommand(MachO::thread_command t, const char *Ptr, begin += count * sizeof(uint32_t); } } - } else if (cputype == MachO::CPU_TYPE_ARM64) { + } else if (cputype == MachO::CPU_TYPE_ARM64 || + cputype == MachO::CPU_TYPE_ARM64_32) { while (begin < end) { if (end - begin > (ptrdiff_t)sizeof(uint32_t)) { memcpy((char *)&flavor, begin, sizeof(uint32_t)); @@ -9790,12 +10092,12 @@ static void PrintMachHeader(const MachOObjectFile *Obj, bool verbose) { } } -void llvm::printMachOFileHeader(const object::ObjectFile *Obj) { +void printMachOFileHeader(const object::ObjectFile *Obj) { const MachOObjectFile *file = dyn_cast<const MachOObjectFile>(Obj); PrintMachHeader(file, !NonVerbose); } -void llvm::printMachOLoadCommands(const object::ObjectFile *Obj) { +void printMachOLoadCommands(const object::ObjectFile *Obj) { const MachOObjectFile *file = dyn_cast<const MachOObjectFile>(Obj); uint32_t filetype = 0; uint32_t cputype = 0; @@ -9817,7 +10119,7 @@ void llvm::printMachOLoadCommands(const object::ObjectFile *Obj) { // export trie dumping //===----------------------------------------------------------------------===// -void llvm::printMachOExportsTrie(const object::MachOObjectFile *Obj) { +void printMachOExportsTrie(const object::MachOObjectFile *Obj) { uint64_t BaseSegmentAddress = 0; for (const auto &Command : Obj->load_commands()) { if (Command.C.cmd == MachO::LC_SEGMENT) { @@ -9835,7 +10137,7 @@ void llvm::printMachOExportsTrie(const object::MachOObjectFile *Obj) { } } Error Err = Error::success(); - for (const llvm::object::ExportEntry &Entry : Obj->exports(Err)) { + for (const object::ExportEntry &Entry : Obj->exports(Err)) { uint64_t Flags = Entry.flags(); bool ReExport = (Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT); bool WeakDef = (Flags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); @@ -9889,17 +10191,17 @@ void llvm::printMachOExportsTrie(const object::MachOObjectFile *Obj) { outs() << "\n"; } if (Err) - report_error(Obj->getFileName(), std::move(Err)); + report_error(std::move(Err), Obj->getFileName()); } //===----------------------------------------------------------------------===// // rebase table dumping //===----------------------------------------------------------------------===// -void llvm::printMachORebaseTable(object::MachOObjectFile *Obj) { +void printMachORebaseTable(object::MachOObjectFile *Obj) { outs() << "segment section address type\n"; Error Err = Error::success(); - for (const llvm::object::MachORebaseEntry &Entry : Obj->rebaseTable(Err)) { + for (const object::MachORebaseEntry &Entry : Obj->rebaseTable(Err)) { StringRef SegmentName = Entry.segmentName(); StringRef SectionName = Entry.sectionName(); uint64_t Address = Entry.address(); @@ -9910,7 +10212,7 @@ void llvm::printMachORebaseTable(object::MachOObjectFile *Obj) { Address, Entry.typeName().str().c_str()); } if (Err) - report_error(Obj->getFileName(), std::move(Err)); + report_error(std::move(Err), Obj->getFileName()); } static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { @@ -9938,12 +10240,12 @@ static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { // bind table dumping //===----------------------------------------------------------------------===// -void llvm::printMachOBindTable(object::MachOObjectFile *Obj) { +void printMachOBindTable(object::MachOObjectFile *Obj) { // Build table of sections so names can used in final output. outs() << "segment section address type " "addend dylib symbol\n"; Error Err = Error::success(); - for (const llvm::object::MachOBindEntry &Entry : Obj->bindTable(Err)) { + for (const object::MachOBindEntry &Entry : Obj->bindTable(Err)) { StringRef SegmentName = Entry.segmentName(); StringRef SectionName = Entry.sectionName(); uint64_t Address = Entry.address(); @@ -9962,18 +10264,18 @@ void llvm::printMachOBindTable(object::MachOObjectFile *Obj) { << Entry.symbolName() << Attr << "\n"; } if (Err) - report_error(Obj->getFileName(), std::move(Err)); + report_error(std::move(Err), Obj->getFileName()); } //===----------------------------------------------------------------------===// // lazy bind table dumping //===----------------------------------------------------------------------===// -void llvm::printMachOLazyBindTable(object::MachOObjectFile *Obj) { +void printMachOLazyBindTable(object::MachOObjectFile *Obj) { outs() << "segment section address " "dylib symbol\n"; Error Err = Error::success(); - for (const llvm::object::MachOBindEntry &Entry : Obj->lazyBindTable(Err)) { + for (const object::MachOBindEntry &Entry : Obj->lazyBindTable(Err)) { StringRef SegmentName = Entry.segmentName(); StringRef SectionName = Entry.sectionName(); uint64_t Address = Entry.address(); @@ -9987,18 +10289,18 @@ void llvm::printMachOLazyBindTable(object::MachOObjectFile *Obj) { << Entry.symbolName() << "\n"; } if (Err) - report_error(Obj->getFileName(), std::move(Err)); + report_error(std::move(Err), Obj->getFileName()); } //===----------------------------------------------------------------------===// // weak bind table dumping //===----------------------------------------------------------------------===// -void llvm::printMachOWeakBindTable(object::MachOObjectFile *Obj) { +void printMachOWeakBindTable(object::MachOObjectFile *Obj) { outs() << "segment section address " "type addend symbol\n"; Error Err = Error::success(); - for (const llvm::object::MachOBindEntry &Entry : Obj->weakBindTable(Err)) { + for (const object::MachOBindEntry &Entry : Obj->weakBindTable(Err)) { // Strong symbols don't have a location to update. if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) { outs() << " strong " @@ -10019,7 +10321,7 @@ void llvm::printMachOWeakBindTable(object::MachOObjectFile *Obj) { << "\n"; } if (Err) - report_error(Obj->getFileName(), std::move(Err)); + report_error(std::move(Err), Obj->getFileName()); } // get_dyld_bind_info_symbolname() is used for disassembly and passed an @@ -10031,16 +10333,66 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, if (info->bindtable == nullptr) { info->bindtable = llvm::make_unique<SymbolAddressMap>(); Error Err = Error::success(); - for (const llvm::object::MachOBindEntry &Entry : info->O->bindTable(Err)) { + for (const object::MachOBindEntry &Entry : info->O->bindTable(Err)) { uint64_t Address = Entry.address(); StringRef name = Entry.symbolName(); if (!name.empty()) (*info->bindtable)[Address] = name; } if (Err) - report_error(info->O->getFileName(), std::move(Err)); + report_error(std::move(Err), info->O->getFileName()); } auto name = info->bindtable->lookup(ReferenceValue); return !name.empty() ? name.data() : nullptr; } +void printLazyBindTable(ObjectFile *o) { + outs() << "Lazy bind table:\n"; + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachOLazyBindTable(MachO); + else + WithColor::error() + << "This operation is only currently supported " + "for Mach-O executable files.\n"; +} + +void printWeakBindTable(ObjectFile *o) { + outs() << "Weak bind table:\n"; + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachOWeakBindTable(MachO); + else + WithColor::error() + << "This operation is only currently supported " + "for Mach-O executable files.\n"; +} + +void printExportsTrie(const ObjectFile *o) { + outs() << "Exports trie:\n"; + if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachOExportsTrie(MachO); + else + WithColor::error() + << "This operation is only currently supported " + "for Mach-O executable files.\n"; +} + +void printRebaseTable(ObjectFile *o) { + outs() << "Rebase table:\n"; + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachORebaseTable(MachO); + else + WithColor::error() + << "This operation is only currently supported " + "for Mach-O executable files.\n"; +} + +void printBindTable(ObjectFile *o) { + outs() << "Bind table:\n"; + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachOBindTable(MachO); + else + WithColor::error() + << "This operation is only currently supported " + "for Mach-O executable files.\n"; +} +} // namespace llvm diff --git a/contrib/llvm/tools/llvm-objdump/WasmDump.cpp b/contrib/llvm/tools/llvm-objdump/WasmDump.cpp index 045002cd4b34..da27a4acbb5f 100644 --- a/contrib/llvm/tools/llvm-objdump/WasmDump.cpp +++ b/contrib/llvm/tools/llvm-objdump/WasmDump.cpp @@ -1,9 +1,8 @@ //===-- WasmDump.cpp - wasm-specific dumper ---------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -15,14 +14,39 @@ #include "llvm-objdump.h" #include "llvm/Object/Wasm.h" -using namespace llvm; -using namespace object; +using namespace llvm::object; -void llvm::printWasmFileHeader(const object::ObjectFile *Obj) { - const WasmObjectFile *File = dyn_cast<const WasmObjectFile>(Obj); +namespace llvm { +void printWasmFileHeader(const object::ObjectFile *Obj) { + const auto *File = dyn_cast<const WasmObjectFile>(Obj); outs() << "Program Header:\n"; outs() << "Version: 0x"; outs().write_hex(File->getHeader().Version); outs() << "\n"; } + +Error getWasmRelocationValueString(const WasmObjectFile *Obj, + const RelocationRef &RelRef, + SmallVectorImpl<char> &Result) { + const wasm::WasmRelocation &Rel = Obj->getWasmRelocation(RelRef); + symbol_iterator SI = RelRef.getSymbol(); + std::string FmtBuf; + raw_string_ostream Fmt(FmtBuf); + if (SI == Obj->symbol_end()) { + // Not all wasm relocations have symbols associated with them. + // In particular R_WASM_TYPE_INDEX_LEB. + Fmt << Rel.Index; + } else { + Expected<StringRef> SymNameOrErr = SI->getName(); + if (!SymNameOrErr) + return SymNameOrErr.takeError(); + StringRef SymName = *SymNameOrErr; + Result.append(SymName.begin(), SymName.end()); + } + Fmt << (Rel.Addend < 0 ? "" : "+") << Rel.Addend; + Fmt.flush(); + Result.append(FmtBuf.begin(), FmtBuf.end()); + return Error::success(); +} +} // namespace llvm diff --git a/contrib/llvm/tools/llvm-objdump/llvm-objdump.cpp b/contrib/llvm/tools/llvm-objdump/llvm-objdump.cpp index 9bd4528ef7f7..58981203c59e 100644 --- a/contrib/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/contrib/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -1,9 +1,8 @@ //===-- llvm-objdump.cpp - Object file dumping utility for llvm -----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -19,6 +18,7 @@ #include "llvm-objdump.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetOperations.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" @@ -68,283 +68,298 @@ #include <unordered_map> #include <utility> -using namespace llvm; -using namespace object; +using namespace llvm::object; + +namespace llvm { + +cl::OptionCategory ObjdumpCat("llvm-objdump Options"); + +// MachO specific +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; + +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)); -cl::opt<bool> - llvm::AllHeaders("all-headers", - cl::desc("Display all available header information")); +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::list<std::string> -InputFilenames(cl::Positional, cl::desc("<input object files>"),cl::ZeroOrMore); +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> 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> 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> 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> 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> -llvm::Disassemble("disassemble", - cl::desc("Display assembler mnemonics for the machine instructions")); +static cl::list<std::string> + DisassembleFunctions("disassemble-functions", cl::CommaSeparated, + cl::desc("List of functions 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 -Disassembled("d", cl::desc("Alias for --disassemble"), - cl::aliasopt(Disassemble)); + DisassembleZeroesShort("z", cl::desc("Alias for --disassemble-zeroes"), + cl::NotHidden, cl::Grouping, + cl::aliasopt(DisassembleZeroes)); -cl::opt<bool> -llvm::DisassembleAll("disassemble-all", - cl::desc("Display assembler mnemonics for the machine instructions")); +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 -DisassembleAlld("D", cl::desc("Alias for --disassemble-all"), - cl::aliasopt(DisassembleAll)); - -cl::opt<bool> llvm::Demangle("demangle", cl::desc("Demangle symbols names"), - cl::init(false)); - -static cl::alias DemangleShort("C", cl::desc("Alias for --demangle"), - cl::aliasopt(llvm::Demangle)); + DisassemblerOptionsShort("M", cl::desc("Alias for --disassembler-options"), + cl::NotHidden, cl::Grouping, cl::Prefix, + cl::CommaSeparated, + cl::aliasopt(DisassemblerOptions)); -static cl::list<std::string> -DisassembleFunctions("df", - cl::CommaSeparated, - cl::desc("List of functions to disassemble")); -static StringSet<> DisasmFuncsSet; +cl::opt<DIDumpType> 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)); -cl::opt<bool> -llvm::Relocations("reloc", - cl::desc("Display the relocation entries in the file")); -static cl::alias RelocationsShort("r", cl::desc("Alias for --reloc"), - cl::NotHidden, - cl::aliasopt(llvm::Relocations)); +static cl::opt<bool> + FaultMapSection("fault-map-section", + cl::desc("Display contents of faultmap section"), + cl::cat(ObjdumpCat)); -cl::opt<bool> -llvm::DynamicRelocations("dynamic-reloc", - cl::desc("Display the dynamic relocation entries in the file")); -static cl::alias -DynamicRelocationsd("R", cl::desc("Alias for --dynamic-reloc"), - cl::aliasopt(DynamicRelocations)); +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> - llvm::SectionContents("full-contents", - cl::desc("Display the content of each section")); +cl::opt<bool> 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)); -cl::opt<bool> llvm::SymbolTable("syms", cl::desc("Display the symbol table")); -static cl::alias SymbolTableShort("t", cl::desc("Alias for --syms"), - cl::NotHidden, - cl::aliasopt(llvm::SymbolTable)); - -cl::opt<bool> -llvm::ExportsTrie("exports-trie", cl::desc("Display mach-o exported symbols")); - -cl::opt<bool> -llvm::Rebase("rebase", cl::desc("Display mach-o rebasing info")); - -cl::opt<bool> -llvm::Bind("bind", cl::desc("Display mach-o binding info")); - -cl::opt<bool> -llvm::LazyBind("lazy-bind", cl::desc("Display mach-o lazy binding info")); - -cl::opt<bool> -llvm::WeakBind("weak-bind", cl::desc("Display mach-o weak binding info")); - -cl::opt<bool> -llvm::RawClangAST("raw-clang-ast", - cl::desc("Dump the raw binary contents of the clang AST section")); +static cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input object files>"), + cl::ZeroOrMore, + cl::cat(ObjdumpCat)); static cl::opt<bool> -MachOOpt("macho", cl::desc("Use MachO specific object file parser")); -static cl::alias -MachOm("m", cl::desc("Alias for --macho"), cl::aliasopt(MachOOpt)); - -cl::opt<std::string> -llvm::TripleName("triple", cl::desc("Target triple to disassemble for, " - "see -version for available targets")); + 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> -llvm::MCPU("mcpu", - cl::desc("Target a specific cpu type (-mcpu=help for details)"), - cl::value_desc("cpu-name"), - cl::init("")); - -cl::opt<std::string> -llvm::ArchName("arch-name", cl::desc("Target arch to disassemble for, " - "see -version for available targets")); - -cl::opt<bool> -llvm::SectionHeaders("section-headers", cl::desc("Display summaries of the " - "headers for each section.")); -static cl::alias -SectionHeadersShort("headers", cl::desc("Alias for --section-headers"), - cl::aliasopt(SectionHeaders)); -static cl::alias -SectionHeadersShorter("h", cl::desc("Alias for --section-headers"), - cl::aliasopt(SectionHeaders)); - -cl::list<std::string> -llvm::FilterSections("section", cl::desc("Operate on the specified sections only. " - "With -macho dump segment,section")); -cl::alias -static FilterSectionsj("j", cl::desc("Alias for --section"), - cl::aliasopt(llvm::FilterSections)); - -cl::list<std::string> -llvm::MAttrs("mattr", - cl::CommaSeparated, - cl::desc("Target specific attributes"), - cl::value_desc("a1,+a2,-a3,...")); - -cl::opt<bool> -llvm::NoShowRawInsn("no-show-raw-insn", cl::desc("When disassembling " - "instructions, do not print " - "the instruction bytes.")); -cl::opt<bool> -llvm::NoLeadingAddr("no-leading-addr", cl::desc("Print no leading address")); - -cl::opt<bool> -llvm::UnwindInfo("unwind-info", cl::desc("Display unwind information")); - -static cl::alias -UnwindInfoShort("u", cl::desc("Alias for --unwind-info"), - cl::aliasopt(UnwindInfo)); + 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> MAttrs("mattr", cl::CommaSeparated, + cl::desc("Target specific attributes"), + cl::value_desc("a1,+a2,-a3,..."), + cl::cat(ObjdumpCat)); + +cl::opt<bool> NoShowRawInsn("no-show-raw-insn", + cl::desc("When disassembling " + "instructions, do not print " + "the instruction bytes."), + cl::cat(ObjdumpCat)); +cl::opt<bool> 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> -llvm::PrivateHeaders("private-headers", - cl::desc("Display format specific file headers")); - -cl::opt<bool> -llvm::FirstPrivateHeader("private-header", - cl::desc("Display only the first format specific file " - "header")); - -static cl::alias -PrivateHeadersShort("p", cl::desc("Alias for --private-headers"), - cl::aliasopt(PrivateHeaders)); - -cl::opt<bool> llvm::FileHeaders( - "file-headers", - cl::desc("Display the contents of the overall file header")); - -static cl::alias FileHeadersShort("f", cl::desc("Alias for --file-headers"), - cl::aliasopt(FileHeaders)); + 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> - llvm::ArchiveHeaders("archive-headers", - cl::desc("Display archive header information")); +cl::opt<bool> PrintImmHex("print-imm-hex", + cl::desc("Use hex format for immediate values"), + cl::cat(ObjdumpCat)); -cl::alias -ArchiveHeadersShort("a", cl::desc("Alias for --archive-headers"), - cl::aliasopt(ArchiveHeaders)); +cl::opt<bool> 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::opt<bool> - llvm::PrintImmHex("print-imm-hex", - cl::desc("Use hex format for immediate values")); - -cl::opt<bool> PrintFaultMaps("fault-map-section", - cl::desc("Display contents of faultmap section")); +cl::list<std::string> + 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> 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)); -cl::opt<DIDumpType> llvm::DwarfDumpType( - "dwarf", cl::init(DIDT_Null), cl::desc("Dump of dwarf debug sections:"), - cl::values(clEnumValN(DIDT_DebugFrame, "frames", ".debug_frame"))); +static cl::opt<bool> + ShowLMA("show-lma", + cl::desc("Display LMA column when dumping ELF section headers"), + cl::cat(ObjdumpCat)); -cl::opt<bool> PrintSource( +static cl::opt<bool> PrintSource( "source", cl::desc( - "Display source inlined with disassembly. Implies disassemble object")); - -cl::alias PrintSourceShort("S", cl::desc("Alias for -source"), - cl::aliasopt(PrintSource)); - -cl::opt<bool> PrintLines("line-numbers", - cl::desc("Display source line numbers with " - "disassembly. Implies disassemble object")); - -cl::alias PrintLinesShort("l", cl::desc("Alias for -line-numbers"), - cl::aliasopt(PrintLines)); + "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)); -cl::opt<unsigned long long> +static cl::opt<uint64_t> StartAddress("start-address", cl::desc("Disassemble beginning at address"), - cl::value_desc("address"), cl::init(0)); -cl::opt<unsigned long long> - StopAddress("stop-address", - cl::desc("Stop disassembly at address"), - cl::value_desc("address"), cl::init(UINT64_MAX)); - -cl::opt<bool> DisassembleZeroes( - "disassemble-zeroes", - cl::desc("Do not skip blocks of zeroes when disassembling")); -cl::alias DisassembleZeroesShort("z", - cl::desc("Alias for --disassemble-zeroes"), - cl::aliasopt(DisassembleZeroes)); + 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> 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 StringRef ToolName; +cl::opt<std::string> TripleName("triple", + cl::desc("Target triple to disassemble for, " + "see -version for available targets"), + cl::cat(ObjdumpCat)); -typedef std::vector<std::tuple<uint64_t, StringRef, uint8_t>> SectionSymbolsTy; +cl::opt<bool> 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)); -namespace { -typedef std::function<bool(llvm::object::SectionRef const &)> FilterPredicate; +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)); -class SectionFilterIterator { -public: - SectionFilterIterator(FilterPredicate P, - llvm::object::section_iterator const &I, - llvm::object::section_iterator const &E) - : Predicate(std::move(P)), Iterator(I), End(E) { - ScanPredicate(); - } - const llvm::object::SectionRef &operator*() const { return *Iterator; } - SectionFilterIterator &operator++() { - ++Iterator; - ScanPredicate(); - return *this; - } - bool operator!=(SectionFilterIterator const &Other) const { - return Iterator != Other.Iterator; - } +static cl::extrahelp + HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); -private: - void ScanPredicate() { - while (Iterator != End && !Predicate(*Iterator)) { - ++Iterator; - } - } - FilterPredicate Predicate; - llvm::object::section_iterator Iterator; - llvm::object::section_iterator End; -}; +static StringSet<> DisasmFuncsSet; +static StringSet<> FoundSectionSet; +static StringRef ToolName; -class SectionFilter { -public: - SectionFilter(FilterPredicate P, llvm::object::ObjectFile const &O) - : Predicate(std::move(P)), Object(O) {} - SectionFilterIterator begin() { - return SectionFilterIterator(Predicate, Object.section_begin(), - Object.section_end()); - } - SectionFilterIterator end() { - return SectionFilterIterator(Predicate, Object.section_end(), - Object.section_end()); - } +typedef std::vector<std::tuple<uint64_t, StringRef, uint8_t>> SectionSymbolsTy; -private: - FilterPredicate Predicate; - llvm::object::ObjectFile const &Object; -}; -SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O) { - return SectionFilter( - [](llvm::object::SectionRef const &S) { - if (FilterSections.empty()) - return true; - llvm::StringRef String; - std::error_code error = S.getName(String); - if (error) - return false; - return is_contained(FilterSections, String); - }, - O); +static bool shouldKeep(object::SectionRef S) { + if (FilterSections.empty()) + return true; + StringRef SecName; + std::error_code error = S.getName(SecName); + if (error) + return false; + // StringSet does not allow empty key so avoid adding sections with + // no name (such as the section with index 0) here. + if (!SecName.empty()) + FoundSectionSet.insert(SecName); + return is_contained(FilterSections, SecName); } + +SectionFilter ToolSectionFilter(object::ObjectFile const &O) { + return SectionFilter([](object::SectionRef S) { return shouldKeep(S); }, O); } -void llvm::error(std::error_code EC) { +void error(std::error_code EC) { if (!EC) return; WithColor::error(errs(), ToolName) @@ -353,34 +368,39 @@ void llvm::error(std::error_code EC) { exit(1); } -LLVM_ATTRIBUTE_NORETURN void llvm::error(Twine Message) { +void error(Error E) { + if (!E) + return; + WithColor::error(errs(), ToolName) << toString(std::move(E)); + exit(1); +} + +LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { WithColor::error(errs(), ToolName) << Message << ".\n"; errs().flush(); exit(1); } -void llvm::warn(StringRef Message) { +void warn(StringRef Message) { WithColor::warning(errs(), ToolName) << Message << ".\n"; errs().flush(); } -LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File, - Twine Message) { - WithColor::error(errs(), ToolName) - << "'" << File << "': " << Message << ".\n"; - exit(1); +static void warn(Twine Message) { + // Output order between errs() and outs() matters especially for archive + // files where the output is per member object. + outs().flush(); + WithColor::warning(errs(), ToolName) << Message << "\n"; + errs().flush(); } -LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File, - std::error_code EC) { - assert(EC); +LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, Twine Message) { WithColor::error(errs(), ToolName) - << "'" << File << "': " << EC.message() << ".\n"; + << "'" << File << "': " << Message << ".\n"; exit(1); } -LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File, - llvm::Error E) { +LLVM_ATTRIBUTE_NORETURN void report_error(Error E, StringRef File) { assert(E); std::string Buf; raw_string_ostream OS(Buf); @@ -390,10 +410,9 @@ LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File, exit(1); } -LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef ArchiveName, - StringRef FileName, - llvm::Error E, - StringRef ArchitectureName) { +LLVM_ATTRIBUTE_NORETURN void report_error(Error E, StringRef ArchiveName, + StringRef FileName, + StringRef ArchitectureName) { assert(E); WithColor::error(errs(), ToolName); if (ArchiveName != "") @@ -410,25 +429,39 @@ LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef ArchiveName, exit(1); } -LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef ArchiveName, - const object::Archive::Child &C, - llvm::Error E, - StringRef ArchitectureName) { +LLVM_ATTRIBUTE_NORETURN void report_error(Error E, StringRef ArchiveName, + const object::Archive::Child &C, + StringRef ArchitectureName) { Expected<StringRef> NameOrErr = C.getName(); // TODO: if we have a error getting the name then it would be nice to print // the index of which archive member this is and or its offset in the // archive instead of "???" as the name. if (!NameOrErr) { consumeError(NameOrErr.takeError()); - llvm::report_error(ArchiveName, "???", std::move(E), ArchitectureName); + report_error(std::move(E), ArchiveName, "???", ArchitectureName); } else - llvm::report_error(ArchiveName, NameOrErr.get(), std::move(E), - ArchitectureName); + report_error(std::move(E), ArchiveName, NameOrErr.get(), ArchitectureName); +} + +static void warnOnNoMatchForSections() { + SetVector<StringRef> MissingSections; + for (StringRef S : FilterSections) { + if (FoundSectionSet.count(S)) + return; + // User may specify a unnamed section. Don't warn for it. + if (!S.empty()) + MissingSections.insert(S); + } + + // Warn only if no section in FilterSections is matched. + for (StringRef S : MissingSections) + warn("section '" + S + "' mentioned in a -j/--section option, but not " + "found in any input file"); } static const Target *getTarget(const ObjectFile *Obj = nullptr) { // Figure out the target triple. - llvm::Triple TheTriple("unknown-unknown-unknown"); + Triple TheTriple("unknown-unknown-unknown"); if (TripleName.empty()) { if (Obj) TheTriple = Obj->makeTriple(); @@ -459,423 +492,21 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) { return TheTarget; } -bool llvm::isRelocAddressLess(RelocationRef A, RelocationRef B) { +bool isRelocAddressLess(RelocationRef A, RelocationRef B) { return A.getOffset() < B.getOffset(); } -static std::string demangle(StringRef Name) { - char *Demangled = nullptr; - if (Name.startswith("_Z")) - Demangled = itaniumDemangle(Name.data(), Demangled, nullptr, nullptr); - else if (Name.startswith("?")) - Demangled = microsoftDemangle(Name.data(), Demangled, nullptr, nullptr); - - if (!Demangled) - return Name; - - std::string Ret = Demangled; - free(Demangled); - return Ret; -} - -template <class ELFT> -static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj, - const RelocationRef &RelRef, - SmallVectorImpl<char> &Result) { - typedef typename ELFObjectFile<ELFT>::Elf_Sym Elf_Sym; - typedef typename ELFObjectFile<ELFT>::Elf_Shdr Elf_Shdr; - typedef typename ELFObjectFile<ELFT>::Elf_Rela Elf_Rela; - - const ELFFile<ELFT> &EF = *Obj->getELFFile(); - DataRefImpl Rel = RelRef.getRawDataRefImpl(); - auto SecOrErr = EF.getSection(Rel.d.a); - if (!SecOrErr) - return errorToErrorCode(SecOrErr.takeError()); - const Elf_Shdr *Sec = *SecOrErr; - auto SymTabOrErr = EF.getSection(Sec->sh_link); - if (!SymTabOrErr) - return errorToErrorCode(SymTabOrErr.takeError()); - const Elf_Shdr *SymTab = *SymTabOrErr; - assert(SymTab->sh_type == ELF::SHT_SYMTAB || - SymTab->sh_type == ELF::SHT_DYNSYM); - auto StrTabSec = EF.getSection(SymTab->sh_link); - if (!StrTabSec) - return errorToErrorCode(StrTabSec.takeError()); - auto StrTabOrErr = EF.getStringTable(*StrTabSec); - if (!StrTabOrErr) - return errorToErrorCode(StrTabOrErr.takeError()); - StringRef StrTab = *StrTabOrErr; - int64_t Addend = 0; - // If there is no Symbol associated with the relocation, we set the undef - // boolean value to 'true'. This will prevent us from calling functions that - // requires the relocation to be associated with a symbol. - bool Undef = false; - switch (Sec->sh_type) { - default: - return object_error::parse_failed; - case ELF::SHT_REL: { - // TODO: Read implicit addend from section data. - break; - } - case ELF::SHT_RELA: { - const Elf_Rela *ERela = Obj->getRela(Rel); - Addend = ERela->r_addend; - Undef = ERela->getSymbol(false) == 0; - break; - } - } - std::string Target; - if (!Undef) { - symbol_iterator SI = RelRef.getSymbol(); - const Elf_Sym *symb = Obj->getSymbol(SI->getRawDataRefImpl()); - if (symb->getType() == ELF::STT_SECTION) { - Expected<section_iterator> SymSI = SI->getSection(); - if (!SymSI) - return errorToErrorCode(SymSI.takeError()); - const Elf_Shdr *SymSec = Obj->getSection((*SymSI)->getRawDataRefImpl()); - auto SecName = EF.getSectionName(SymSec); - if (!SecName) - return errorToErrorCode(SecName.takeError()); - Target = *SecName; - } else { - Expected<StringRef> SymName = symb->getName(StrTab); - if (!SymName) - return errorToErrorCode(SymName.takeError()); - if (Demangle) - Target = demangle(*SymName); - else - Target = *SymName; - } - } else - Target = "*ABS*"; - - // Default scheme is to print Target, as well as "+ <addend>" for nonzero - // addend. Should be acceptable for all normal purposes. - std::string FmtBuf; - raw_string_ostream Fmt(FmtBuf); - Fmt << Target; - if (Addend != 0) - Fmt << (Addend < 0 ? "" : "+") << Addend; - Fmt.flush(); - Result.append(FmtBuf.begin(), FmtBuf.end()); - return std::error_code(); -} - -static std::error_code getRelocationValueString(const ELFObjectFileBase *Obj, - const RelocationRef &Rel, - SmallVectorImpl<char> &Result) { - if (auto *ELF32LE = dyn_cast<ELF32LEObjectFile>(Obj)) - return getRelocationValueString(ELF32LE, Rel, Result); - if (auto *ELF64LE = dyn_cast<ELF64LEObjectFile>(Obj)) - return getRelocationValueString(ELF64LE, Rel, Result); - if (auto *ELF32BE = dyn_cast<ELF32BEObjectFile>(Obj)) - return getRelocationValueString(ELF32BE, Rel, Result); - auto *ELF64BE = cast<ELF64BEObjectFile>(Obj); - return getRelocationValueString(ELF64BE, Rel, Result); -} - -static std::error_code getRelocationValueString(const COFFObjectFile *Obj, - const RelocationRef &Rel, - SmallVectorImpl<char> &Result) { - symbol_iterator SymI = Rel.getSymbol(); - Expected<StringRef> SymNameOrErr = SymI->getName(); - if (!SymNameOrErr) - return errorToErrorCode(SymNameOrErr.takeError()); - StringRef SymName = *SymNameOrErr; - Result.append(SymName.begin(), SymName.end()); - return std::error_code(); -} - -static void printRelocationTargetName(const MachOObjectFile *O, - const MachO::any_relocation_info &RE, - raw_string_ostream &Fmt) { - // Target of a scattered relocation is an address. In the interest of - // generating pretty output, scan through the symbol table looking for a - // symbol that aligns with that address. If we find one, print it. - // Otherwise, we just print the hex address of the target. - if (O->isRelocationScattered(RE)) { - uint32_t Val = O->getPlainRelocationSymbolNum(RE); - - for (const SymbolRef &Symbol : O->symbols()) { - Expected<uint64_t> Addr = Symbol.getAddress(); - if (!Addr) - report_error(O->getFileName(), Addr.takeError()); - if (*Addr != Val) - continue; - Expected<StringRef> Name = Symbol.getName(); - if (!Name) - report_error(O->getFileName(), Name.takeError()); - Fmt << *Name; - return; - } - - // If we couldn't find a symbol that this relocation refers to, try - // to find a section beginning instead. - for (const SectionRef &Section : ToolSectionFilter(*O)) { - std::error_code ec; - - StringRef Name; - uint64_t Addr = Section.getAddress(); - if (Addr != Val) - continue; - if ((ec = Section.getName(Name))) - report_error(O->getFileName(), ec); - Fmt << Name; - return; - } - - Fmt << format("0x%x", Val); - return; - } - - StringRef S; - bool isExtern = O->getPlainRelocationExternal(RE); - uint64_t Val = O->getPlainRelocationSymbolNum(RE); - - if (O->getAnyRelocationType(RE) == MachO::ARM64_RELOC_ADDEND) { - Fmt << format("0x%0" PRIx64, Val); - return; - } - - if (isExtern) { - symbol_iterator SI = O->symbol_begin(); - advance(SI, Val); - Expected<StringRef> SOrErr = SI->getName(); - if (!SOrErr) - report_error(O->getFileName(), SOrErr.takeError()); - S = *SOrErr; - } else { - section_iterator SI = O->section_begin(); - // Adjust for the fact that sections are 1-indexed. - if (Val == 0) { - Fmt << "0 (?,?)"; - return; - } - uint32_t I = Val - 1; - while (I != 0 && SI != O->section_end()) { - --I; - advance(SI, 1); - } - if (SI == O->section_end()) - Fmt << Val << " (?,?)"; - else - SI->getName(S); - } - - Fmt << S; -} - -static std::error_code getRelocationValueString(const WasmObjectFile *Obj, - const RelocationRef &RelRef, - SmallVectorImpl<char> &Result) { - const wasm::WasmRelocation& Rel = Obj->getWasmRelocation(RelRef); - symbol_iterator SI = RelRef.getSymbol(); - std::string FmtBuf; - raw_string_ostream Fmt(FmtBuf); - if (SI == Obj->symbol_end()) { - // Not all wasm relocations have symbols associated with them. - // In particular R_WEBASSEMBLY_TYPE_INDEX_LEB. - Fmt << Rel.Index; - } else { - Expected<StringRef> SymNameOrErr = SI->getName(); - if (!SymNameOrErr) - return errorToErrorCode(SymNameOrErr.takeError()); - StringRef SymName = *SymNameOrErr; - Result.append(SymName.begin(), SymName.end()); - } - Fmt << (Rel.Addend < 0 ? "" : "+") << Rel.Addend; - Fmt.flush(); - Result.append(FmtBuf.begin(), FmtBuf.end()); - return std::error_code(); -} - -static std::error_code getRelocationValueString(const MachOObjectFile *Obj, - const RelocationRef &RelRef, - SmallVectorImpl<char> &Result) { - DataRefImpl Rel = RelRef.getRawDataRefImpl(); - MachO::any_relocation_info RE = Obj->getRelocation(Rel); - - unsigned Arch = Obj->getArch(); - - std::string FmtBuf; - raw_string_ostream Fmt(FmtBuf); - unsigned Type = Obj->getAnyRelocationType(RE); - bool IsPCRel = Obj->getAnyRelocationPCRel(RE); - - // Determine any addends that should be displayed with the relocation. - // These require decoding the relocation type, which is triple-specific. - - // X86_64 has entirely custom relocation types. - if (Arch == Triple::x86_64) { - switch (Type) { - case MachO::X86_64_RELOC_GOT_LOAD: - case MachO::X86_64_RELOC_GOT: { - printRelocationTargetName(Obj, RE, Fmt); - Fmt << "@GOT"; - if (IsPCRel) - Fmt << "PCREL"; - break; - } - case MachO::X86_64_RELOC_SUBTRACTOR: { - DataRefImpl RelNext = Rel; - Obj->moveRelocationNext(RelNext); - MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); - - // X86_64_RELOC_SUBTRACTOR must be followed by a relocation of type - // X86_64_RELOC_UNSIGNED. - // NOTE: Scattered relocations don't exist on x86_64. - unsigned RType = Obj->getAnyRelocationType(RENext); - if (RType != MachO::X86_64_RELOC_UNSIGNED) - report_error(Obj->getFileName(), "Expected X86_64_RELOC_UNSIGNED after " - "X86_64_RELOC_SUBTRACTOR."); - - // The X86_64_RELOC_UNSIGNED contains the minuend symbol; - // X86_64_RELOC_SUBTRACTOR contains the subtrahend. - printRelocationTargetName(Obj, RENext, Fmt); - Fmt << "-"; - printRelocationTargetName(Obj, RE, Fmt); - break; - } - case MachO::X86_64_RELOC_TLV: - printRelocationTargetName(Obj, RE, Fmt); - Fmt << "@TLV"; - if (IsPCRel) - Fmt << "P"; - break; - case MachO::X86_64_RELOC_SIGNED_1: - printRelocationTargetName(Obj, RE, Fmt); - Fmt << "-1"; - break; - case MachO::X86_64_RELOC_SIGNED_2: - printRelocationTargetName(Obj, RE, Fmt); - Fmt << "-2"; - break; - case MachO::X86_64_RELOC_SIGNED_4: - printRelocationTargetName(Obj, RE, Fmt); - Fmt << "-4"; - break; - default: - printRelocationTargetName(Obj, RE, Fmt); - break; - } - // X86 and ARM share some relocation types in common. - } else if (Arch == Triple::x86 || Arch == Triple::arm || - Arch == Triple::ppc) { - // Generic relocation types... - switch (Type) { - case MachO::GENERIC_RELOC_PAIR: // prints no info - return std::error_code(); - case MachO::GENERIC_RELOC_SECTDIFF: { - DataRefImpl RelNext = Rel; - Obj->moveRelocationNext(RelNext); - MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); - - // X86 sect diff's must be followed by a relocation of type - // GENERIC_RELOC_PAIR. - unsigned RType = Obj->getAnyRelocationType(RENext); - - if (RType != MachO::GENERIC_RELOC_PAIR) - report_error(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " - "GENERIC_RELOC_SECTDIFF."); - - printRelocationTargetName(Obj, RE, Fmt); - Fmt << "-"; - printRelocationTargetName(Obj, RENext, Fmt); - break; - } - } - - if (Arch == Triple::x86 || Arch == Triple::ppc) { - switch (Type) { - case MachO::GENERIC_RELOC_LOCAL_SECTDIFF: { - DataRefImpl RelNext = Rel; - Obj->moveRelocationNext(RelNext); - MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); - - // X86 sect diff's must be followed by a relocation of type - // GENERIC_RELOC_PAIR. - unsigned RType = Obj->getAnyRelocationType(RENext); - if (RType != MachO::GENERIC_RELOC_PAIR) - report_error(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " - "GENERIC_RELOC_LOCAL_SECTDIFF."); - - printRelocationTargetName(Obj, RE, Fmt); - Fmt << "-"; - printRelocationTargetName(Obj, RENext, Fmt); - break; - } - case MachO::GENERIC_RELOC_TLV: { - printRelocationTargetName(Obj, RE, Fmt); - Fmt << "@TLV"; - if (IsPCRel) - Fmt << "P"; - break; - } - default: - printRelocationTargetName(Obj, RE, Fmt); - } - } else { // ARM-specific relocations - switch (Type) { - case MachO::ARM_RELOC_HALF: - case MachO::ARM_RELOC_HALF_SECTDIFF: { - // Half relocations steal a bit from the length field to encode - // whether this is an upper16 or a lower16 relocation. - bool isUpper = (Obj->getAnyRelocationLength(RE) & 0x1) == 1; - - if (isUpper) - Fmt << ":upper16:("; - else - Fmt << ":lower16:("; - printRelocationTargetName(Obj, RE, Fmt); - - DataRefImpl RelNext = Rel; - Obj->moveRelocationNext(RelNext); - MachO::any_relocation_info RENext = Obj->getRelocation(RelNext); - - // ARM half relocs must be followed by a relocation of type - // ARM_RELOC_PAIR. - unsigned RType = Obj->getAnyRelocationType(RENext); - if (RType != MachO::ARM_RELOC_PAIR) - report_error(Obj->getFileName(), "Expected ARM_RELOC_PAIR after " - "ARM_RELOC_HALF"); - - // NOTE: The half of the target virtual address is stashed in the - // address field of the secondary relocation, but we can't reverse - // engineer the constant offset from it without decoding the movw/movt - // instruction to find the other half in its immediate field. - - // ARM_RELOC_HALF_SECTDIFF encodes the second section in the - // symbol/section pointer of the follow-on relocation. - if (Type == MachO::ARM_RELOC_HALF_SECTDIFF) { - Fmt << "-"; - printRelocationTargetName(Obj, RENext, Fmt); - } - - Fmt << ")"; - break; - } - default: { printRelocationTargetName(Obj, RE, Fmt); } - } - } - } else - printRelocationTargetName(Obj, RE, Fmt); - - Fmt.flush(); - Result.append(FmtBuf.begin(), FmtBuf.end()); - return std::error_code(); -} - -static std::error_code getRelocationValueString(const RelocationRef &Rel, - SmallVectorImpl<char> &Result) { +static Error getRelocationValueString(const RelocationRef &Rel, + SmallVectorImpl<char> &Result) { const ObjectFile *Obj = Rel.getObject(); if (auto *ELF = dyn_cast<ELFObjectFileBase>(Obj)) - return getRelocationValueString(ELF, Rel, Result); + return getELFRelocationValueString(ELF, Rel, Result); if (auto *COFF = dyn_cast<COFFObjectFile>(Obj)) - return getRelocationValueString(COFF, Rel, Result); + return getCOFFRelocationValueString(COFF, Rel, Result); if (auto *Wasm = dyn_cast<WasmObjectFile>(Obj)) - return getRelocationValueString(Wasm, Rel, Result); + return getWasmRelocationValueString(Wasm, Rel, Result); if (auto *MachO = dyn_cast<MachOObjectFile>(Obj)) - return getRelocationValueString(MachO, Rel, Result); + return getMachORelocationValueString(MachO, Rel, Result); llvm_unreachable("unknown object file format"); } @@ -928,13 +559,15 @@ private: public: SourcePrinter() = default; SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) : Obj(Obj) { - symbolize::LLVMSymbolizer::Options SymbolizerOpts( - DILineInfoSpecifier::FunctionNameKind::None, true, false, false, - DefaultArch); + symbolize::LLVMSymbolizer::Options SymbolizerOpts; + SymbolizerOpts.PrintFunctions = DILineInfoSpecifier::FunctionNameKind::None; + SymbolizerOpts.Demangle = false; + SymbolizerOpts.DefaultArch = DefaultArch; Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts)); } virtual ~SourcePrinter() = default; - virtual void printSourceLine(raw_ostream &OS, uint64_t Address, + virtual void printSourceLine(raw_ostream &OS, + object::SectionedAddress Address, StringRef Delimiter = "; "); }; @@ -949,35 +582,37 @@ bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { Buffer = std::move(*BufferOrError); } // Chomp the file to get lines - size_t BufferSize = Buffer->getBufferSize(); - const char *BufferStart = Buffer->getBufferStart(); - for (const char *Start = BufferStart, *End = BufferStart; - End < BufferStart + BufferSize; End++) - if (*End == '\n' || End == BufferStart + BufferSize - 1 || - (*End == '\r' && *(End + 1) == '\n')) { - LineCache[LineInfo.FileName].push_back(StringRef(Start, End - Start)); - if (*End == '\r') - End++; - Start = End + 1; + 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(raw_ostream &OS, uint64_t Address, +void SourcePrinter::printSourceLine(raw_ostream &OS, + object::SectionedAddress Address, StringRef Delimiter) { if (!Symbolizer) return; + DILineInfo LineInfo = DILineInfo(); - auto ExpectecLineInfo = - Symbolizer->symbolizeCode(Obj->getFileName(), Address); - if (!ExpectecLineInfo) - consumeError(ExpectecLineInfo.takeError()); + auto ExpectedLineInfo = Symbolizer->symbolizeCode(*Obj, Address); + if (!ExpectedLineInfo) + consumeError(ExpectedLineInfo.takeError()); else - LineInfo = *ExpectecLineInfo; + LineInfo = *ExpectedLineInfo; - if ((LineInfo.FileName == "<invalid>") || OldLineInfo.Line == LineInfo.Line || - LineInfo.Line == 0) + if ((LineInfo.FileName == "<invalid>") || LineInfo.Line == 0 || + ((OldLineInfo.Line == LineInfo.Line) && + (OldLineInfo.FileName == LineInfo.FileName))) return; if (PrintLines) @@ -986,53 +621,79 @@ void SourcePrinter::printSourceLine(raw_ostream &OS, uint64_t Address, if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) if (!cacheSource(LineInfo)) return; - auto FileBuffer = SourceCache.find(LineInfo.FileName); - if (FileBuffer != SourceCache.end()) { - auto LineBuffer = LineCache.find(LineInfo.FileName); - if (LineBuffer != LineCache.end()) { - if (LineInfo.Line > LineBuffer->second.size()) - return; - // Vector begins at 0, line numbers are non-zero - OS << Delimiter << LineBuffer->second[LineInfo.Line - 1].ltrim() - << "\n"; - } + auto LineBuffer = LineCache.find(LineInfo.FileName); + if (LineBuffer != LineCache.end()) { + if (LineInfo.Line > LineBuffer->second.size()) + return; + // Vector begins at 0, line numbers are non-zero + OS << Delimiter << LineBuffer->second[LineInfo.Line - 1] << '\n'; } } OldLineInfo = LineInfo; } +static bool isAArch64Elf(const ObjectFile *Obj) { + const auto *Elf = dyn_cast<ELFObjectFileBase>(Obj); + return Elf && Elf->getEMachine() == ELF::EM_AARCH64; +} + static bool isArmElf(const ObjectFile *Obj) { - return (Obj->isELF() && - (Obj->getArch() == Triple::aarch64 || - Obj->getArch() == Triple::aarch64_be || - Obj->getArch() == Triple::arm || Obj->getArch() == Triple::armeb || - Obj->getArch() == Triple::thumb || - Obj->getArch() == Triple::thumbeb)); + const auto *Elf = dyn_cast<ELFObjectFileBase>(Obj); + return Elf && Elf->getEMachine() == ELF::EM_ARM; +} + +static bool hasMappingSymbols(const ObjectFile *Obj) { + return isArmElf(Obj) || isAArch64Elf(Obj); +} + +static void printRelocation(const RelocationRef &Rel, uint64_t Address, + bool Is64Bits) { + StringRef Fmt = Is64Bits ? "\t\t%016" PRIx64 ": " : "\t\t\t%08" PRIx64 ": "; + SmallString<16> Name; + SmallString<32> Val; + Rel.getTypeName(Name); + error(getRelocationValueString(Rel, Val)); + outs() << format(Fmt.data(), Address) << Name << "\t" << Val << "\n"; } class PrettyPrinter { public: virtual ~PrettyPrinter() = default; virtual void printInst(MCInstPrinter &IP, const MCInst *MI, - ArrayRef<uint8_t> Bytes, uint64_t Address, - raw_ostream &OS, StringRef Annot, - MCSubtargetInfo const &STI, SourcePrinter *SP, + ArrayRef<uint8_t> Bytes, + object::SectionedAddress Address, raw_ostream &OS, + StringRef Annot, MCSubtargetInfo const &STI, + SourcePrinter *SP, std::vector<RelocationRef> *Rels = nullptr) { if (SP && (PrintSource || PrintLines)) SP->printSourceLine(OS, Address); - if (!NoLeadingAddr) - OS << format("%8" PRIx64 ":", Address); - if (!NoShowRawInsn) { - OS << "\t"; - dumpBytes(Bytes, OS); + + { + formatted_raw_ostream FOS(OS); + if (!NoLeadingAddr) + FOS << format("%8" PRIx64 ":", Address.Address); + if (!NoShowRawInsn) { + FOS << ' '; + dumpBytes(Bytes, FOS); + } + FOS.flush(); + // The output of printInst starts with a tab. Print some spaces so that + // the tab has 1 column and advances to the target tab stop. + unsigned TabStop = NoShowRawInsn ? 16 : 40; + unsigned Column = FOS.getColumn(); + FOS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8); + + // The dtor calls flush() to ensure the indent comes before printInst(). } + if (MI) IP.printInst(MI, OS, "", STI); else - OS << " <unknown>"; + OS << "\t<unknown>"; } }; PrettyPrinter PrettyPrinterInst; + class HexagonPrettyPrinter : public PrettyPrinter { public: void printLead(ArrayRef<uint8_t> Bytes, uint64_t Address, @@ -1044,17 +705,17 @@ public: if (!NoShowRawInsn) { OS << "\t"; dumpBytes(Bytes.slice(0, 4), OS); - OS << format("%08" PRIx32, opcode); + OS << format("\t%08" PRIx32, opcode); } } void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes, - uint64_t Address, raw_ostream &OS, StringRef Annot, - MCSubtargetInfo const &STI, SourcePrinter *SP, + object::SectionedAddress Address, raw_ostream &OS, + StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, std::vector<RelocationRef> *Rels) override { if (SP && (PrintSource || PrintLines)) SP->printSourceLine(OS, Address, ""); if (!MI) { - printLead(Bytes, Address, OS); + printLead(Bytes, Address.Address, OS); OS << " <unknown>"; return; } @@ -1070,21 +731,15 @@ public: auto HeadTail = PacketBundle.first.split('\n'); auto Preamble = " { "; auto Separator = ""; - StringRef Fmt = "\t\t\t%08" PRIx64 ": "; - std::vector<RelocationRef>::const_iterator RelCur = Rels->begin(); - std::vector<RelocationRef>::const_iterator RelEnd = Rels->end(); // Hexagon's packets require relocations to be inline rather than // clustered at the end of the packet. + std::vector<RelocationRef>::const_iterator RelCur = Rels->begin(); + std::vector<RelocationRef>::const_iterator RelEnd = Rels->end(); auto PrintReloc = [&]() -> void { - while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address)) { - if (RelCur->getOffset() == Address) { - SmallString<16> Name; - SmallString<32> Val; - RelCur->getTypeName(Name); - error(getRelocationValueString(*RelCur, Val)); - OS << Separator << format(Fmt.data(), Address) << Name << "\t" << Val - << "\n"; + while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address.Address)) { + if (RelCur->getOffset() == Address.Address) { + printRelocation(*RelCur, Address.Address, false); return; } ++RelCur; @@ -1096,7 +751,7 @@ public: Separator = "\n"; if (SP && (PrintSource || PrintLines)) SP->printSourceLine(OS, Address, ""); - printLead(Bytes, Address, OS); + printLead(Bytes, Address.Address, OS); OS << Preamble; Preamble = " "; StringRef Inst; @@ -1114,7 +769,7 @@ public: OS << " } " << PacketBundle.second; PrintReloc(); Bytes = Bytes.slice(4); - Address += 4; + Address.Address += 4; } } }; @@ -1123,14 +778,12 @@ HexagonPrettyPrinter HexagonPrettyPrinterInst; class AMDGCNPrettyPrinter : public PrettyPrinter { public: void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes, - uint64_t Address, raw_ostream &OS, StringRef Annot, - MCSubtargetInfo const &STI, SourcePrinter *SP, + object::SectionedAddress Address, raw_ostream &OS, + StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, std::vector<RelocationRef> *Rels) override { if (SP && (PrintSource || PrintLines)) SP->printSourceLine(OS, Address); - typedef support::ulittle32_t U32; - if (MI) { SmallString<40> InstStr; raw_svector_ostream IS(InstStr); @@ -1144,7 +797,7 @@ public: // remaining if (Bytes.size() >= 4) { OS << format("\t.long 0x%08" PRIx32 " ", - static_cast<uint32_t>(*reinterpret_cast<const U32*>(Bytes.data()))); + support::endian::read32<support::little>(Bytes.data())); OS.indent(42); } else { OS << format("\t.byte 0x%02" PRIx8, Bytes[0]); @@ -1154,20 +807,21 @@ public: } } - OS << format("// %012" PRIX64 ": ", Address); - if (Bytes.size() >=4) { - for (auto D : makeArrayRef(reinterpret_cast<const U32*>(Bytes.data()), - Bytes.size() / sizeof(U32))) - // D should be explicitly casted to uint32_t here as it is passed - // by format to snprintf as vararg. - OS << format("%08" PRIX32 " ", static_cast<uint32_t>(D)); + OS << format("// %012" PRIX64 ":", Address.Address); + if (Bytes.size() >= 4) { + // D should be casted to uint32_t here as it is passed by format to + // snprintf as vararg. + for (uint32_t D : makeArrayRef( + reinterpret_cast<const support::little32_t *>(Bytes.data()), + Bytes.size() / 4)) + OS << format(" %08" PRIX32, D); } else { - for (unsigned int i = 0; i < Bytes.size(); i++) - OS << format("%02" PRIX8 " ", Bytes[i]); + for (unsigned char B : Bytes) + OS << format(" %02" PRIX8, B); } if (!Annot.empty()) - OS << "// " << Annot; + OS << " // " << Annot; } }; AMDGCNPrettyPrinter AMDGCNPrettyPrinterInst; @@ -1175,13 +829,13 @@ AMDGCNPrettyPrinter AMDGCNPrettyPrinterInst; class BPFPrettyPrinter : public PrettyPrinter { public: void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes, - uint64_t Address, raw_ostream &OS, StringRef Annot, - MCSubtargetInfo const &STI, SourcePrinter *SP, + object::SectionedAddress Address, raw_ostream &OS, + StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, std::vector<RelocationRef> *Rels) override { if (SP && (PrintSource || PrintLines)) SP->printSourceLine(OS, Address); if (!NoLeadingAddr) - OS << format("%8" PRId64 ":", Address / 8); + OS << format("%8" PRId64 ":", Address.Address / 8); if (!NoShowRawInsn) { OS << "\t"; dumpBytes(Bytes, OS); @@ -1189,7 +843,7 @@ public: if (MI) IP.printInst(MI, OS, "", STI); else - OS << " <unknown>"; + OS << "\t<unknown>"; } }; BPFPrettyPrinter BPFPrettyPrinterInst; @@ -1227,27 +881,25 @@ addDynamicElfSymbols(const ELFObjectFile<ELFT> *Obj, std::map<SectionRef, SectionSymbolsTy> &AllSymbols) { for (auto Symbol : Obj->getDynamicSymbolIterators()) { uint8_t SymbolType = Symbol.getELFType(); - if (SymbolType != ELF::STT_FUNC || Symbol.getSize() == 0) + if (SymbolType == ELF::STT_SECTION) continue; - Expected<uint64_t> AddressOrErr = Symbol.getAddress(); - if (!AddressOrErr) - report_error(Obj->getFileName(), AddressOrErr.takeError()); + uint64_t Address = unwrapOrError(Symbol.getAddress(), Obj->getFileName()); + // ELFSymbolRef::getAddress() returns size instead of value for common + // symbols which is not desirable for disassembly output. Overriding. + if (SymbolType == ELF::STT_COMMON) + Address = Obj->getSymbol(Symbol.getRawDataRefImpl())->st_value; - Expected<StringRef> Name = Symbol.getName(); - if (!Name) - report_error(Obj->getFileName(), Name.takeError()); - if (Name->empty()) + StringRef Name = unwrapOrError(Symbol.getName(), Obj->getFileName()); + if (Name.empty()) continue; - Expected<section_iterator> SectionOrErr = Symbol.getSection(); - if (!SectionOrErr) - report_error(Obj->getFileName(), SectionOrErr.takeError()); - section_iterator SecI = *SectionOrErr; + section_iterator SecI = + unwrapOrError(Symbol.getSection(), Obj->getFileName()); if (SecI == Obj->section_end()) continue; - AllSymbols[*SecI].emplace_back(*AddressOrErr, *Name, SymbolType); + AllSymbols[*SecI].emplace_back(Address, Name, SymbolType); } } @@ -1285,14 +937,10 @@ static void addPltEntries(const ObjectFile *Obj, SymbolRef Symbol(PltEntry.first, ElfObj); uint8_t SymbolType = getElfSymbolType(Obj, Symbol); - Expected<StringRef> NameOrErr = Symbol.getName(); - if (!NameOrErr) - report_error(Obj->getFileName(), NameOrErr.takeError()); - if (NameOrErr->empty()) - continue; - StringRef Name = Saver.save((*NameOrErr + "@plt").str()); - - AllSymbols[*Plt].emplace_back(PltEntry.second, Name, SymbolType); + StringRef Name = unwrapOrError(Symbol.getName(), Obj->getFileName()); + if (!Name.empty()) + AllSymbols[*Plt].emplace_back( + PltEntry.second, Saver.save((Name + "@plt").str()), SymbolType); } } } @@ -1301,10 +949,6 @@ static void addPltEntries(const ObjectFile *Obj, // returns the number of zero bytes that can be skipped when dumping the // disassembly of the instructions in Buf. static size_t countSkippableZeroBytes(ArrayRef<uint8_t> Buf) { - // When -z or --disassemble-zeroes are given we always dissasemble them. - if (DisassembleZeroes) - return 0; - // Find the number of leading zeroes. size_t N = 0; while (N < Buf.size() && !Buf[N]) @@ -1320,108 +964,160 @@ static size_t countSkippableZeroBytes(ArrayRef<uint8_t> Buf) { return N & ~0x3; } -static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { - if (StartAddress > StopAddress) - error("Start address should be less than stop address"); - - const Target *TheTarget = getTarget(Obj); - - // Package up features to be passed to target/subtarget - SubtargetFeatures Features = Obj->getFeatures(); - if (!MAttrs.empty()) - for (unsigned I = 0; I != MAttrs.size(); ++I) - Features.AddFeature(MAttrs[I]); - - std::unique_ptr<const MCRegisterInfo> MRI( - TheTarget->createMCRegInfo(TripleName)); - if (!MRI) - report_error(Obj->getFileName(), "no register info for target " + - TripleName); - - // Set up disassembler. - std::unique_ptr<const MCAsmInfo> AsmInfo( - TheTarget->createMCAsmInfo(*MRI, TripleName)); - if (!AsmInfo) - report_error(Obj->getFileName(), "no assembly info for target " + - TripleName); - std::unique_ptr<const MCSubtargetInfo> STI( - TheTarget->createMCSubtargetInfo(TripleName, MCPU, Features.getString())); - if (!STI) - report_error(Obj->getFileName(), "no subtarget info for target " + - TripleName); - std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo()); - if (!MII) - report_error(Obj->getFileName(), "no instruction info for target " + - TripleName); - MCObjectFileInfo MOFI; - MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI); - // FIXME: for now initialize MCObjectFileInfo with default values - MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx); - - std::unique_ptr<MCDisassembler> DisAsm( - TheTarget->createMCDisassembler(*STI, Ctx)); - if (!DisAsm) - report_error(Obj->getFileName(), "no disassembler for target " + - TripleName); +// Returns a map from sections to their relocations. +static std::map<SectionRef, std::vector<RelocationRef>> +getRelocsMap(object::ObjectFile const &Obj) { + std::map<SectionRef, std::vector<RelocationRef>> Ret; + for (SectionRef Sec : Obj.sections()) { + section_iterator Relocated = Sec.getRelocatedSection(); + if (Relocated == Obj.section_end() || !shouldKeep(*Relocated)) + continue; + std::vector<RelocationRef> &V = Ret[*Relocated]; + for (const RelocationRef &R : Sec.relocations()) + V.push_back(R); + // Sort relocations by address. + llvm::stable_sort(V, isRelocAddressLess); + } + return Ret; +} - std::unique_ptr<const MCInstrAnalysis> MIA( - TheTarget->createMCInstrAnalysis(MII.get())); +// Used for --adjust-vma to check if address should be adjusted by the +// specified value for a given section. +// For ELF we do not adjust non-allocatable sections like debug ones, +// because they are not loadable. +// TODO: implement for other file formats. +static bool shouldAdjustVA(const SectionRef &Section) { + const ObjectFile *Obj = Section.getObject(); + if (isa<object::ELFObjectFileBase>(Obj)) + return ELFSectionRef(Section).getFlags() & ELF::SHF_ALLOC; + return false; +} - int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); - std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( - Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI)); - if (!IP) - report_error(Obj->getFileName(), "no instruction printer for target " + - TripleName); - IP->setPrintImmHex(PrintImmHex); - PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName)); - StringRef Fmt = Obj->getBytesInAddress() > 4 ? "\t\t%016" PRIx64 ": " : - "\t\t\t%08" PRIx64 ": "; +typedef std::pair<uint64_t, char> MappingSymbolPair; +static char getMappingSymbolKind(ArrayRef<MappingSymbolPair> MappingSymbols, + uint64_t Address) { + auto It = + partition_point(MappingSymbols, [Address](const MappingSymbolPair &Val) { + return Val.first <= Address; + }); + // Return zero for any address before the first mapping symbol; this means + // we should use the default disassembly mode, depending on the target. + if (It == MappingSymbols.begin()) + return '\x00'; + return (It - 1)->second; +} - SourcePrinter SP(Obj, TheTarget->getName()); +static uint64_t +dumpARMELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End, + const ObjectFile *Obj, ArrayRef<uint8_t> Bytes, + ArrayRef<MappingSymbolPair> MappingSymbols) { + support::endianness Endian = + Obj->isLittleEndian() ? support::little : support::big; + while (Index < End) { + outs() << format("%8" PRIx64 ":", SectionAddr + Index); + outs() << "\t"; + if (Index + 4 <= End) { + dumpBytes(Bytes.slice(Index, 4), outs()); + outs() << "\t.word\t" + << format_hex( + support::endian::read32(Bytes.data() + Index, Endian), 10); + Index += 4; + } else if (Index + 2 <= End) { + dumpBytes(Bytes.slice(Index, 2), outs()); + outs() << "\t\t.short\t" + << format_hex( + support::endian::read16(Bytes.data() + Index, Endian), 6); + Index += 2; + } else { + dumpBytes(Bytes.slice(Index, 1), outs()); + outs() << "\t\t.byte\t" << format_hex(Bytes[0], 4); + ++Index; + } + outs() << "\n"; + if (getMappingSymbolKind(MappingSymbols, Index) != 'd') + break; + } + return Index; +} - // Create a mapping, RelocSecs = SectionRelocMap[S], where sections - // in RelocSecs contain the relocations for section S. - std::error_code EC; - std::map<SectionRef, SmallVector<SectionRef, 1>> SectionRelocMap; - for (const SectionRef &Section : ToolSectionFilter(*Obj)) { - section_iterator Sec2 = Section.getRelocatedSection(); - if (Sec2 != Obj->section_end()) - SectionRelocMap[*Sec2].push_back(Section); +static void dumpELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End, + ArrayRef<uint8_t> Bytes) { + // print out data up to 8 bytes at a time in hex and ascii + uint8_t AsciiData[9] = {'\0'}; + uint8_t Byte; + int NumBytes = 0; + + for (; Index < End; ++Index) { + if (NumBytes == 0) + outs() << format("%8" PRIx64 ":", SectionAddr + Index); + Byte = Bytes.slice(Index)[0]; + outs() << format(" %02x", Byte); + AsciiData[NumBytes] = isPrint(Byte) ? Byte : '.'; + + uint8_t IndentOffset = 0; + NumBytes++; + if (Index == End - 1 || NumBytes > 8) { + // Indent the space for less than 8 bytes data. + // 2 spaces for byte and one for space between bytes + IndentOffset = 3 * (8 - NumBytes); + for (int Excess = NumBytes; Excess < 8; Excess++) + AsciiData[Excess] = '\0'; + NumBytes = 8; + } + if (NumBytes == 8) { + AsciiData[8] = '\0'; + outs() << std::string(IndentOffset, ' ') << " "; + outs() << reinterpret_cast<char *>(AsciiData); + outs() << '\n'; + NumBytes = 0; + } } +} + +static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, + MCContext &Ctx, MCDisassembler *PrimaryDisAsm, + MCDisassembler *SecondaryDisAsm, + const MCInstrAnalysis *MIA, MCInstPrinter *IP, + const MCSubtargetInfo *PrimarySTI, + const MCSubtargetInfo *SecondarySTI, + PrettyPrinter &PIP, + SourcePrinter &SP, bool InlineRelocs) { + const MCSubtargetInfo *STI = PrimarySTI; + MCDisassembler *DisAsm = PrimaryDisAsm; + bool PrimaryIsThumb = false; + if (isArmElf(Obj)) + PrimaryIsThumb = STI->checkFeatures("+thumb-mode"); + + std::map<SectionRef, std::vector<RelocationRef>> RelocMap; + if (InlineRelocs) + RelocMap = getRelocsMap(*Obj); + bool Is64Bits = Obj->getBytesInAddress() > 4; // Create a mapping from virtual address to symbol name. This is used to // pretty print the symbols while disassembling. std::map<SectionRef, SectionSymbolsTy> AllSymbols; SectionSymbolsTy AbsoluteSymbols; + const StringRef FileName = Obj->getFileName(); for (const SymbolRef &Symbol : Obj->symbols()) { - Expected<uint64_t> AddressOrErr = Symbol.getAddress(); - if (!AddressOrErr) - report_error(Obj->getFileName(), AddressOrErr.takeError()); - uint64_t Address = *AddressOrErr; - - Expected<StringRef> Name = Symbol.getName(); - if (!Name) - report_error(Obj->getFileName(), Name.takeError()); - if (Name->empty()) - continue; + uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName); - Expected<section_iterator> SectionOrErr = Symbol.getSection(); - if (!SectionOrErr) - report_error(Obj->getFileName(), SectionOrErr.takeError()); + StringRef Name = unwrapOrError(Symbol.getName(), FileName); + if (Name.empty()) + continue; uint8_t SymbolType = ELF::STT_NOTYPE; - if (Obj->isELF()) + if (Obj->isELF()) { SymbolType = getElfSymbolType(Obj, Symbol); + if (SymbolType == ELF::STT_SECTION) + continue; + } - section_iterator SecI = *SectionOrErr; + section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName); if (SecI != Obj->section_end()) - AllSymbols[*SecI].emplace_back(Address, *Name, SymbolType); + AllSymbols[*SecI].emplace_back(Address, Name, SymbolType); else - AbsoluteSymbols.emplace_back(Address, *Name, SymbolType); - - + AbsoluteSymbols.emplace_back(Address, Name, SymbolType); } if (AllSymbols.empty() && Obj->isELF()) addDynamicElfSymbols(Obj, AllSymbols); @@ -1448,31 +1144,28 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { error(ExportEntry.getExportRVA(RVA)); uint64_t VA = COFFObj->getImageBase() + RVA; - auto Sec = std::upper_bound( - SectionAddresses.begin(), SectionAddresses.end(), VA, - [](uint64_t LHS, const std::pair<uint64_t, SectionRef> &RHS) { - return LHS < RHS.first; + auto Sec = partition_point( + SectionAddresses, [VA](const std::pair<uint64_t, SectionRef> &O) { + return O.first <= VA; }); - if (Sec != SectionAddresses.begin()) + if (Sec != SectionAddresses.begin()) { --Sec; - else - Sec = SectionAddresses.end(); - - if (Sec != SectionAddresses.end()) AllSymbols[Sec->second].emplace_back(VA, Name, ELF::STT_NOTYPE); - else + } else AbsoluteSymbols.emplace_back(VA, Name, ELF::STT_NOTYPE); } } // Sort all the symbols, this allows us to use a simple binary search to find // a symbol near an address. + StringSet<> FoundDisasmFuncsSet; for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols) array_pod_sort(SecSyms.second.begin(), SecSyms.second.end()); array_pod_sort(AbsoluteSymbols.begin(), AbsoluteSymbols.end()); for (const SectionRef &Section : ToolSectionFilter(*Obj)) { - if (!DisassembleAll && (!Section.isText() || Section.isVirtual())) + if (FilterSections.empty() && !DisassembleAll && + (!Section.isText() || Section.isVirtual())) continue; uint64_t SectionAddr = Section.getAddress(); @@ -1482,25 +1175,23 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // Get the list of all the symbols in this section. SectionSymbolsTy &Symbols = AllSymbols[Section]; - std::vector<uint64_t> DataMappingSymsAddr; - std::vector<uint64_t> TextMappingSymsAddr; - if (isArmElf(Obj)) { + std::vector<MappingSymbolPair> MappingSymbols; + if (hasMappingSymbols(Obj)) { for (const auto &Symb : Symbols) { uint64_t Address = std::get<0>(Symb); StringRef Name = std::get<1>(Symb); if (Name.startswith("$d")) - DataMappingSymsAddr.push_back(Address - SectionAddr); + MappingSymbols.emplace_back(Address - SectionAddr, 'd'); if (Name.startswith("$x")) - TextMappingSymsAddr.push_back(Address - SectionAddr); + MappingSymbols.emplace_back(Address - SectionAddr, 'x'); if (Name.startswith("$a")) - TextMappingSymsAddr.push_back(Address - SectionAddr); + MappingSymbols.emplace_back(Address - SectionAddr, 'a'); if (Name.startswith("$t")) - TextMappingSymsAddr.push_back(Address - SectionAddr); + MappingSymbols.emplace_back(Address - SectionAddr, 't'); } } - llvm::sort(DataMappingSymsAddr); - llvm::sort(TextMappingSymsAddr); + llvm::sort(MappingSymbols); if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) { // AMDGPU disassembler uses symbolizer for printing labels @@ -1514,19 +1205,6 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { } } - // Make a list of all the relocations for this section. - std::vector<RelocationRef> Rels; - if (InlineRelocs) { - for (const SectionRef &RelocSec : SectionRelocMap[Section]) { - for (const RelocationRef &Reloc : RelocSec.relocations()) { - Rels.push_back(Reloc); - } - } - } - - // Sort relocations by address. - llvm::sort(Rels, isRelocAddressLess); - StringRef SegmentName = ""; if (const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(Obj)) { DataRefImpl DR = Section.getRawDataRefImpl(); @@ -1546,56 +1224,54 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { SmallString<40> Comments; raw_svector_ostream CommentStream(Comments); - StringRef BytesStr; - error(Section.getContents(BytesStr)); - ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()), - BytesStr.size()); + ArrayRef<uint8_t> Bytes = arrayRefFromStringRef( + unwrapOrError(Section.getContents(), Obj->getFileName())); + + uint64_t VMAAdjustment = 0; + if (shouldAdjustVA(Section)) + VMAAdjustment = AdjustVMA; uint64_t Size; uint64_t Index; bool PrintedSection = false; - + std::vector<RelocationRef> Rels = RelocMap[Section]; std::vector<RelocationRef>::const_iterator RelCur = Rels.begin(); std::vector<RelocationRef>::const_iterator RelEnd = Rels.end(); // Disassemble symbol by symbol. for (unsigned SI = 0, SE = Symbols.size(); SI != SE; ++SI) { - uint64_t Start = std::get<0>(Symbols[SI]) - SectionAddr; - // The end is either the section end or the beginning of the next - // symbol. - uint64_t End = (SI == SE - 1) - ? SectSize - : std::get<0>(Symbols[SI + 1]) - SectionAddr; - // Don't try to disassemble beyond the end of section contents. - if (End > SectSize) - End = SectSize; - // If this symbol has the same address as the next symbol, then skip it. - if (Start >= End) - continue; + std::string SymbolName = std::get<1>(Symbols[SI]).str(); + if (Demangle) + SymbolName = demangle(SymbolName); - // Check if we need to skip symbol - // Skip if the symbol's data is not between StartAddress and StopAddress - if (End + SectionAddr < StartAddress || - Start + SectionAddr > StopAddress) { + // Skip if --disassemble-functions is not empty and the symbol is not in + // the list. + if (!DisasmFuncsSet.empty() && !DisasmFuncsSet.count(SymbolName)) continue; - } - /// Skip if user requested specific symbols and this is not in the list - if (!DisasmFuncsSet.empty() && - !DisasmFuncsSet.count(std::get<1>(Symbols[SI]))) + uint64_t Start = std::get<0>(Symbols[SI]); + if (Start < SectionAddr || StopAddress <= Start) + continue; + else + FoundDisasmFuncsSet.insert(SymbolName); + + // The end is the section end, the beginning of the next symbol, or + // --stop-address. + uint64_t End = std::min<uint64_t>(SectionAddr + SectSize, StopAddress); + if (SI + 1 < SE) + End = std::min(End, std::get<0>(Symbols[SI + 1])); + if (Start >= End || End <= StartAddress) continue; + Start -= SectionAddr; + End -= SectionAddr; if (!PrintedSection) { PrintedSection = true; - outs() << "Disassembly of section "; + outs() << "\nDisassembly of section "; if (!SegmentName.empty()) outs() << SegmentName << ","; - outs() << SectionName << ':'; + outs() << SectionName << ":\n"; } - // Stop disassembly at the stop address specified - if (End + SectionAddr > StopAddress) - End = StopAddress - SectionAddr; - if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) { if (std::get<2>(Symbols[SI]) == ELF::STT_AMDGPU_HSA_KERNEL) { // skip amd_kernel_code_t at the begining of kernel symbol (256 bytes) @@ -1615,13 +1291,10 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { outs() << '\n'; if (!NoLeadingAddr) - outs() << format("%016" PRIx64 " ", SectionAddr + Start); + outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ", + SectionAddr + Start + VMAAdjustment); - StringRef SymbolName = std::get<1>(Symbols[SI]); - if (Demangle) - outs() << demangle(SymbolName) << ":\n"; - else - outs() << SymbolName << ":\n"; + outs() << SymbolName << ":\n"; // Don't print raw contents of a virtual section. A virtual section // doesn't have any contents in the file. @@ -1636,143 +1309,82 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { raw_ostream &DebugOut = nulls(); #endif - for (Index = Start; Index < End; Index += Size) { - MCInst Inst; + // Some targets (like WebAssembly) have a special prelude at the start + // of each symbol. + DisAsm->onSymbolStart(SymbolName, Size, Bytes.slice(Start, End - Start), + SectionAddr + Start, DebugOut, CommentStream); + Start += Size; + + Index = Start; + if (SectionAddr < StartAddress) + Index = std::max<uint64_t>(Index, StartAddress - SectionAddr); + + // If there is a data/common symbol inside an ELF text section and we are + // only disassembling text (applicable all architectures), we are in a + // situation where we must print the data and not disassemble it. + if (Obj->isELF() && !DisassembleAll && Section.isText()) { + uint8_t SymTy = std::get<2>(Symbols[SI]); + if (SymTy == ELF::STT_OBJECT || SymTy == ELF::STT_COMMON) { + dumpELFData(SectionAddr, Index, End, Bytes); + Index = End; + } + } - if (Index + SectionAddr < StartAddress || - Index + SectionAddr > StopAddress) { - // skip byte by byte till StartAddress is reached - Size = 1; + bool CheckARMELFData = hasMappingSymbols(Obj) && + std::get<2>(Symbols[SI]) != ELF::STT_OBJECT && + !DisassembleAll; + while (Index < End) { + // ARM and AArch64 ELF binaries can interleave data and text in the + // same section. We rely on the markers introduced to understand what + // we need to dump. If the data marker is within a function, it is + // denoted as a word/short etc. + if (CheckARMELFData && + getMappingSymbolKind(MappingSymbols, Index) == 'd') { + Index = dumpARMELFData(SectionAddr, Index, End, Obj, Bytes, + MappingSymbols); continue; } - // AArch64 ELF binaries can interleave data and text in the - // same section. We rely on the markers introduced to - // understand what we need to dump. If the data marker is within a - // function, it is denoted as a word/short etc - if (isArmElf(Obj) && std::get<2>(Symbols[SI]) != ELF::STT_OBJECT && - !DisassembleAll) { - uint64_t Stride = 0; - - auto DAI = std::lower_bound(DataMappingSymsAddr.begin(), - DataMappingSymsAddr.end(), Index); - if (DAI != DataMappingSymsAddr.end() && *DAI == Index) { - // Switch to data. - while (Index < End) { - outs() << format("%8" PRIx64 ":", SectionAddr + Index); - outs() << "\t"; - if (Index + 4 <= End) { - Stride = 4; - dumpBytes(Bytes.slice(Index, 4), outs()); - outs() << "\t.word\t"; - uint32_t Data = 0; - if (Obj->isLittleEndian()) { - const auto Word = - reinterpret_cast<const support::ulittle32_t *>( - Bytes.data() + Index); - Data = *Word; - } else { - const auto Word = reinterpret_cast<const support::ubig32_t *>( - Bytes.data() + Index); - Data = *Word; - } - outs() << "0x" << format("%08" PRIx32, Data); - } else if (Index + 2 <= End) { - Stride = 2; - dumpBytes(Bytes.slice(Index, 2), outs()); - outs() << "\t\t.short\t"; - uint16_t Data = 0; - if (Obj->isLittleEndian()) { - const auto Short = - reinterpret_cast<const support::ulittle16_t *>( - Bytes.data() + Index); - Data = *Short; - } else { - const auto Short = - reinterpret_cast<const support::ubig16_t *>(Bytes.data() + - Index); - Data = *Short; - } - outs() << "0x" << format("%04" PRIx16, Data); - } else { - Stride = 1; - dumpBytes(Bytes.slice(Index, 1), outs()); - outs() << "\t\t.byte\t"; - outs() << "0x" << format("%02" PRIx8, Bytes.slice(Index, 1)[0]); - } - Index += Stride; - outs() << "\n"; - auto TAI = std::lower_bound(TextMappingSymsAddr.begin(), - TextMappingSymsAddr.end(), Index); - if (TAI != TextMappingSymsAddr.end() && *TAI == Index) - break; - } + + // When -z or --disassemble-zeroes are given we always dissasemble + // them. Otherwise we might want to skip zero bytes we see. + if (!DisassembleZeroes) { + uint64_t MaxOffset = End - Index; + // For -reloc: print zero blocks patched by relocations, so that + // relocations can be shown in the dump. + if (RelCur != RelEnd) + MaxOffset = RelCur->getOffset() - Index; + + if (size_t N = + countSkippableZeroBytes(Bytes.slice(Index, MaxOffset))) { + outs() << "\t\t..." << '\n'; + Index += N; + continue; } } - // If there is a data symbol inside an ELF text section and we are only - // disassembling text (applicable all architectures), - // we are in a situation where we must print the data and not - // disassemble it. - if (Obj->isELF() && std::get<2>(Symbols[SI]) == ELF::STT_OBJECT && - !DisassembleAll && Section.isText()) { - // print out data up to 8 bytes at a time in hex and ascii - uint8_t AsciiData[9] = {'\0'}; - uint8_t Byte; - int NumBytes = 0; - - for (Index = Start; Index < End; Index += 1) { - if (((SectionAddr + Index) < StartAddress) || - ((SectionAddr + Index) > StopAddress)) - continue; - if (NumBytes == 0) { - outs() << format("%8" PRIx64 ":", SectionAddr + Index); - outs() << "\t"; - } - Byte = Bytes.slice(Index)[0]; - outs() << format(" %02x", Byte); - AsciiData[NumBytes] = isPrint(Byte) ? Byte : '.'; - - uint8_t IndentOffset = 0; - NumBytes++; - if (Index == End - 1 || NumBytes > 8) { - // Indent the space for less than 8 bytes data. - // 2 spaces for byte and one for space between bytes - IndentOffset = 3 * (8 - NumBytes); - for (int Excess = 8 - NumBytes; Excess < 8; Excess++) - AsciiData[Excess] = '\0'; - NumBytes = 8; - } - if (NumBytes == 8) { - AsciiData[8] = '\0'; - outs() << std::string(IndentOffset, ' ') << " "; - outs() << reinterpret_cast<char *>(AsciiData); - outs() << '\n'; - NumBytes = 0; - } + if (SecondarySTI) { + if (getMappingSymbolKind(MappingSymbols, Index) == 'a') { + STI = PrimaryIsThumb ? SecondarySTI : PrimarySTI; + DisAsm = PrimaryIsThumb ? SecondaryDisAsm : PrimaryDisAsm; + } else if (getMappingSymbolKind(MappingSymbols, Index) == 't') { + STI = PrimaryIsThumb ? PrimarySTI : SecondarySTI; + DisAsm = PrimaryIsThumb ? PrimaryDisAsm : SecondaryDisAsm; } } - if (Index >= End) - break; - - if (size_t N = - countSkippableZeroBytes(Bytes.slice(Index, End - Index))) { - outs() << "\t\t..." << '\n'; - Index += N; - if (Index >= End) - break; - } // Disassemble a real instruction or a data when disassemble all is // provided - bool Disassembled = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), - SectionAddr + Index, DebugOut, - CommentStream); + MCInst Inst; + bool Disassembled = DisAsm->getInstruction( + Inst, Size, Bytes.slice(Index), SectionAddr + Index, DebugOut, + CommentStream); if (Size == 0) Size = 1; - PIP.printInst(*IP, Disassembled ? &Inst : nullptr, - Bytes.slice(Index, Size), SectionAddr + Index, outs(), "", - *STI, &SP, &Rels); + PIP.printInst( + *IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size), + {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, outs(), + "", *STI, &SP, &Rels); outs() << CommentStream.str(); Comments.clear(); @@ -1791,37 +1403,34 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // N.B. We don't walk the relocations in the relocatable case yet. auto *TargetSectionSymbols = &Symbols; if (!Obj->isRelocatableObject()) { - auto SectionAddress = std::upper_bound( - SectionAddresses.begin(), SectionAddresses.end(), Target, - [](uint64_t LHS, - const std::pair<uint64_t, SectionRef> &RHS) { - return LHS < RHS.first; + auto It = partition_point( + SectionAddresses, + [=](const std::pair<uint64_t, SectionRef> &O) { + return O.first <= Target; }); - if (SectionAddress != SectionAddresses.begin()) { - --SectionAddress; - TargetSectionSymbols = &AllSymbols[SectionAddress->second]; + if (It != SectionAddresses.begin()) { + --It; + TargetSectionSymbols = &AllSymbols[It->second]; } else { TargetSectionSymbols = &AbsoluteSymbols; } } - // Find the first symbol in the section whose offset is less than + // Find the last symbol in the section whose offset is less than // or equal to the target. If there isn't a section that contains // the target, find the nearest preceding absolute symbol. - auto TargetSym = std::upper_bound( - TargetSectionSymbols->begin(), TargetSectionSymbols->end(), - Target, [](uint64_t LHS, - const std::tuple<uint64_t, StringRef, uint8_t> &RHS) { - return LHS < std::get<0>(RHS); + auto TargetSym = partition_point( + *TargetSectionSymbols, + [=](const std::tuple<uint64_t, StringRef, uint8_t> &O) { + return std::get<0>(O) <= Target; }); if (TargetSym == TargetSectionSymbols->begin()) { TargetSectionSymbols = &AbsoluteSymbols; - TargetSym = std::upper_bound( - AbsoluteSymbols.begin(), AbsoluteSymbols.end(), - Target, [](uint64_t LHS, - const std::tuple<uint64_t, StringRef, uint8_t> &RHS) { - return LHS < std::get<0>(RHS); - }); + TargetSym = partition_point( + AbsoluteSymbols, + [=](const std::tuple<uint64_t, StringRef, uint8_t> &O) { + return std::get<0>(O) <= Target; + }); } if (TargetSym != TargetSectionSymbols->begin()) { --TargetSym; @@ -1838,34 +1447,125 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { outs() << "\n"; // Hexagon does this in pretty printer - if (Obj->getArch() != Triple::hexagon) + if (Obj->getArch() != Triple::hexagon) { // Print relocation for instruction. while (RelCur != RelEnd) { - uint64_t Addr = RelCur->getOffset(); - SmallString<16> Name; - SmallString<32> Val; - + uint64_t Offset = RelCur->getOffset(); // If this relocation is hidden, skip it. - if (getHidden(*RelCur) || ((SectionAddr + Addr) < StartAddress)) { + if (getHidden(*RelCur) || SectionAddr + Offset < StartAddress) { ++RelCur; continue; } - // Stop when rel_cur's address is past the current instruction. - if (Addr >= Index + Size) + // Stop when RelCur's offset is past the current instruction. + if (Offset >= Index + Size) break; - RelCur->getTypeName(Name); - error(getRelocationValueString(*RelCur, Val)); - outs() << format(Fmt.data(), SectionAddr + Addr) << Name << "\t" - << Val << "\n"; + + // When --adjust-vma is used, update the address printed. + if (RelCur->getSymbol() != Obj->symbol_end()) { + Expected<section_iterator> SymSI = + RelCur->getSymbol()->getSection(); + if (SymSI && *SymSI != Obj->section_end() && + shouldAdjustVA(**SymSI)) + Offset += AdjustVMA; + } + + printRelocation(*RelCur, SectionAddr + Offset, Is64Bits); ++RelCur; } + } + + Index += Size; } } } + StringSet<> MissingDisasmFuncsSet = + set_difference(DisasmFuncsSet, FoundDisasmFuncsSet); + for (StringRef MissingDisasmFunc : MissingDisasmFuncsSet.keys()) + warn("failed to disassemble missing function " + MissingDisasmFunc); +} + +static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { + const Target *TheTarget = getTarget(Obj); + + // Package up features to be passed to target/subtarget + SubtargetFeatures Features = Obj->getFeatures(); + if (!MAttrs.empty()) + for (unsigned I = 0; I != MAttrs.size(); ++I) + Features.AddFeature(MAttrs[I]); + + std::unique_ptr<const MCRegisterInfo> MRI( + TheTarget->createMCRegInfo(TripleName)); + if (!MRI) + report_error(Obj->getFileName(), + "no register info for target " + TripleName); + + // Set up disassembler. + std::unique_ptr<const MCAsmInfo> AsmInfo( + TheTarget->createMCAsmInfo(*MRI, TripleName)); + if (!AsmInfo) + report_error(Obj->getFileName(), + "no assembly info for target " + TripleName); + std::unique_ptr<const MCSubtargetInfo> STI( + TheTarget->createMCSubtargetInfo(TripleName, MCPU, Features.getString())); + if (!STI) + report_error(Obj->getFileName(), + "no subtarget info for target " + TripleName); + std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo()); + if (!MII) + report_error(Obj->getFileName(), + "no instruction info for target " + TripleName); + MCObjectFileInfo MOFI; + MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI); + // FIXME: for now initialize MCObjectFileInfo with default values + MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx); + + std::unique_ptr<MCDisassembler> DisAsm( + TheTarget->createMCDisassembler(*STI, Ctx)); + if (!DisAsm) + report_error(Obj->getFileName(), + "no disassembler for target " + TripleName); + + // If we have an ARM object file, we need a second disassembler, because + // ARM CPUs have two different instruction sets: ARM mode, and Thumb mode. + // We use mapping symbols to switch between the two assemblers, where + // appropriate. + std::unique_ptr<MCDisassembler> SecondaryDisAsm; + std::unique_ptr<const MCSubtargetInfo> SecondarySTI; + if (isArmElf(Obj) && !STI->checkFeatures("+mclass")) { + if (STI->checkFeatures("+thumb-mode")) + Features.AddFeature("-thumb-mode"); + else + Features.AddFeature("+thumb-mode"); + SecondarySTI.reset(TheTarget->createMCSubtargetInfo(TripleName, MCPU, + Features.getString())); + SecondaryDisAsm.reset(TheTarget->createMCDisassembler(*SecondarySTI, Ctx)); + } + + std::unique_ptr<const MCInstrAnalysis> MIA( + TheTarget->createMCInstrAnalysis(MII.get())); + + int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); + std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( + Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI)); + if (!IP) + report_error(Obj->getFileName(), + "no instruction printer for target " + TripleName); + IP->setPrintImmHex(PrintImmHex); + + PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName)); + SourcePrinter SP(Obj, TheTarget->getName()); + + for (StringRef Opt : DisassemblerOptions) + if (!IP->applyTargetSpecificCLOption(Opt)) + error("Unrecognized disassembler option: " + Opt); + + disassembleObject(TheTarget, Obj, Ctx, DisAsm.get(), SecondaryDisAsm.get(), + MIA.get(), IP.get(), STI.get(), SecondarySTI.get(), PIP, + SP, InlineRelocs); } -void llvm::printRelocations(const ObjectFile *Obj) { +void printRelocations(const ObjectFile *Obj) { StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; // Regular objdump doesn't print relocations in non-relocatable object @@ -1873,28 +1573,40 @@ void llvm::printRelocations(const ObjectFile *Obj) { if (!Obj->isRelocatableObject()) return; + // Build a mapping from relocation target to a vector of relocation + // sections. Usually, there is an only one relocation section for + // each relocated section. + MapVector<SectionRef, std::vector<SectionRef>> SecToRelSec; for (const SectionRef &Section : ToolSectionFilter(*Obj)) { if (Section.relocation_begin() == Section.relocation_end()) continue; + const SectionRef TargetSec = *Section.getRelocatedSection(); + SecToRelSec[TargetSec].push_back(Section); + } + + for (std::pair<SectionRef, std::vector<SectionRef>> &P : SecToRelSec) { StringRef SecName; - error(Section.getName(SecName)); + error(P.first.getName(SecName)); outs() << "RELOCATION RECORDS FOR [" << SecName << "]:\n"; - for (const RelocationRef &Reloc : Section.relocations()) { - uint64_t Address = Reloc.getOffset(); - SmallString<32> RelocName; - SmallString<32> ValueStr; - if (Address < StartAddress || Address > StopAddress || getHidden(Reloc)) - continue; - Reloc.getTypeName(RelocName); - error(getRelocationValueString(Reloc, ValueStr)); - outs() << format(Fmt.data(), Address) << " " << RelocName << " " - << ValueStr << "\n"; + + for (SectionRef Section : P.second) { + for (const RelocationRef &Reloc : Section.relocations()) { + uint64_t Address = Reloc.getOffset(); + SmallString<32> RelocName; + SmallString<32> ValueStr; + if (Address < StartAddress || Address > StopAddress || getHidden(Reloc)) + continue; + Reloc.getTypeName(RelocName); + error(getRelocationValueString(Reloc, ValueStr)); + outs() << format(Fmt.data(), Address) << " " << RelocName << " " + << ValueStr << "\n"; + } } outs() << "\n"; } } -void llvm::printDynamicRelocations(const ObjectFile *Obj) { +void printDynamicRelocations(const ObjectFile *Obj) { // For the moment, this option is for ELF only if (!Obj->isELF()) return; @@ -1911,9 +1623,7 @@ void llvm::printDynamicRelocations(const ObjectFile *Obj) { outs() << "DYNAMIC RELOCATION RECORDS\n"; StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; - for (const SectionRef &Section : DynRelSec) { - if (Section.relocation_begin() == Section.relocation_end()) - continue; + for (const SectionRef &Section : DynRelSec) for (const RelocationRef &Reloc : Section.relocations()) { uint64_t Address = Reloc.getOffset(); SmallString<32> RelocName; @@ -1923,34 +1633,60 @@ void llvm::printDynamicRelocations(const ObjectFile *Obj) { outs() << format(Fmt.data(), Address) << " " << RelocName << " " << ValueStr << "\n"; } - } } -void llvm::printSectionHeaders(const ObjectFile *Obj) { - outs() << "Sections:\n" - "Idx Name Size Address Type\n"; +// Returns true if we need to show LMA column when dumping section headers. We +// show it only when the platform is ELF and either we have at least one section +// whose VMA and LMA are different and/or when --show-lma flag is used. +static bool shouldDisplayLMA(const ObjectFile *Obj) { + if (!Obj->isELF()) + return false; + for (const SectionRef &S : ToolSectionFilter(*Obj)) + if (S.getAddress() != getELFSectionLMA(S)) + return true; + return ShowLMA; +} + +void printSectionHeaders(const ObjectFile *Obj) { + bool HasLMAColumn = shouldDisplayLMA(Obj); + if (HasLMAColumn) + outs() << "Sections:\n" + "Idx Name Size VMA LMA " + "Type\n"; + else + outs() << "Sections:\n" + "Idx Name Size VMA Type\n"; + for (const SectionRef &Section : ToolSectionFilter(*Obj)) { StringRef Name; error(Section.getName(Name)); - uint64_t Address = Section.getAddress(); + uint64_t VMA = Section.getAddress(); + if (shouldAdjustVA(Section)) + VMA += AdjustVMA; + uint64_t Size = Section.getSize(); bool Text = Section.isText(); bool Data = Section.isData(); bool BSS = Section.isBSS(); std::string Type = (std::string(Text ? "TEXT " : "") + (Data ? "DATA " : "") + (BSS ? "BSS" : "")); - outs() << format("%3d %-13s %08" PRIx64 " %016" PRIx64 " %s\n", - (unsigned)Section.getIndex(), Name.str().c_str(), Size, - Address, Type.c_str()); + + if (HasLMAColumn) + outs() << format("%3d %-13s %08" PRIx64 " %016" PRIx64 " %016" PRIx64 + " %s\n", + (unsigned)Section.getIndex(), Name.str().c_str(), Size, + VMA, getELFSectionLMA(Section), Type.c_str()); + else + outs() << format("%3d %-13s %08" PRIx64 " %016" PRIx64 " %s\n", + (unsigned)Section.getIndex(), Name.str().c_str(), Size, + VMA, Type.c_str()); } outs() << "\n"; } -void llvm::printSectionContents(const ObjectFile *Obj) { - std::error_code EC; +void printSectionContents(const ObjectFile *Obj) { for (const SectionRef &Section : ToolSectionFilter(*Obj)) { StringRef Name; - StringRef Contents; error(Section.getName(Name)); uint64_t BaseAddr = Section.getAddress(); uint64_t Size = Section.getSize(); @@ -1965,7 +1701,7 @@ void llvm::printSectionContents(const ObjectFile *Obj) { continue; } - error(Section.getContents(Contents)); + StringRef Contents = unwrapOrError(Section.getContents(), Obj->getFileName()); // Dump out the content as hex and printable ascii characters. for (std::size_t Addr = 0, End = Contents.size(); Addr < End; Addr += 16) { @@ -1993,8 +1729,8 @@ void llvm::printSectionContents(const ObjectFile *Obj) { } } -void llvm::printSymbolTable(const ObjectFile *O, StringRef ArchiveName, - StringRef ArchitectureName) { +void printSymbolTable(const ObjectFile *O, StringRef ArchiveName, + StringRef ArchitectureName) { outs() << "SYMBOL TABLE:\n"; if (const COFFObjectFile *Coff = dyn_cast<const COFFObjectFile>(O)) { @@ -2002,41 +1738,24 @@ void llvm::printSymbolTable(const ObjectFile *O, StringRef ArchiveName, return; } + const StringRef FileName = O->getFileName(); for (auto I = O->symbol_begin(), E = O->symbol_end(); I != E; ++I) { - // Skip printing the special zero symbol when dumping an ELF file. - // This makes the output consistent with the GNU objdump. - if (I == O->symbol_begin() && isa<ELFObjectFileBase>(O)) - continue; - const SymbolRef &Symbol = *I; - Expected<uint64_t> AddressOrError = Symbol.getAddress(); - if (!AddressOrError) - report_error(ArchiveName, O->getFileName(), AddressOrError.takeError(), - ArchitectureName); - uint64_t Address = *AddressOrError; + uint64_t Address = unwrapOrError(Symbol.getAddress(), ArchiveName, FileName, + ArchitectureName); if ((Address < StartAddress) || (Address > StopAddress)) continue; - Expected<SymbolRef::Type> TypeOrError = Symbol.getType(); - if (!TypeOrError) - report_error(ArchiveName, O->getFileName(), TypeOrError.takeError(), - ArchitectureName); - SymbolRef::Type Type = *TypeOrError; + SymbolRef::Type Type = unwrapOrError(Symbol.getType(), ArchiveName, + FileName, ArchitectureName); uint32_t Flags = Symbol.getFlags(); - Expected<section_iterator> SectionOrErr = Symbol.getSection(); - if (!SectionOrErr) - report_error(ArchiveName, O->getFileName(), SectionOrErr.takeError(), - ArchitectureName); - section_iterator Section = *SectionOrErr; + section_iterator Section = unwrapOrError(Symbol.getSection(), ArchiveName, + FileName, ArchitectureName); StringRef Name; - if (Type == SymbolRef::ST_Debug && Section != O->section_end()) { + if (Type == SymbolRef::ST_Debug && Section != O->section_end()) Section->getName(Name); - } else { - Expected<StringRef> NameOrErr = Symbol.getName(); - if (!NameOrErr) - report_error(ArchiveName, O->getFileName(), NameOrErr.takeError(), - ArchitectureName); - Name = *NameOrErr; - } + else + Name = unwrapOrError(Symbol.getName(), ArchiveName, FileName, + ArchitectureName); bool Global = Flags & SymbolRef::SF_Global; bool Weak = Flags & SymbolRef::SF_Weak; @@ -2136,59 +1855,9 @@ static void printUnwindInfo(const ObjectFile *O) { "for COFF and MachO object files.\n"; } -void llvm::printExportsTrie(const ObjectFile *o) { - outs() << "Exports trie:\n"; - if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) - printMachOExportsTrie(MachO); - else - WithColor::error(errs(), ToolName) - << "This operation is only currently supported " - "for Mach-O executable files.\n"; -} - -void llvm::printRebaseTable(ObjectFile *o) { - outs() << "Rebase table:\n"; - if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) - printMachORebaseTable(MachO); - else - WithColor::error(errs(), ToolName) - << "This operation is only currently supported " - "for Mach-O executable files.\n"; -} - -void llvm::printBindTable(ObjectFile *o) { - outs() << "Bind table:\n"; - if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) - printMachOBindTable(MachO); - else - WithColor::error(errs(), ToolName) - << "This operation is only currently supported " - "for Mach-O executable files.\n"; -} - -void llvm::printLazyBindTable(ObjectFile *o) { - outs() << "Lazy bind table:\n"; - if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) - printMachOLazyBindTable(MachO); - else - WithColor::error(errs(), ToolName) - << "This operation is only currently supported " - "for Mach-O executable files.\n"; -} - -void llvm::printWeakBindTable(ObjectFile *o) { - outs() << "Weak bind table:\n"; - if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) - printMachOWeakBindTable(MachO); - else - WithColor::error(errs(), ToolName) - << "This operation is only currently supported " - "for Mach-O executable files.\n"; -} - /// Dump the raw contents of the __clangast section so the output can be piped /// into llvm-bcanalyzer. -void llvm::printRawClangAST(const ObjectFile *Obj) { +void printRawClangAST(const ObjectFile *Obj) { if (outs().is_displayed()) { WithColor::error(errs(), ToolName) << "The -raw-clang-ast option will dump the raw binary contents of " @@ -2215,8 +1884,8 @@ void llvm::printRawClangAST(const ObjectFile *Obj) { if (!ClangASTSection) return; - StringRef ClangASTContents; - error(ClangASTSection.getValue().getContents(ClangASTContents)); + StringRef ClangASTContents = unwrapOrError( + ClangASTSection.getValue().getContents(), Obj->getFileName()); outs().write(ClangASTContents.data(), ClangASTContents.size()); } @@ -2252,9 +1921,8 @@ static void printFaultMaps(const ObjectFile *Obj) { return; } - StringRef FaultMapContents; - error(FaultMapSection.getValue().getContents(FaultMapContents)); - + StringRef FaultMapContents = + unwrapOrError(FaultMapSection.getValue().getContents(), Obj->getFileName()); FaultMapParser FMP(FaultMapContents.bytes_begin(), FaultMapContents.bytes_end()); @@ -2264,7 +1932,9 @@ static void printFaultMaps(const ObjectFile *Obj) { static void printPrivateFileHeaders(const ObjectFile *O, bool OnlyFirst) { if (O->isELF()) { printELFFileHeader(O); - return printELFDynamicSection(O); + printELFDynamicSection(O); + printELFSymbolVersionInfo(O); + return; } if (O->isCOFF()) return printCOFFFileHeader(O); @@ -2285,12 +1955,9 @@ static void printFileHeaders(const ObjectFile *O) { Triple::ArchType AT = O->getArch(); outs() << "architecture: " << Triple::getArchTypeName(AT) << "\n"; - Expected<uint64_t> StartAddrOrErr = O->getStartAddress(); - if (!StartAddrOrErr) - report_error(O->getFileName(), StartAddrOrErr.takeError()); + uint64_t Address = unwrapOrError(O->getStartAddress(), O->getFileName()); StringRef Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; - uint64_t Address = StartAddrOrErr.get(); outs() << "start address: " << "0x" << format(Fmt.data(), Address) << "\n\n"; } @@ -2315,22 +1982,9 @@ static void printArchiveChild(StringRef Filename, const Archive::Child &C) { outs() << " "; - Expected<unsigned> UIDOrErr = C.getUID(); - if (!UIDOrErr) - report_error(Filename, UIDOrErr.takeError()); - unsigned UID = UIDOrErr.get(); - outs() << format("%d/", UID); - - Expected<unsigned> GIDOrErr = C.getGID(); - if (!GIDOrErr) - report_error(Filename, GIDOrErr.takeError()); - unsigned GID = GIDOrErr.get(); - outs() << format("%-d ", GID); - - Expected<uint64_t> Size = C.getRawSize(); - if (!Size) - report_error(Filename, Size.takeError()); - outs() << format("%6" PRId64, Size.get()) << " "; + outs() << format("%d/%d %6" PRId64 " ", unwrapOrError(C.getUID(), Filename), + unwrapOrError(C.getGID(), Filename), + unwrapOrError(C.getRawSize(), Filename)); StringRef RawLastModified = C.getRawLastModified(); unsigned Seconds; @@ -2349,10 +2003,7 @@ static void printArchiveChild(StringRef Filename, const Archive::Child &C) { Expected<StringRef> NameOrErr = C.getName(); if (!NameOrErr) { consumeError(NameOrErr.takeError()); - Expected<StringRef> RawNameOrErr = C.getRawName(); - if (!RawNameOrErr) - report_error(Filename, NameOrErr.takeError()); - Name = RawNameOrErr.get(); + Name = unwrapOrError(C.getRawName(), Filename); } else { Name = NameOrErr.get(); } @@ -2404,7 +2055,7 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr, printWeakBindTable(O); if (RawClangAST) printRawClangAST(O); - if (PrintFaultMaps) + if (FaultMapSection) printFaultMaps(O); if (DwarfDumpType != DIDT_Null) { std::unique_ptr<DIContext> DICtx = DWARFContext::create(*O); @@ -2439,7 +2090,7 @@ static void dumpArchive(const Archive *A) { Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(A->getFileName(), C, std::move(E)); + report_error(std::move(E), A->getFileName(), C); continue; } if (ObjectFile *O = dyn_cast<ObjectFile>(&*ChildOrErr.get())) @@ -2447,10 +2098,11 @@ static void dumpArchive(const Archive *A) { else if (COFFImportFile *I = dyn_cast<COFFImportFile>(&*ChildOrErr.get())) dumpObject(I, A, &C); else - report_error(A->getFileName(), object_error::invalid_file_type); + report_error(errorCodeToError(object_error::invalid_file_type), + A->getFileName()); } if (Err) - report_error(A->getFileName(), std::move(Err)); + report_error(std::move(Err), A->getFileName()); } /// Open file and figure out how to dump it. @@ -2464,10 +2116,8 @@ static void dumpInput(StringRef file) { } // Attempt to open the binary. - Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(file); - if (!BinaryOrErr) - report_error(file, BinaryOrErr.takeError()); - Binary &Binary = *BinaryOrErr.get().getBinary(); + OwningBinary<Binary> OBinary = unwrapOrError(createBinary(file), file); + Binary &Binary = *OBinary.getBinary(); if (Archive *A = dyn_cast<Archive>(&Binary)) dumpArchive(A); @@ -2476,22 +2126,29 @@ static void dumpInput(StringRef file) { else if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Binary)) parseInputMachO(UB); else - report_error(file, object_error::invalid_file_type); + report_error(errorCodeToError(object_error::invalid_file_type), file); } +} // namespace llvm int main(int argc, char **argv) { + using namespace llvm; InitLLVM X(argc, argv); + const cl::OptionCategory *OptionFilters[] = {&ObjdumpCat, &MachOCat}; + cl::HideUnrelatedOptions(OptionFilters); // Initialize targets and assembly printers/parsers. - llvm::InitializeAllTargetInfos(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllDisassemblers(); + InitializeAllTargetInfos(); + InitializeAllTargetMCs(); + InitializeAllDisassemblers(); // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n"); + if (StartAddress >= StopAddress) + error("start address should be less than stop address"); + ToolName = argv[0]; // Defaults to a.out if no filenames specified. @@ -2499,40 +2156,22 @@ int main(int argc, char **argv) { InputFilenames.push_back("a.out"); if (AllHeaders) - FileHeaders = PrivateHeaders = Relocations = SectionHeaders = SymbolTable = - true; + ArchiveHeaders = FileHeaders = PrivateHeaders = Relocations = + SectionHeaders = SymbolTable = true; - if (DisassembleAll || PrintSource || PrintLines) + if (DisassembleAll || PrintSource || PrintLines || + (!DisassembleFunctions.empty())) Disassemble = true; - if (!Disassemble - && !Relocations - && !DynamicRelocations - && !SectionHeaders - && !SectionContents - && !SymbolTable - && !UnwindInfo - && !PrivateHeaders - && !FileHeaders - && !FirstPrivateHeader - && !ExportsTrie - && !Rebase - && !Bind - && !LazyBind - && !WeakBind - && !RawClangAST - && !(UniversalHeaders && MachOOpt) - && !ArchiveHeaders - && !(IndirectSymbols && MachOOpt) - && !(DataInCode && MachOOpt) - && !(LinkOptHints && MachOOpt) - && !(InfoPlist && MachOOpt) - && !(DylibsUsed && MachOOpt) - && !(DylibId && MachOOpt) - && !(ObjcMetaData && MachOOpt) - && !(!FilterSections.empty() && MachOOpt) - && !PrintFaultMaps - && DwarfDumpType == DIDT_Null) { + if (!ArchiveHeaders && !Disassemble && DwarfDumpType == DIDT_Null && + !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST && + !Relocations && !SectionHeaders && !SectionContents && !SymbolTable && + !UnwindInfo && !FaultMapSection && + !(MachOOpt && + (Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie || + FirstPrivateHeader || IndirectSymbols || InfoPlist || LazyBind || + LinkOptHints || ObjcMetaData || Rebase || UniversalHeaders || + WeakBind || !FilterSections.empty()))) { cl::PrintHelpMessage(); return 2; } @@ -2542,5 +2181,7 @@ int main(int argc, char **argv) { llvm::for_each(InputFilenames, dumpInput); + warnOnNoMatchForSections(); + return EXIT_SUCCESS; } diff --git a/contrib/llvm/tools/llvm-objdump/llvm-objdump.h b/contrib/llvm/tools/llvm-objdump/llvm-objdump.h index fe2cb05fe227..e58d4a05c2e6 100644 --- a/contrib/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/contrib/llvm/tools/llvm-objdump/llvm-objdump.h @@ -1,8 +1,7 @@ // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -19,57 +18,86 @@ namespace llvm { class StringRef; namespace object { - class COFFObjectFile; - class COFFImportFile; - class MachOObjectFile; - class MachOUniversalBinary; - class ObjectFile; - class Archive; - class RelocationRef; +class COFFObjectFile; +class COFFImportFile; +class ELFObjectFileBase; +class ELFSectionRef; +class MachOObjectFile; +class MachOUniversalBinary; +class RelocationRef; } -extern cl::opt<std::string> TripleName; -extern cl::opt<std::string> ArchName; -extern cl::opt<std::string> MCPU; -extern cl::list<std::string> MAttrs; -extern cl::list<std::string> FilterSections; -extern cl::opt<bool> AllHeaders; extern cl::opt<bool> Demangle; -extern cl::opt<bool> Disassemble; -extern cl::opt<bool> DisassembleAll; -extern cl::opt<bool> NoShowRawInsn; -extern cl::opt<bool> NoLeadingAddr; -extern cl::opt<bool> PrivateHeaders; -extern cl::opt<bool> FileHeaders; -extern cl::opt<bool> FirstPrivateHeader; -extern cl::opt<bool> ExportsTrie; -extern cl::opt<bool> Rebase; -extern cl::opt<bool> Bind; -extern cl::opt<bool> LazyBind; -extern cl::opt<bool> WeakBind; -extern cl::opt<bool> RawClangAST; -extern cl::opt<bool> UniversalHeaders; -extern cl::opt<bool> ArchiveHeaders; -extern cl::opt<bool> IndirectSymbols; -extern cl::opt<bool> DataInCode; -extern cl::opt<bool> LinkOptHints; -extern cl::opt<bool> InfoPlist; -extern cl::opt<bool> DylibsUsed; -extern cl::opt<bool> DylibId; -extern cl::opt<bool> ObjcMetaData; -extern cl::opt<std::string> DisSymName; -extern cl::opt<bool> NonVerbose; -extern cl::opt<bool> Relocations; -extern cl::opt<bool> DynamicRelocations; -extern cl::opt<bool> SectionHeaders; -extern cl::opt<bool> SectionContents; -extern cl::opt<bool> SymbolTable; -extern cl::opt<bool> UnwindInfo; -extern cl::opt<bool> PrintImmHex; -extern cl::opt<DIDumpType> DwarfDumpType; + +typedef std::function<bool(llvm::object::SectionRef const &)> FilterPredicate; + +class SectionFilterIterator { +public: + SectionFilterIterator(FilterPredicate P, + llvm::object::section_iterator const &I, + llvm::object::section_iterator const &E) + : Predicate(std::move(P)), Iterator(I), End(E) { + ScanPredicate(); + } + const llvm::object::SectionRef &operator*() const { return *Iterator; } + SectionFilterIterator &operator++() { + ++Iterator; + ScanPredicate(); + return *this; + } + bool operator!=(SectionFilterIterator const &Other) const { + return Iterator != Other.Iterator; + } + +private: + void ScanPredicate() { + while (Iterator != End && !Predicate(*Iterator)) { + ++Iterator; + } + } + FilterPredicate Predicate; + llvm::object::section_iterator Iterator; + llvm::object::section_iterator End; +}; + +class SectionFilter { +public: + SectionFilter(FilterPredicate P, llvm::object::ObjectFile const &O) + : Predicate(std::move(P)), Object(O) {} + SectionFilterIterator begin() { + return SectionFilterIterator(Predicate, Object.section_begin(), + Object.section_end()); + } + SectionFilterIterator end() { + return SectionFilterIterator(Predicate, Object.section_end(), + Object.section_end()); + } + +private: + FilterPredicate Predicate; + llvm::object::ObjectFile const &Object; +}; // Various helper functions. +SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O); + +Error getELFRelocationValueString(const object::ELFObjectFileBase *Obj, + const object::RelocationRef &Rel, + llvm::SmallVectorImpl<char> &Result); +Error getCOFFRelocationValueString(const object::COFFObjectFile *Obj, + const object::RelocationRef &Rel, + llvm::SmallVectorImpl<char> &Result); +Error getWasmRelocationValueString(const object::WasmObjectFile *Obj, + const object::RelocationRef &RelRef, + llvm::SmallVectorImpl<char> &Result); +Error getMachORelocationValueString(const object::MachOObjectFile *Obj, + const object::RelocationRef &RelRef, + llvm::SmallVectorImpl<char> &Result); + +uint64_t getELFSectionLMA(const object::ELFSectionRef& Sec); + void error(std::error_code ec); +void error(Error E); bool isRelocAddressLess(object::RelocationRef A, object::RelocationRef B); void parseInputMachO(StringRef Filename); void parseInputMachO(object::MachOUniversalBinary *UB); @@ -82,6 +110,7 @@ void printMachOLazyBindTable(object::MachOObjectFile *O); void printMachOWeakBindTable(object::MachOObjectFile *O); void printELFFileHeader(const object::ObjectFile *O); void printELFDynamicSection(const object::ObjectFile *Obj); +void printELFSymbolVersionInfo(const object::ObjectFile *Obj); void printCOFFFileHeader(const object::ObjectFile *O); void printCOFFSymbolTable(const object::COFFImportFile *I); void printCOFFSymbolTable(const object::COFFObjectFile *O); @@ -103,18 +132,20 @@ void printSymbolTable(const object::ObjectFile *O, StringRef ArchiveName, void warn(StringRef Message); LLVM_ATTRIBUTE_NORETURN void error(Twine Message); LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, Twine Message); -LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, std::error_code EC); -LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, llvm::Error E); -LLVM_ATTRIBUTE_NORETURN void report_error(StringRef FileName, - StringRef ArchiveName, - llvm::Error E, - StringRef ArchitectureName - = StringRef()); -LLVM_ATTRIBUTE_NORETURN void report_error(StringRef ArchiveName, - const object::Archive::Child &C, - llvm::Error E, - StringRef ArchitectureName - = StringRef()); +LLVM_ATTRIBUTE_NORETURN void report_error(Error E, StringRef File); +LLVM_ATTRIBUTE_NORETURN void +report_error(Error E, StringRef FileName, StringRef ArchiveName, + StringRef ArchitectureName = StringRef()); +LLVM_ATTRIBUTE_NORETURN void +report_error(Error E, StringRef ArchiveName, const object::Archive::Child &C, + StringRef ArchitectureName = StringRef()); + +template <typename T, typename... Ts> +T unwrapOrError(Expected<T> EO, Ts &&... Args) { + if (EO) + return std::move(*EO); + report_error(EO.takeError(), std::forward<Ts>(Args)...); +} } // end namespace llvm diff --git a/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.cpp b/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.cpp index 2b96c8f986aa..162d12c120b4 100644 --- a/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.cpp @@ -1,9 +1,8 @@ //===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -341,9 +340,7 @@ static void iterateOneModule(PDBFile &File, LinePrinter &P, if (ModiStream == kInvalidStreamIndex) return; - auto ModStreamData = MappedBlockStream::createIndexedStream( - File.getMsfLayout(), File.getMsfBuffer(), ModiStream, - File.getAllocator()); + auto ModStreamData = File.createIndexedStream(ModiStream); ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData)); if (auto EC = ModStream.reload()) { P.formatLine("Could not parse debug information."); diff --git a/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.h index aa5342998e56..d3aceb47679e 100644 --- a/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.h +++ b/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.h @@ -1,9 +1,8 @@ //===- BytesOutputStyle.h ------------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp index e4f6aa7f6ec5..962d4cf88a8a 100644 --- a/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -1,9 +1,8 @@ //===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -14,6 +13,7 @@ #include "MinimalSymbolDumper.h" #include "MinimalTypeDumper.h" #include "StreamUtil.h" +#include "TypeReferenceTracker.h" #include "llvm-pdbutil.h" #include "llvm/ADT/STLExtras.h" @@ -61,7 +61,12 @@ using namespace llvm::msf; using namespace llvm::pdb; DumpOutputStyle::DumpOutputStyle(InputFile &File) - : File(File), P(2, false, outs()) {} + : File(File), P(2, false, outs()) { + if (opts::dump::DumpTypeRefStats) + RefTracker.reset(new TypeReferenceTracker(File)); +} + +DumpOutputStyle::~DumpOutputStyle() {} PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); } object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); } @@ -77,6 +82,10 @@ void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) { } Error DumpOutputStyle::dump() { + // Walk symbols & globals if we are supposed to mark types referenced. + if (opts::dump::DumpTypeRefStats) + RefTracker->mark(); + if (opts::dump::DumpSummary) { if (auto EC = dumpFileSummary()) return EC; @@ -101,6 +110,12 @@ Error DumpOutputStyle::dump() { P.NewLine(); } + if (opts::dump::DumpTypeStats) { + if (auto EC = dumpTypeStats()) + return EC; + P.NewLine(); + } + if (opts::dump::DumpNamedStreams) { if (auto EC = dumpNamedStreams()) return EC; @@ -188,6 +203,11 @@ Error DumpOutputStyle::dump() { return EC; } + if (opts::dump::DumpTypeRefStats) { + if (auto EC = dumpTypeRefStats()) + return EC; + } + if (opts::dump::DumpSectionHeaders) { if (auto EC = dumpSectionHeaders()) return EC; @@ -203,6 +223,8 @@ Error DumpOutputStyle::dump() { return EC; } + P.NewLine(); + return Error::success(); } @@ -293,18 +315,30 @@ static inline std::string formatModuleDetailKind(SymbolKind K) { return formatSymbolKind(K); } +// Get the stats sorted by size, descending. +std::vector<StatCollection::KindAndStat> +StatCollection::getStatsSortedBySize() const { + std::vector<KindAndStat> SortedStats(Individual.begin(), Individual.end()); + llvm::stable_sort(SortedStats, + [](const KindAndStat &LHS, const KindAndStat &RHS) { + return LHS.second.Size > RHS.second.Size; + }); + return SortedStats; +} + template <typename Kind> static void printModuleDetailStats(LinePrinter &P, StringRef Label, const StatCollection &Stats) { P.NewLine(); P.formatLine(" {0}", Label); AutoIndent Indent(P); - P.formatLine("{0,40}: {1,7} entries ({2,8} bytes)", "Total", + P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", "Total", Stats.Totals.Count, Stats.Totals.Size); P.formatLine("{0}", fmt_repeat('-', 74)); - for (const auto &K : Stats.Individual) { + + for (const auto &K : Stats.getStatsSortedBySize()) { std::string KindName = formatModuleDetailKind(Kind(K.first)); - P.formatLine("{0,40}: {1,7} entries ({2,8} bytes)", KindName, + P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", KindName, K.second.Count, K.second.Size); } } @@ -662,6 +696,35 @@ Error DumpOutputStyle::dumpSymbolStats() { return Error::success(); } +Error DumpOutputStyle::dumpTypeStats() { + printHeader(P, "Type Record Stats"); + + // Iterate the types, categorize by kind, accumulate size stats. + StatCollection TypeStats; + LazyRandomTypeCollection &Types = File.types(); + for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) { + CVType Type = Types.getType(*TI); + TypeStats.update(uint32_t(Type.kind()), Type.length()); + } + + P.NewLine(); + P.formatLine(" Types"); + AutoIndent Indent(P); + P.formatLine("{0,14}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total", + TypeStats.Totals.Count, TypeStats.Totals.Size, + (double)TypeStats.Totals.Size / TypeStats.Totals.Count); + P.formatLine("{0}", fmt_repeat('-', 74)); + + for (const auto &K : TypeStats.getStatsSortedBySize()) { + P.formatLine("{0,14}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", + formatTypeLeafKind(TypeLeafKind(K.first)), K.second.Count, + K.second.Size, (double)K.second.Size / K.second.Count); + } + + + return Error::success(); +} + static bool isValidNamespaceIdentifier(StringRef S) { if (S.empty()) return false; @@ -806,7 +869,7 @@ Error DumpOutputStyle::dumpUdtStats() { fmt_align(SizeHeader, AlignStyle::Right, SD)); P.formatLine("{0}", fmt_repeat('-', TableWidth)); - for (const auto &Stat : UdtTargetStats.Individual) { + for (const auto &Stat : UdtTargetStats.getStatsSortedBySize()) { StringRef Label = getUdtStatLabel(Stat.first); P.formatLine("{0} | {1:N} {2:N}", fmt_align(Label, AlignStyle::Right, FieldWidth), @@ -819,12 +882,25 @@ Error DumpOutputStyle::dumpUdtStats() { fmt_align(UdtStats.Totals.Count, AlignStyle::Right, CD), fmt_align(UdtStats.Totals.Size, AlignStyle::Right, SD)); P.formatLine("{0}", fmt_repeat('-', TableWidth)); - for (const auto &Stat : NamespacedStats) { - std::string Label = formatv("namespace '{0}'", Stat.getKey()); + struct StrAndStat { + StringRef Key; + StatCollection::Stat Stat; + }; + + // Print namespace stats in descending order of size. + std::vector<StrAndStat> NamespacedStatsSorted; + for (const auto &Stat : NamespacedStats) + NamespacedStatsSorted.push_back({Stat.getKey(), Stat.second}); + llvm::stable_sort(NamespacedStatsSorted, + [](const StrAndStat &L, const StrAndStat &R) { + return L.Stat.Size > R.Stat.Size; + }); + for (const auto &Stat : NamespacedStatsSorted) { + std::string Label = formatv("namespace '{0}'", Stat.Key); P.formatLine("{0} | {1:N} {2:N}", fmt_align(Label, AlignStyle::Right, FieldWidth), - fmt_align(Stat.second.Count, AlignStyle::Right, CD), - fmt_align(Stat.second.Size, AlignStyle::Right, SD)); + fmt_align(Stat.Stat.Count, AlignStyle::Right, CD), + fmt_align(Stat.Stat.Size, AlignStyle::Right, SD)); } return Error::success(); } @@ -921,6 +997,10 @@ Error DumpOutputStyle::dumpInlineeLines() { P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee, fmtle(Entry.Header->SourceLineNum)); Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true); + for (const auto &ExtraFileID : Entry.ExtraFiles) { + P.formatLine(" "); + Strings.formatFromChecksumsOffset(P, ExtraFileID, true); + } } P.NewLine(); }); @@ -1011,17 +1091,12 @@ Error DumpOutputStyle::dumpOldFpo(PDBFile &File) { ExitOnError Err("Error dumping old fpo data:"); auto &Dbi = Err(File.getPDBDbiStream()); - uint32_t Index = Dbi.getDebugStreamIndex(DbgHeaderType::FPO); - if (Index == kInvalidStreamIndex) { + if (!Dbi.hasOldFpoRecords()) { printStreamNotPresent("FPO"); return Error::success(); } - std::unique_ptr<MappedBlockStream> OldFpo = File.createIndexedStream(Index); - BinaryStreamReader Reader(*OldFpo); - FixedStreamArray<object::FpoData> Records; - Err(Reader.readArray(Records, - Reader.bytesRemaining() / sizeof(object::FpoData))); + const FixedStreamArray<object::FpoData>& Records = Dbi.getOldFpoRecords(); P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use " "BP | Has SEH | Frame Type"); @@ -1043,18 +1118,12 @@ Error DumpOutputStyle::dumpNewFpo(PDBFile &File) { ExitOnError Err("Error dumping new fpo data:"); auto &Dbi = Err(File.getPDBDbiStream()); - uint32_t Index = Dbi.getDebugStreamIndex(DbgHeaderType::NewFPO); - if (Index == kInvalidStreamIndex) { + if (!Dbi.hasNewFpoRecords()) { printStreamNotPresent("New FPO"); return Error::success(); } - std::unique_ptr<MappedBlockStream> NewFpo = File.createIndexedStream(Index); - - DebugFrameDataSubsectionRef FDS; - if (auto EC = FDS.initialize(*NewFpo)) - return make_error<RawError>(raw_error_code::corrupt_file, - "Invalid new fpo stream"); + const DebugFrameDataSubsectionRef& FDS = Dbi.getNewFpoRecords(); P.printLine(" RVA | Code | Locals | Params | Stack | Prolog | Saved Regs " "| Has SEH | Has C++EH | Start | Program"); @@ -1239,14 +1308,15 @@ static void buildDepSet(LazyRandomTypeCollection &Types, static void dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, - uint32_t NumTypeRecords, uint32_t NumHashBuckets, + TypeReferenceTracker *RefTracker, uint32_t NumTypeRecords, + uint32_t NumHashBuckets, FixedStreamArray<support::ulittle32_t> HashValues, TpiStream *Stream, bool Bytes, bool Extras) { Printer.formatLine("Showing {0:N} records", NumTypeRecords); uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords); - MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, + MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker, NumHashBuckets, HashValues, Stream); if (auto EC = codeview::visitTypeStream(Types, V)) { @@ -1257,12 +1327,13 @@ dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, static void dumpPartialTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, + TypeReferenceTracker *RefTracker, TpiStream &Stream, ArrayRef<TypeIndex> TiList, bool Bytes, bool Extras, bool Deps) { uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); - MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, + MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker, Stream.getNumHashBuckets(), Stream.getHashValues(), &Stream); @@ -1311,12 +1382,12 @@ Error DumpOutputStyle::dumpTypesFromObjectFile() { else continue; - StringRef Contents; - if (auto EC = S.getContents(Contents)) - return errorCodeToError(EC); + Expected<StringRef> ContentsOrErr = S.getContents(); + if (!ContentsOrErr) + return ContentsOrErr.takeError(); uint32_t Magic; - BinaryStreamReader Reader(Contents, llvm::support::little); + BinaryStreamReader Reader(*ContentsOrErr, llvm::support::little); if (auto EC = Reader.readInteger(Magic)) return EC; if (Magic != COFF::DEBUG_SECTION_MAGIC) @@ -1326,8 +1397,8 @@ Error DumpOutputStyle::dumpTypesFromObjectFile() { Types.reset(Reader, 100); if (opts::dump::DumpTypes) { - dumpFullTypeStream(P, Types, 0, 0, {}, nullptr, opts::dump::DumpTypeData, - false); + dumpFullTypeStream(P, Types, RefTracker.get(), 0, 0, {}, nullptr, + opts::dump::DumpTypeData, false); } else if (opts::dump::DumpTypeExtras) { auto LocalHashes = LocallyHashedType::hashTypeCollection(Types); auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types); @@ -1396,23 +1467,36 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids(); + // Only emit notes about referenced/unreferenced for types. + TypeReferenceTracker *MaybeTracker = + (StreamIdx == StreamTPI) ? RefTracker.get() : nullptr; + // Enable resolving forward decls. Stream.buildHashMap(); if (DumpTypes || !Indices.empty()) { if (Indices.empty()) - dumpFullTypeStream(P, Types, Stream.getNumTypeRecords(), + dumpFullTypeStream(P, Types, MaybeTracker, Stream.getNumTypeRecords(), Stream.getNumHashBuckets(), Stream.getHashValues(), &Stream, DumpBytes, DumpExtras); else { std::vector<TypeIndex> TiList(Indices.begin(), Indices.end()); - dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, - opts::dump::DumpTypeDependents); + dumpPartialTypeStream(P, Types, MaybeTracker, Stream, TiList, DumpBytes, + DumpExtras, opts::dump::DumpTypeDependents); } } if (DumpExtras) { P.NewLine(); + + P.formatLine("Header Version: {0}", + static_cast<uint32_t>(Stream.getTpiVersion())); + P.formatLine("Hash Stream Index: {0}", Stream.getTypeHashStreamIndex()); + P.formatLine("Aux Hash Stream Index: {0}", + Stream.getTypeHashStreamAuxIndex()); + P.formatLine("Hash Key Size: {0}", Stream.getHashKeySize()); + P.formatLine("Num Hash Buckets: {0}", Stream.getNumHashBuckets()); + auto IndexOffsets = Stream.getTypeIndexOffsets(); P.formatLine("Type Index Offsets:"); for (const auto &IO : IndexOffsets) { @@ -1523,6 +1607,34 @@ Error DumpOutputStyle::dumpModuleSymsForPdb() { return Error::success(); } +Error DumpOutputStyle::dumpTypeRefStats() { + printHeader(P, "Type Reference Statistics"); + AutoIndent Indent(P); + + // Sum the byte size of all type records, and the size and count of all + // referenced records. + size_t TotalRecs = File.types().size(); + size_t RefRecs = 0; + size_t TotalBytes = 0; + size_t RefBytes = 0; + auto &Types = File.types(); + for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) { + CVType Type = File.types().getType(*TI); + TotalBytes += Type.length(); + if (RefTracker->isTypeReferenced(*TI)) { + ++RefRecs; + RefBytes += Type.length(); + } + } + + P.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs, TotalRecs, + (double)RefRecs / TotalRecs); + P.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes, TotalBytes, + (double)RefBytes / TotalBytes); + + return Error::success(); +} + Error DumpOutputStyle::dumpGSIRecords() { printHeader(P, "GSI Records"); diff --git a/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h index 9b3a85587bde..796cd7a10c36 100644 --- a/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h +++ b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h @@ -1,9 +1,8 @@ //===- DumpOutputStyle.h -------------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -35,6 +34,7 @@ class COFFObjectFile; namespace pdb { class GSIHashTable; class InputFile; +class TypeReferenceTracker; struct StatCollection { struct Stat { @@ -49,6 +49,8 @@ struct StatCollection { } }; + using KindAndStat = std::pair<uint32_t, Stat>; + void update(uint32_t Kind, uint32_t RecordSize) { Totals.update(RecordSize); auto Iter = Individual.try_emplace(Kind, 1, RecordSize); @@ -57,12 +59,15 @@ struct StatCollection { } Stat Totals; DenseMap<uint32_t, Stat> Individual; + + std::vector<KindAndStat> getStatsSortedBySize() const; }; class DumpOutputStyle : public OutputStyle { public: DumpOutputStyle(InputFile &File); + ~DumpOutputStyle() override; Error dump() override; @@ -77,6 +82,7 @@ private: Error dumpStreamSummary(); Error dumpSymbolStats(); Error dumpUdtStats(); + Error dumpTypeStats(); Error dumpNamedStreams(); Error dumpStringTable(); Error dumpStringTableFromPdb(); @@ -90,6 +96,7 @@ private: Error dumpNewFpo(PDBFile &File); Error dumpTpiStream(uint32_t StreamIdx); Error dumpTypesFromObjectFile(); + Error dumpTypeRefStats(); Error dumpModules(); Error dumpModuleFiles(); Error dumpModuleSymsForPdb(); @@ -105,6 +112,7 @@ private: void dumpSectionHeaders(StringRef Label, DbgHeaderType Type); InputFile &File; + std::unique_ptr<TypeReferenceTracker> RefTracker; LinePrinter P; SmallVector<StreamInfo, 32> StreamPurposes; }; diff --git a/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp b/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp index d16bfa480e1d..94faa0463981 100644 --- a/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp @@ -1,9 +1,8 @@ //===- ExplainOutputStyle.cpp --------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h index 9a497accb812..f405cf615e92 100644 --- a/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h +++ b/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h @@ -1,9 +1,8 @@ //===- ExplainOutputStyle.h ----------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/FormatUtil.cpp b/contrib/llvm/tools/llvm-pdbutil/FormatUtil.cpp index f55d478127d6..1a13f383e53c 100644 --- a/contrib/llvm/tools/llvm-pdbutil/FormatUtil.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/FormatUtil.cpp @@ -1,9 +1,8 @@ //===- FormatUtil.cpp ----------------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/FormatUtil.h b/contrib/llvm/tools/llvm-pdbutil/FormatUtil.h index 9a003c9285c9..19ce248f9a6f 100644 --- a/contrib/llvm/tools/llvm-pdbutil/FormatUtil.h +++ b/contrib/llvm/tools/llvm-pdbutil/FormatUtil.h @@ -1,9 +1,8 @@ //===- FormatUtil.h ------------------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/InputFile.cpp b/contrib/llvm/tools/llvm-pdbutil/InputFile.cpp index 8eb116cf0d80..bd23bfdbe31a 100644 --- a/contrib/llvm/tools/llvm-pdbutil/InputFile.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/InputFile.cpp @@ -1,9 +1,8 @@ //===- InputFile.cpp ------------------------------------------ *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -67,17 +66,20 @@ getModuleDebugStream(PDBFile &File, StringRef &ModuleName, uint32_t Index) { static inline bool isCodeViewDebugSubsection(object::SectionRef Section, StringRef Name, BinaryStreamReader &Reader) { - StringRef SectionName, Contents; + StringRef SectionName; if (Section.getName(SectionName)) return false; if (SectionName != Name) return false; - if (Section.getContents(Contents)) + Expected<StringRef> ContentsOrErr = Section.getContents(); + if (!ContentsOrErr) { + consumeError(ContentsOrErr.takeError()); return false; + } - Reader = BinaryStreamReader(Contents, support::little); + Reader = BinaryStreamReader(*ContentsOrErr, support::little); uint32_t Magic; if (Reader.bytesRemaining() < sizeof(uint32_t)) return false; diff --git a/contrib/llvm/tools/llvm-pdbutil/InputFile.h b/contrib/llvm/tools/llvm-pdbutil/InputFile.h index ee4e651c1e99..f25390c971d0 100644 --- a/contrib/llvm/tools/llvm-pdbutil/InputFile.h +++ b/contrib/llvm/tools/llvm-pdbutil/InputFile.h @@ -1,9 +1,8 @@ //===- InputFile.h -------------------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/LinePrinter.cpp b/contrib/llvm/tools/llvm-pdbutil/LinePrinter.cpp index e80a1762450b..280c000bd65f 100644 --- a/contrib/llvm/tools/llvm-pdbutil/LinePrinter.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/LinePrinter.cpp @@ -1,9 +1,8 @@ //===- LinePrinter.cpp ------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -187,8 +186,7 @@ void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File, return; } - auto S = MappedBlockStream::createIndexedStream( - File.getMsfLayout(), File.getMsfBuffer(), StreamIdx, File.getAllocator()); + auto S = File.createIndexedStream(StreamIdx); if (!S) { NewLine(); formatLine("Stream {0}: Not present", StreamIdx); diff --git a/contrib/llvm/tools/llvm-pdbutil/LinePrinter.h b/contrib/llvm/tools/llvm-pdbutil/LinePrinter.h index 09bde28f516a..7ecfae17354f 100644 --- a/contrib/llvm/tools/llvm-pdbutil/LinePrinter.h +++ b/contrib/llvm/tools/llvm-pdbutil/LinePrinter.h @@ -1,9 +1,8 @@ //===- LinePrinter.h ------------------------------------------ *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -133,8 +132,7 @@ struct AutoIndent { template <class T> inline raw_ostream &operator<<(LinePrinter &Printer, const T &Item) { - Printer.getStream() << Item; - return Printer.getStream(); + return Printer.getStream() << Item; } enum class PDB_ColorItem { diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp index 2c7b213b0a9f..e5ae47050678 100644 --- a/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -1,9 +1,8 @@ //===- MinimalSymbolDumper.cpp -------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -207,6 +206,7 @@ static std::string formatSourceLanguage(SourceLanguage Lang) { RETURN_CASE(SourceLanguage, MSIL, "msil"); RETURN_CASE(SourceLanguage, HLSL, "hlsl"); RETURN_CASE(SourceLanguage, D, "d"); + RETURN_CASE(SourceLanguage, Swift, "swift"); } return formatUnknownEnum(Lang); } @@ -287,21 +287,39 @@ static std::string formatCookieKind(FrameCookieKind Kind) { return formatUnknownEnum(Kind); } -static std::string formatRegisterId(RegisterId Id) { - switch (Id) { +static std::string formatRegisterId(RegisterId Id, CPUType Cpu) { + if (Cpu == CPUType::ARM64) { + switch (Id) { +#define CV_REGISTERS_ARM64 +#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name) +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_ARM64 + + default: + break; + } + } else { + switch (Id) { +#define CV_REGISTERS_X86 #define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name) #include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" #undef CV_REGISTER +#undef CV_REGISTERS_X86 + + default: + break; + } } return formatUnknownEnum(Id); } -static std::string formatRegisterId(uint16_t Reg16) { - return formatRegisterId(RegisterId(Reg16)); +static std::string formatRegisterId(uint16_t Reg16, CPUType Cpu) { + return formatRegisterId(RegisterId(Reg16), Cpu); } -static std::string formatRegisterId(ulittle16_t &Reg16) { - return formatRegisterId(uint16_t(Reg16)); +static std::string formatRegisterId(ulittle16_t &Reg16, CPUType Cpu) { + return formatRegisterId(uint16_t(Reg16), Cpu); } static std::string formatRange(LocalVariableAddrRange Range) { @@ -331,7 +349,7 @@ Error MinimalSymbolDumper::visitSymbolBegin(codeview::CVSymbol &Record, // append to the existing line. P.formatLine("{0} | {1} [size = {2}]", fmt_align(Offset, AlignStyle::Right, 6), - formatSymbolKind(Record.Type), Record.length()); + formatSymbolKind(Record.kind()), Record.length()); P.Indent(); return Error::success(); } @@ -562,7 +580,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, AutoIndent Indent(P, 7); P.formatLine("register = {0}, offset = {1}, offset in parent = {2}, has " "spilled udt = {3}", - formatRegisterId(Def.Hdr.Register), + formatRegisterId(Def.Hdr.Register, CompilationCPU), int32_t(Def.Hdr.BasePointerOffset), Def.offsetInParent(), Def.hasSpilledUDTMember()); P.formatLine("range = {0}, gaps = {1}", formatRange(Def.Range), @@ -575,7 +593,7 @@ Error MinimalSymbolDumper::visitKnownRecord( AutoIndent Indent(P, 7); P.formatLine("register = {0}, may have no name = {1}, range start = " "{2}, length = {3}", - formatRegisterId(DefRangeRegister.Hdr.Register), + formatRegisterId(DefRangeRegister.Hdr.Register, CompilationCPU), bool(DefRangeRegister.Hdr.MayHaveNoName), formatSegmentOffset(DefRangeRegister.Range.ISectStart, DefRangeRegister.Range.OffsetStart), @@ -590,7 +608,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, AutoIndent Indent(P, 7); bool NoName = !!(Def.Hdr.MayHaveNoName == 0); P.formatLine("register = {0}, may have no name = {1}, offset in parent = {2}", - formatRegisterId(Def.Hdr.Register), NoName, + formatRegisterId(Def.Hdr.Register, CompilationCPU), NoName, uint32_t(Def.Hdr.OffsetInParent)); P.formatLine("range = {0}, gaps = {1}", formatRange(Def.Range), formatGaps(P.getIndentLevel() + 9, Def.Gaps)); @@ -617,7 +635,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DefRangeSym &Def) { Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameCookieSym &FC) { AutoIndent Indent(P, 7); P.formatLine("code offset = {0}, Register = {1}, kind = {2}, flags = {3}", - FC.CodeOffset, formatRegisterId(FC.Register), + FC.CodeOffset, formatRegisterId(FC.Register, CompilationCPU), formatCookieKind(FC.CookieKind), FC.Flags); return Error::success(); } @@ -631,9 +649,10 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameProcSym &FP) { FP.BytesOfCalleeSavedRegisters, formatSegmentOffset(FP.SectionIdOfExceptionHandler, FP.OffsetOfExceptionHandler)); - P.formatLine("local fp reg = {0}, param fp reg = {1}", - formatRegisterId(FP.getLocalFramePtrReg(CompilationCPU)), - formatRegisterId(FP.getParamFramePtrReg(CompilationCPU))); + P.formatLine( + "local fp reg = {0}, param fp reg = {1}", + formatRegisterId(FP.getLocalFramePtrReg(CompilationCPU), CompilationCPU), + formatRegisterId(FP.getParamFramePtrReg(CompilationCPU), CompilationCPU)); P.formatLine("flags = {0}", formatFrameProcedureOptions(P.getIndentLevel() + 9, FP.Flags)); return Error::success(); @@ -650,13 +669,89 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, InlineSiteSym &IS) { AutoIndent Indent(P, 7); - auto Bytes = makeArrayRef(IS.AnnotationData); - StringRef Annotations(reinterpret_cast<const char *>(Bytes.begin()), - Bytes.size()); - P.formatLine("inlinee = {0}, parent = {1}, end = {2}", idIndex(IS.Inlinee), IS.Parent, IS.End); - P.formatLine("annotations = {0}", toHex(Annotations)); + + // Break down the annotation byte code and calculate code and line offsets. + // FIXME: It would be helpful if we could look up the initial file and inlinee + // lines offset using the inlinee index above. + uint32_t CodeOffset = 0; + int32_t LineOffset = 0; + for (auto &Annot : IS.annotations()) { + P.formatLine(" {0}", fmt_align(toHex(Annot.Bytes), AlignStyle::Left, 9)); + + auto formatCodeOffset = [&](uint32_t Delta) { + CodeOffset += Delta; + P.format(" code 0x{0} (+0x{1})", utohexstr(CodeOffset), utohexstr(Delta)); + }; + auto formatCodeLength = [&](uint32_t Length) { + // Notably, changing the code length does not affect the code offset. + P.format(" code end 0x{0} (+0x{1})", utohexstr(CodeOffset + Length), + utohexstr(Length)); + }; + auto formatLineOffset = [&](int32_t Delta) { + LineOffset += Delta; + char Sign = Delta > 0 ? '+' : '-'; + P.format(" line {0} ({1}{2})", LineOffset, Sign, std::abs(Delta)); + }; + + // Use the opcode to interpret the integer values. + switch (Annot.OpCode) { + case BinaryAnnotationsOpCode::Invalid: + break; + case BinaryAnnotationsOpCode::CodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffset: + formatCodeOffset(Annot.U1); + break; + case BinaryAnnotationsOpCode::ChangeLineOffset: + formatLineOffset(Annot.S1); + break; + case BinaryAnnotationsOpCode::ChangeCodeLength: + formatCodeLength(Annot.U1); + // Apparently this annotation updates the code offset. It's hard to make + // MSVC produce this opcode, but clang uses it, and debuggers seem to use + // this interpretation. + CodeOffset += Annot.U1; + break; + case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset: + formatCodeOffset(Annot.U1); + formatLineOffset(Annot.S1); + break; + case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset: + formatCodeOffset(Annot.U2); + formatCodeLength(Annot.U1); + break; + + case BinaryAnnotationsOpCode::ChangeFile: { + uint32_t FileOffset = Annot.U1; + StringRef Filename = "<unknown>"; + if (SymGroup) { + if (Expected<StringRef> MaybeFile = + SymGroup->getNameFromStringTable(FileOffset)) + Filename = *MaybeFile; + else + return MaybeFile.takeError(); + } + P.format(" setfile {0} 0x{1}", utohexstr(FileOffset)); + break; + } + + // The rest of these are hard to convince MSVC to emit, so they are not as + // well understood. + case BinaryAnnotationsOpCode::ChangeCodeOffsetBase: + formatCodeOffset(Annot.U1); + break; + case BinaryAnnotationsOpCode::ChangeLineEndDelta: + case BinaryAnnotationsOpCode::ChangeRangeKind: + case BinaryAnnotationsOpCode::ChangeColumnStart: + case BinaryAnnotationsOpCode::ChangeColumnEnd: + P.format(" {0} {1}", Annot.Name, Annot.U1); + break; + case BinaryAnnotationsOpCode::ChangeColumnEndDelta: + P.format(" {0} {1}", Annot.Name, Annot.S1); + break; + } + } return Error::success(); } @@ -665,7 +760,8 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, P.format(" `{0}`", Register.Name); AutoIndent Indent(P, 7); P.formatLine("register = {0}, type = {1}", - formatRegisterId(Register.Register), typeIndex(Register.Index)); + formatRegisterId(Register.Register, CompilationCPU), + typeIndex(Register.Index)); return Error::success(); } @@ -753,9 +849,9 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, RegRelativeSym &RegRel) { P.format(" `{0}`", RegRel.Name); AutoIndent Indent(P, 7); - P.formatLine("type = {0}, register = {1}, offset = {2}", - typeIndex(RegRel.Type), formatRegisterId(RegRel.Register), - RegRel.Offset); + P.formatLine( + "type = {0}, register = {1}, offset = {2}", typeIndex(RegRel.Type), + formatRegisterId(RegRel.Register, CompilationCPU), RegRel.Offset); return Error::success(); } @@ -780,3 +876,12 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, P.format(" `{0}`", UN.Name); return Error::success(); } + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + AnnotationSym &Annot) { + AutoIndent Indent(P, 7); + P.formatLine("addr = {0}", formatSegmentOffset(Annot.Segment, Annot.CodeOffset)); + P.formatLine("strings = {0}", typesetStringList(P.getIndentLevel() + 9 + 2, + Annot.Strings)); + return Error::success(); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h index 033e193cee6c..cdc75c1cfba0 100644 --- a/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h @@ -1,9 +1,8 @@ //===- MinimalSymbolDumper.h ---------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp index 3f10e8ab8a1e..3fdef085f19e 100644 --- a/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -1,9 +1,8 @@ //===- MinimalTypeDumper.cpp ---------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -11,6 +10,7 @@ #include "FormatUtil.h" #include "LinePrinter.h" +#include "TypeReferenceTracker.h" #include "llvm-pdbutil.h" #include "llvm/DebugInfo/CodeView/CVRecord.h" @@ -222,11 +222,10 @@ Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { // formatLine puts the newline at the beginning, so we use formatLine here // to start a new line, and then individual visit methods use format to // append to the existing line. - if (!Hashes) { - P.formatLine("{0} | {1} [size = {2}]", - fmt_align(Index, AlignStyle::Right, Width), - formatTypeLeafKind(Record.Type), Record.length()); - } else { + P.formatLine("{0} | {1} [size = {2}", + fmt_align(Index, AlignStyle::Right, Width), + formatTypeLeafKind(Record.kind()), Record.length()); + if (Hashes) { std::string H; if (Index.toArrayIndex() >= HashValues.size()) { H = "(not present)"; @@ -242,13 +241,19 @@ Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { else H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash); } - P.formatLine("{0} | {1} [size = {2}, hash = {3}]", - fmt_align(Index, AlignStyle::Right, Width), - formatTypeLeafKind(Record.Type), Record.length(), H); + P.format(", hash = {0}", H); } + if (RefTracker) { + if (RefTracker->isTypeReferenced(Index)) + P.format(", referenced"); + else + P.format(", unreferenced"); + } + P.format("]"); P.Indent(Width + 3); return Error::success(); } + Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) { P.Unindent(Width + 3); if (RecordBytes) { diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h b/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h index 8f6bdc6110ae..6bc456d47ac4 100644 --- a/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h @@ -1,9 +1,8 @@ //===- MinimalTypeDumper.h ------------------------------------ *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -21,17 +20,19 @@ class LazyRandomTypeCollection; namespace pdb { class LinePrinter; class TpiStream; +class TypeReferenceTracker; class MinimalTypeDumpVisitor : public codeview::TypeVisitorCallbacks { public: MinimalTypeDumpVisitor(LinePrinter &P, uint32_t Width, bool RecordBytes, bool Hashes, codeview::LazyRandomTypeCollection &Types, + TypeReferenceTracker *RefTracker, uint32_t NumHashBuckets, FixedStreamArray<support::ulittle32_t> HashValues, pdb::TpiStream *Stream) : P(P), Width(Width), RecordBytes(RecordBytes), Hashes(Hashes), - Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues), - Stream(Stream) {} + Types(Types), RefTracker(RefTracker), NumHashBuckets(NumHashBuckets), + HashValues(HashValues), Stream(Stream) {} Error visitTypeBegin(codeview::CVType &Record, codeview::TypeIndex Index) override; @@ -57,6 +58,7 @@ private: bool RecordBytes = false; bool Hashes = false; codeview::LazyRandomTypeCollection &Types; + pdb::TypeReferenceTracker *RefTracker = nullptr; uint32_t NumHashBuckets; codeview::TypeIndex CurrentTypeIndex; FixedStreamArray<support::ulittle32_t> HashValues; diff --git a/contrib/llvm/tools/llvm-pdbutil/OutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/OutputStyle.h index dfefc25a215e..40b0de8bdf72 100644 --- a/contrib/llvm/tools/llvm-pdbutil/OutputStyle.h +++ b/contrib/llvm/tools/llvm-pdbutil/OutputStyle.h @@ -1,9 +1,8 @@ //===- OutputStyle.h ------------------------------------------ *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PdbYaml.cpp b/contrib/llvm/tools/llvm-pdbutil/PdbYaml.cpp index 3ea333608314..a26241967b5a 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PdbYaml.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PdbYaml.cpp @@ -1,9 +1,8 @@ -//===- PdbYAML.cpp -------------------------------------------- *- C++ --*-===// +//===-- PdbYaml.cpp ------------------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -51,6 +50,7 @@ template <> struct ScalarEnumerationTraits<llvm::pdb::PDB_Machine> { io.enumCase(Value, "SH3DSP", PDB_Machine::SH3DSP); io.enumCase(Value, "Thumb", PDB_Machine::Thumb); io.enumCase(Value, "WceMipsV2", PDB_Machine::WceMipsV2); + io.enumCase(Value, "Arm64", PDB_Machine::Arm64); } }; diff --git a/contrib/llvm/tools/llvm-pdbutil/PdbYaml.h b/contrib/llvm/tools/llvm-pdbutil/PdbYaml.h index 97ba87266cc6..ed6346c2c4db 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PdbYaml.h +++ b/contrib/llvm/tools/llvm-pdbutil/PdbYaml.h @@ -1,9 +1,8 @@ //===- PdbYAML.h ---------------------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp index bcdecca81aec..cd01a4004819 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp @@ -1,9 +1,8 @@ //===- PrettyBuiltinDumper.cpp ---------------------------------- *- C++ *-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.h index fb6b0b172e6e..3bdef34c48f8 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.h @@ -1,9 +1,8 @@ //===- PrettyBuiltinDumper.h ---------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp index f009f53a3932..b7eccac5988c 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp @@ -1,9 +1,8 @@ //===- PrettyClassDefinitionDumper.cpp --------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h index 6569a1d304f6..f43c5c11bdfd 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h @@ -1,9 +1,8 @@ //===- PrettyClassDefinitionDumper.h ----------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp index a572522c8cd7..a522935e34f1 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp @@ -1,9 +1,8 @@ //===- PrettyClassLayoutGraphicalDumper.h -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h index f83f1a6c1b34..8f78b3b503d0 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h @@ -1,9 +1,8 @@ //===- PrettyClassLayoutGraphicalDumper.h -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp index 94a0b2d5e780..cf769ff66472 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp @@ -1,9 +1,8 @@ //===- PrettyCompilandDumper.cpp - llvm-pdbutil compiland dumper -*- C++ *-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.h index 1a840e49607c..c83a58672d1a 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.h @@ -1,9 +1,8 @@ //===- PrettyCompilandDumper.h - llvm-pdbutil compiland dumper -*- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.cpp index f4cbd3f8fa14..9ed5893f252e 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.cpp @@ -1,9 +1,8 @@ //===- PrettyEnumDumper.cpp -------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.h index c6e65a6d1772..e7c5c1aeb018 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.h @@ -1,9 +1,8 @@ //===- PrettyEnumDumper.h ---------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp index 1270223b1c78..fede031ec0c0 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp @@ -1,9 +1,8 @@ //===- PrettyExternalSymbolDumper.cpp -------------------------- *- C++ *-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h index 6a009862ddd4..58fafe943315 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h @@ -1,9 +1,8 @@ //===- PrettyExternalSymbolDumper.h --------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp index 836ede41054e..b820ca333965 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp @@ -1,9 +1,8 @@ //===- PrettyFunctionDumper.cpp --------------------------------- *- C++ *-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -139,7 +138,8 @@ void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) { if (Symbol.hasFramePointer()) { WithColor(Printer, PDB_ColorItem::Register).get() - << Symbol.getLocalBasePointerRegisterId(); + << CPURegister{Symbol.getRawSymbol().getPlatform(), + Symbol.getLocalBasePointerRegisterId()}; } else { WithColor(Printer, PDB_ColorItem::Register).get() << "FPO"; } @@ -229,9 +229,9 @@ void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) { uint32_t TypeId = Symbol.getTypeId(); auto Type = Symbol.getSession().getSymbolById(TypeId); if (Type) - Printer << "<unknown-type>"; - else Type->dump(*this); + else + Printer << "<unknown-type>"; } void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) { diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.h index 1a6f5430ec5a..df62604ac881 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.h @@ -1,9 +1,8 @@ //===- PrettyFunctionDumper.h --------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp index daf3cd45b327..e8f8e5aa62c9 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp @@ -1,9 +1,8 @@ //===- PrettyTypeDumper.cpp - PDBSymDumper type dumper *------------ C++ *-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.h index 36e586fea7e3..b6539d95bf31 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.h @@ -1,9 +1,8 @@ //===- PrettyTypeDumper.h - PDBSymDumper implementation for types *- C++ *-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp index 2b3f3691ed98..ef73a8cdf9c4 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp @@ -1,9 +1,8 @@ //===- PrettyTypedefDumper.cpp - PDBSymDumper impl for typedefs -- * C++ *-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.h index 133bbfb7db0e..ad8b3f37dcfd 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.h @@ -1,9 +1,8 @@ //===- PrettyTypedefDumper.h - llvm-pdbutil typedef dumper ---*- C++ ----*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp index ddac8cf0da4a..6dd7cc384cc9 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp @@ -1,9 +1,8 @@ //===- PrettyVariableDumper.cpp ---------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.h index cacf1ce9577b..65cf5cd2cf55 100644 --- a/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.h +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.h @@ -1,9 +1,8 @@ //===- PrettyVariableDumper.h - PDBSymDumper variable dumper ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp index 367d947d25ee..7dfc2beefe78 100644 --- a/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp @@ -1,9 +1,8 @@ //===- StreamUtil.cpp - PDB stream utilities --------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h index 0e2e80707361..f810f7dc15b4 100644 --- a/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h +++ b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h @@ -1,9 +1,8 @@ //===- Streamutil.h - PDB stream utilities ----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp b/contrib/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp new file mode 100644 index 000000000000..f184f02e01ee --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp @@ -0,0 +1,160 @@ +//===- TypeReferenceTracker.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 +// +//===----------------------------------------------------------------------===// + +#include "TypeReferenceTracker.h" + +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::codeview; + +// LazyRandomTypeCollection doesn't appear to expose the number of records, so +// just iterate up front to find out. +static uint32_t getNumRecordsInCollection(LazyRandomTypeCollection &Types) { + uint32_t NumTypes = 0; + for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) + ++NumTypes; + return NumTypes; +} + +TypeReferenceTracker::TypeReferenceTracker(InputFile &File) + : File(File), Types(File.types()), + Ids(File.isPdb() ? &File.ids() : nullptr) { + NumTypeRecords = getNumRecordsInCollection(Types); + TypeReferenced.resize(NumTypeRecords, false); + + // If this is a PDB, ids are stored separately, so make a separate bit vector. + if (Ids) { + NumIdRecords = getNumRecordsInCollection(*Ids); + IdReferenced.resize(NumIdRecords, false); + } + + // Get the TpiStream pointer for forward decl resolution if this is a pdb. + // Build the hash map to enable resolving forward decls. + if (File.isPdb()) { + Tpi = &cantFail(File.pdb().getPDBTpiStream()); + Tpi->buildHashMap(); + } +} + +void TypeReferenceTracker::mark() { + // Walk type roots: + // - globals + // - modi symbols + // - LF_UDT_MOD_SRC_LINE? VC always links these in. + for (SymbolGroup SG : File.symbol_groups()) { + if (File.isObj()) { + for (const auto &SS : SG.getDebugSubsections()) { + // FIXME: Are there other type-referencing subsections? Inlinees? + // Probably for IDs. + if (SS.kind() != DebugSubsectionKind::Symbols) + continue; + + CVSymbolArray Symbols; + BinaryStreamReader Reader(SS.getRecordData()); + cantFail(Reader.readArray(Symbols, Reader.getLength())); + for (const CVSymbol &S : Symbols) + addTypeRefsFromSymbol(S); + } + } else if (SG.hasDebugStream()) { + for (const CVSymbol &S : SG.getPdbModuleStream().getSymbolArray()) + addTypeRefsFromSymbol(S); + } + } + + // Walk globals and mark types referenced from globals. + if (File.isPdb() && File.pdb().hasPDBGlobalsStream()) { + SymbolStream &SymStream = cantFail(File.pdb().getPDBSymbolStream()); + GlobalsStream &GS = cantFail(File.pdb().getPDBGlobalsStream()); + for (uint32_t PubSymOff : GS.getGlobalsTable()) { + CVSymbol Sym = SymStream.readRecord(PubSymOff); + addTypeRefsFromSymbol(Sym); + } + } + + // FIXME: Should we walk Ids? +} + +void TypeReferenceTracker::addOneTypeRef(TiRefKind RefKind, TypeIndex RefTI) { + // If it's simple or already seen, no need to add to work list. + BitVector &TypeOrIdReferenced = + (Ids && RefKind == TiRefKind::IndexRef) ? IdReferenced : TypeReferenced; + if (RefTI.isSimple() || TypeOrIdReferenced.test(RefTI.toArrayIndex())) + return; + + // Otherwise, mark it seen and add it to the work list. + TypeOrIdReferenced.set(RefTI.toArrayIndex()); + RefWorklist.push_back({RefKind, RefTI}); +} + +void TypeReferenceTracker::addTypeRefsFromSymbol(const CVSymbol &Sym) { + SmallVector<TiReference, 4> DepList; + // FIXME: Check for failure. + discoverTypeIndicesInSymbol(Sym, DepList); + addReferencedTypes(Sym.content(), DepList); + markReferencedTypes(); +} + +void TypeReferenceTracker::addReferencedTypes(ArrayRef<uint8_t> RecData, + ArrayRef<TiReference> DepList) { + for (const auto &Ref : DepList) { + // FIXME: Report OOB slice instead of truncating. + ArrayRef<uint8_t> ByteSlice = + RecData.drop_front(Ref.Offset).take_front(4 * Ref.Count); + ArrayRef<TypeIndex> TIs( + reinterpret_cast<const TypeIndex *>(ByteSlice.data()), + ByteSlice.size() / 4); + + // If this is a PDB and this is an item reference, track it in the IPI + // bitvector. Otherwise, it's a type ref, or there is only one stream. + for (TypeIndex RefTI : TIs) + addOneTypeRef(Ref.Kind, RefTI); + } +} + +void TypeReferenceTracker::markReferencedTypes() { + while (!RefWorklist.empty()) { + TiRefKind RefKind; + TypeIndex RefTI; + std::tie(RefKind, RefTI) = RefWorklist.pop_back_val(); + Optional<CVType> Rec = (Ids && RefKind == TiRefKind::IndexRef) + ? Ids->tryGetType(RefTI) + : Types.tryGetType(RefTI); + if (!Rec) + continue; // FIXME: Report a reference to a non-existant type. + + SmallVector<TiReference, 4> DepList; + // FIXME: Check for failure. + discoverTypeIndices(*Rec, DepList); + addReferencedTypes(Rec->content(), DepList); + + // If this is a tag kind and this is a PDB input, mark the complete type as + // referenced. + // FIXME: This limitation makes this feature somewhat useless on object file + // inputs. + if (Tpi) { + switch (Rec->kind()) { + default: + break; + case LF_CLASS: + case LF_INTERFACE: + case LF_STRUCTURE: + case LF_UNION: + case LF_ENUM: + addOneTypeRef(TiRefKind::TypeRef, + cantFail(Tpi->findFullDeclForForwardRef(RefTI))); + break; + } + } + } +} diff --git a/contrib/llvm/tools/llvm-pdbutil/TypeReferenceTracker.h b/contrib/llvm/tools/llvm-pdbutil/TypeReferenceTracker.h new file mode 100644 index 000000000000..8861731ab6ee --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/TypeReferenceTracker.h @@ -0,0 +1,69 @@ +//===- TypeReferenceTracker.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_LLVMPDBDUMP_TYPEREFERENCETRACKER_H +#define LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H + +#include "InputFile.h" + +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { + +class TpiStream; + +/// Maintains bitvector to track whether a type was referenced by a symbol +/// record. +class TypeReferenceTracker { +public: + TypeReferenceTracker(InputFile &File); + + // Do the work of marking referenced types. + void mark(); + + // Return true if a symbol record transitively references this type. + bool isTypeReferenced(codeview::TypeIndex TI) { + return TI.toArrayIndex() <= NumTypeRecords && + TypeReferenced.test(TI.toArrayIndex()); + } + +private: + void addTypeRefsFromSymbol(const codeview::CVSymbol &Sym); + + // Mark types on this list as referenced. + void addReferencedTypes(ArrayRef<uint8_t> RecData, + ArrayRef<codeview::TiReference> Refs); + + // Consume all types on the worklist. + void markReferencedTypes(); + + void addOneTypeRef(codeview::TiRefKind RefKind, codeview::TypeIndex RefTI); + + InputFile &File; + codeview::LazyRandomTypeCollection &Types; + codeview::LazyRandomTypeCollection *Ids = nullptr; + TpiStream *Tpi = nullptr; + BitVector TypeReferenced; + BitVector IdReferenced; + SmallVector<std::pair<codeview::TiRefKind, codeview::TypeIndex>, 10> + RefWorklist; + uint32_t NumTypeRecords = 0; + uint32_t NumIdRecords = 0; +}; + +} // namespace pdb +} // namespace llvm + +#endif // LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H diff --git a/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.cpp b/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.cpp index 62b5c428d410..80b76657facc 100644 --- a/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.cpp @@ -1,9 +1,8 @@ //===- YAMLOutputStyle.cpp ------------------------------------ *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -232,10 +231,7 @@ Error YAMLOutputStyle::dumpDbiStream() { if (ModiStream == kInvalidStreamIndex) continue; - auto ModStreamData = msf::MappedBlockStream::createIndexedStream( - File.getMsfLayout(), File.getMsfBuffer(), ModiStream, - File.getAllocator()); - + auto ModStreamData = File.createIndexedStream(ModiStream); pdb::ModuleDebugStreamRef ModS(MI, std::move(ModStreamData)); if (auto EC = ModS.reload()) return EC; diff --git a/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.h index a5ad3355d2ab..7a50af1abe3f 100644 --- a/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.h +++ b/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.h @@ -1,9 +1,8 @@ //===- YAMLOutputStyle.h -------------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp index 76f61a2a95a7..785a98086791 100644 --- a/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -1,9 +1,8 @@ //===- llvm-pdbutil.cpp - Dump debug info from a PDB file -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -463,7 +462,10 @@ cl::opt<bool> DumpSymbolStats( "sym-stats", cl::desc("Dump a detailed breakdown of symbol usage/size for each module"), cl::cat(MsfOptions), cl::sub(DumpSubcommand)); - +cl::opt<bool> DumpTypeStats( + "type-stats", + cl::desc("Dump a detailed breakdown of type usage/size"), + cl::cat(MsfOptions), cl::sub(DumpSubcommand)); cl::opt<bool> DumpUdtStats( "udt-stats", cl::desc("Dump a detailed breakdown of S_UDT record usage / stats"), @@ -477,6 +479,11 @@ cl::opt<bool> DumpTypeData( "type-data", cl::desc("dump CodeView type record raw bytes from TPI stream"), cl::cat(TypeOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> + DumpTypeRefStats("type-ref-stats", + cl::desc("dump statistics on the number and size of types " + "transitively referenced by symbol records"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); cl::opt<bool> DumpTypeExtras("type-extras", cl::desc("dump type hashes and index offsets"), @@ -927,7 +934,7 @@ static std::string stringOr(std::string Str, std::string IfEmpty) { static void dumpInjectedSources(LinePrinter &Printer, IPDBSession &Session) { auto Sources = Session.getInjectedSources(); - if (0 == Sources->getChildCount()) { + if (!Sources || !Sources->getChildCount()) { Printer.printLine("There are no injected sources."); return; } @@ -940,9 +947,6 @@ static void dumpInjectedSources(LinePrinter &Printer, IPDBSession &Session) { std::string VFName = stringOr(IS->getVirtualFileName(), "<null>"); uint32_t CRC = IS->getCrc32(); - std::string CompressionStr; - llvm::raw_string_ostream Stream(CompressionStr); - Stream << IS->getCompression(); WithColor(Printer, PDB_ColorItem::Path).get() << File; Printer << " ("; WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Size; @@ -961,7 +965,9 @@ static void dumpInjectedSources(LinePrinter &Printer, IPDBSession &Session) { Printer << ", "; WithColor(Printer, PDB_ColorItem::Keyword).get() << "compression"; Printer << "="; - WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Stream.str(); + dumpPDBSourceCompression( + WithColor(Printer, PDB_ColorItem::LiteralValue).get(), + IS->getCompression()); if (!opts::pretty::ShowInjectedSourceContent) continue; @@ -970,7 +976,12 @@ static void dumpInjectedSources(LinePrinter &Printer, IPDBSession &Session) { int Indent = Printer.getIndentLevel(); Printer.Unindent(Indent); - Printer.printLine(IS->getCode()); + if (IS->getCompression() == PDB_SourceCompression::None) + Printer.printLine(IS->getCode()); + else + Printer.formatBinary("Compressed data", + arrayRefFromStringRef(IS->getCode()), + /*StartOffset=*/0); // Re-indent back to the original level. Printer.Indent(Indent); @@ -1272,12 +1283,7 @@ static void dumpPretty(StringRef Path) { WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---INJECTED SOURCES---"; AutoIndent Indent1(Printer); - - if (ReaderType == PDB_ReaderType::Native) - Printer.printLine( - "Injected sources are not supported with the native reader."); - else - dumpInjectedSources(Printer, *Session); + dumpInjectedSources(Printer, *Session); } Printer.NewLine(); @@ -1377,8 +1383,7 @@ static void exportStream() { << "' (index " << Index << ") to file " << OutFileName << ".\n"; } - SourceStream = MappedBlockStream::createIndexedStream( - File.getMsfLayout(), File.getMsfBuffer(), Index, File.getAllocator()); + SourceStream = File.createIndexedStream(Index); auto OutFile = ExitOnErr( FileOutputBuffer::create(OutFileName, SourceStream->getLength())); FileBufferByteStream DestStream(std::move(OutFile), llvm::support::little); diff --git a/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h index a57cc51d7fd7..321f41bba7f1 100644 --- a/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h @@ -1,9 +1,8 @@ //===- llvm-pdbutil.h ----------------------------------------- *- C++ --*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -141,6 +140,7 @@ extern llvm::cl::opt<bool> DumpSummary; extern llvm::cl::opt<bool> DumpFpm; extern llvm::cl::opt<bool> DumpStreams; extern llvm::cl::opt<bool> DumpSymbolStats; +extern llvm::cl::opt<bool> DumpTypeStats; extern llvm::cl::opt<bool> DumpUdtStats; extern llvm::cl::opt<bool> DumpStreamBlocks; @@ -156,6 +156,7 @@ extern llvm::cl::opt<bool> DumpTypeData; extern llvm::cl::opt<bool> DumpTypeExtras; extern llvm::cl::list<uint32_t> DumpTypeIndex; extern llvm::cl::opt<bool> DumpTypeDependents; +extern llvm::cl::opt<bool> DumpTypeRefStats; extern llvm::cl::opt<bool> DumpSectionHeaders; extern llvm::cl::opt<bool> DumpIds; diff --git a/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp b/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp index c25cbc2b64df..16d3ebe3fcbc 100644 --- a/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -1,9 +1,8 @@ //===- llvm-profdata.cpp - LLVM profile data tool -------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -27,8 +26,8 @@ #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" -#include "llvm/Support/WithColor.h" #include "llvm/Support/ThreadPool.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -201,6 +200,32 @@ static bool isFatalError(instrprof_error IPE) { } } +/// Computer the overlap b/w profile BaseFilename and TestFileName, +/// and store the program level result to Overlap. +static void overlapInput(const std::string &BaseFilename, + const std::string &TestFilename, WriterContext *WC, + OverlapStats &Overlap, + const OverlapFuncFilters &FuncFilter, + raw_fd_ostream &OS, bool IsCS) { + auto ReaderOrErr = InstrProfReader::create(TestFilename); + if (Error E = ReaderOrErr.takeError()) { + // Skip the empty profiles by returning sliently. + instrprof_error IPE = InstrProfError::take(std::move(E)); + if (IPE != instrprof_error::empty_raw_profile) + WC->Err = make_error<InstrProfError>(IPE); + return; + } + + auto Reader = std::move(ReaderOrErr.get()); + for (auto &I : *Reader) { + OverlapStats FuncOverlap(OverlapStats::FunctionLevel); + FuncOverlap.setFuncInfo(I.Name, I.Hash); + + WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter); + FuncOverlap.dump(OS); + } +} + /// Load an input into a writer context. static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, WriterContext *WC) { @@ -226,7 +251,8 @@ static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, auto Reader = std::move(ReaderOrErr.get()); bool IsIRProfile = Reader->isIRLevelProfile(); - if (WC->Writer.setIsIRLevelProfile(IsIRProfile)) { + bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); + if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { WC->Err = make_error<StringError>( "Merge IR generated profile with Clang generated profile.", std::error_code()); @@ -291,11 +317,6 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, OutputFormat != PF_Text) exitWithError("Unknown format is specified."); - std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); - if (EC) - exitWithErrorCode(EC, OutputFilename); - std::mutex ErrorLock; SmallSet<instrprof_error, 4> WriterErrorCodes; @@ -358,6 +379,11 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, WC->ErrWhence); } + std::error_code EC; + raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); + if (EC) + exitWithErrorCode(EC, OutputFilename); + InstrProfWriter &Writer = Contexts[0]->Writer; if (OutputFormat == PF_Text) { if (Error E = Writer.writeText(Output)) @@ -407,12 +433,6 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs, StringRef OutputFilename, ProfileFormat OutputFormat) { using namespace sampleprof; - auto WriterOrErr = - SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); - if (std::error_code EC = WriterOrErr.getError()) - exitWithErrorCode(EC, OutputFilename); - - auto Writer = std::move(WriterOrErr.get()); StringMap<FunctionSamples> ProfileMap; SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; LLVMContext Context; @@ -447,6 +467,12 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs, } } } + auto WriterOrErr = + SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); + if (std::error_code EC = WriterOrErr.getError()) + exitWithErrorCode(EC, OutputFilename); + + auto Writer = std::move(WriterOrErr.get()); Writer->write(ProfileMap); } @@ -608,6 +634,65 @@ static int merge_main(int argc, const char *argv[]) { return 0; } +/// Computer the overlap b/w profile BaseFilename and profile TestFilename. +static void overlapInstrProfile(const std::string &BaseFilename, + const std::string &TestFilename, + const OverlapFuncFilters &FuncFilter, + raw_fd_ostream &OS, bool IsCS) { + std::mutex ErrorLock; + SmallSet<instrprof_error, 4> WriterErrorCodes; + WriterContext Context(false, ErrorLock, WriterErrorCodes); + WeightedFile WeightedInput{BaseFilename, 1}; + OverlapStats Overlap; + Error E = Overlap.accumuateCounts(BaseFilename, TestFilename, IsCS); + if (E) + 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); + } + if (Overlap.Test.CountSum < 1.0f) { + OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n"; + exit(0); + } + loadInput(WeightedInput, nullptr, &Context); + overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS, + IsCS); + Overlap.dump(OS); +} + +static int overlap_main(int argc, const char *argv[]) { + cl::opt<std::string> BaseFilename(cl::Positional, cl::Required, + cl::desc("<base profile file>")); + cl::opt<std::string> TestFilename(cl::Positional, cl::Required, + cl::desc("<test profile file>")); + 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<unsigned long long> ValueCutoff( + "value-cutoff", cl::init(-1), + cl::desc( + "Function level overlap information for every function 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::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); + + std::error_code EC; + raw_fd_ostream OS(Output.data(), EC, sys::fs::F_Text); + if (EC) + exitWithErrorCode(EC, Output); + + overlapInstrProfile(BaseFilename, TestFilename, + OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, + IsCS); + + return 0; +} + typedef struct ValueSitesStats { ValueSitesStats() : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), @@ -643,7 +728,7 @@ static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, for (uint32_t V = 0; V < NV; V++) { OS << "\t[ " << format("%2u", I) << ", "; if (Symtab == nullptr) - OS << format("%4u", VD[V].Value); + OS << format("%4" PRIu64, VD[V].Value); else OS << Symtab->getFuncName(VD[V].Value); OS << ", " << format("%10" PRId64, VD[V].Count) << " ] (" @@ -670,9 +755,10 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, uint32_t TopN, bool ShowIndirectCallTargets, bool ShowMemOPSizes, bool ShowDetailedSummary, std::vector<uint32_t> DetailedSummaryCutoffs, - bool ShowAllFunctions, uint64_t ValueCutoff, - bool OnlyListBelow, const std::string &ShowFunction, - bool TextFormat, raw_fd_ostream &OS) { + bool ShowAllFunctions, bool ShowCS, + uint64_t ValueCutoff, bool OnlyListBelow, + const std::string &ShowFunction, bool TextFormat, + raw_fd_ostream &OS) { auto ReaderOrErr = InstrProfReader::create(Filename); std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs); if (ShowDetailedSummary && Cutoffs.empty()) { @@ -709,6 +795,11 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, OS << ":ir\n"; for (const auto &Func : *Reader) { + if (Reader->isIRLevelProfile()) { + bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash); + if (FuncIsCS != ShowCS) + continue; + } bool Show = ShowAllFunctions || (!ShowFunction.empty() && Func.Name.find(ShowFunction) != Func.Name.npos); @@ -900,6 +991,8 @@ static int show_main(int argc, const char *argv[]) { cl::value_desc("800000,901000,999999")); cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false), cl::desc("Details for every function")); + cl::opt<bool> ShowCS("showcs", cl::init(false), + cl::desc("Show context sensitive counts")); cl::opt<std::string> ShowFunction("function", cl::desc("Details for matching functions")); @@ -927,6 +1020,12 @@ static int show_main(int argc, const char *argv[]) { if (OutputFilename.empty()) OutputFilename = "-"; + if (!Filename.compare(OutputFilename)) { + errs() << sys::path::filename(argv[0]) + << ": Input file name cannot be the same as the output file name!\n"; + return 1; + } + std::error_code EC; raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); if (EC) @@ -935,14 +1034,12 @@ static int show_main(int argc, const char *argv[]) { if (ShowAllFunctions && !ShowFunction.empty()) WithColor::warning() << "-function argument ignored: showing all functions\n"; - std::vector<uint32_t> Cutoffs(DetailedSummaryCutoffs.begin(), - DetailedSummaryCutoffs.end()); if (ProfileKind == instr) return showInstrProfile(Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, - ShowAllFunctions, ValueCutoff, OnlyListBelow, - ShowFunction, TextFormat, OS); + ShowAllFunctions, ShowCS, ValueCutoff, + OnlyListBelow, ShowFunction, TextFormat, OS); else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, ShowFunction, OS); @@ -959,6 +1056,8 @@ int main(int argc, const char *argv[]) { func = merge_main; else if (strcmp(argv[1], "show") == 0) func = show_main; + else if (strcmp(argv[1], "overlap") == 0) + func = overlap_main; if (func) { std::string Invocation(ProgName.str() + " " + argv[1]); @@ -973,7 +1072,7 @@ int main(int argc, const char *argv[]) { << "USAGE: " << ProgName << " <command> [args...]\n" << "USAGE: " << ProgName << " <command> -help\n\n" << "See each individual command --help for more details.\n" - << "Available commands: merge, show\n"; + << "Available commands: merge, show, overlap\n"; return 0; } } @@ -983,6 +1082,6 @@ int main(int argc, const char *argv[]) { else errs() << ProgName << ": Unknown command!\n"; - errs() << "USAGE: " << ProgName << " <merge|show> [args...]\n"; + errs() << "USAGE: " << ProgName << " <merge|show|overlap> [args...]\n"; return 1; } diff --git a/contrib/llvm/tools/llvm-readobj/ARMEHABIPrinter.h b/contrib/llvm/tools/llvm-readobj/ARMEHABIPrinter.h index 51128f113c4c..11f9d6166a59 100644 --- a/contrib/llvm/tools/llvm-readobj/ARMEHABIPrinter.h +++ b/contrib/llvm/tools/llvm-readobj/ARMEHABIPrinter.h @@ -1,9 +1,8 @@ //===--- ARMEHABIPrinter.h - ARM EHABI Unwind Information Printer ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -366,6 +365,8 @@ template <typename ET> ErrorOr<StringRef> PrinterContext<ET>::FunctionAtAddress(unsigned Section, uint64_t Address) const { + if (!Symtab) + return readobj_error::unknown_symbol; auto StrTableOrErr = ELF->getStringTableForSymtab(*Symtab); if (!StrTableOrErr) error(StrTableOrErr.takeError()); @@ -551,13 +552,15 @@ void PrinterContext<ET>::PrintIndexTable(unsigned SectionIndex, const Elf_Shdr *EHT = FindExceptionTable(SectionIndex, Entry * IndexTableEntrySize + 4); - if (auto Name = ELF->getSectionName(EHT)) - SW.printString("ExceptionHandlingTable", *Name); + if (EHT) + if (auto Name = ELF->getSectionName(EHT)) + SW.printString("ExceptionHandlingTable", *Name); uint64_t TableEntryOffset = PREL31(Word1, IT->sh_addr); SW.printHex("TableEntryOffset", TableEntryOffset); - PrintExceptionTable(IT, EHT, TableEntryOffset); + if (EHT) + PrintExceptionTable(IT, EHT, TableEntryOffset); } } } diff --git a/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp index 4b823b816c35..4de14e2e78d5 100644 --- a/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp +++ b/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp @@ -1,9 +1,8 @@ //===-- ARMWinEHPrinter.cpp - Windows on ARM EH Data Printer ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -1095,17 +1094,17 @@ void Decoder::dumpProcedureData(const COFFObjectFile &COFF, break; } -std::error_code Decoder::dumpProcedureData(const COFFObjectFile &COFF) { +Error Decoder::dumpProcedureData(const COFFObjectFile &COFF) { for (const auto &Section : COFF.sections()) { - StringRef SectionName; - if (std::error_code EC = - COFF.getSectionName(COFF.getCOFFSection(Section), SectionName)) - return EC; + Expected<StringRef> NameOrErr = + COFF.getSectionName(COFF.getCOFFSection(Section)); + if (!NameOrErr) + return NameOrErr.takeError(); - if (SectionName.startswith(".pdata")) + if (NameOrErr->startswith(".pdata")) dumpProcedureData(COFF, Section); } - return std::error_code(); + return Error::success(); } } } diff --git a/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.h b/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.h index e271a1e6fe77..5de7062cb1d7 100644 --- a/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.h +++ b/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.h @@ -1,9 +1,8 @@ //===--- ARMWinEHPrinter.h - Windows on ARM Unwind Information Printer ----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -157,7 +156,7 @@ public: Decoder(ScopedPrinter &SW, bool isAArch64) : SW(SW), OS(SW.getOStream()), isAArch64(isAArch64) {} - std::error_code dumpProcedureData(const object::COFFObjectFile &COFF); + Error dumpProcedureData(const object::COFFObjectFile &COFF); }; } } diff --git a/contrib/llvm/tools/llvm-readobj/COFFDumper.cpp b/contrib/llvm/tools/llvm-readobj/COFFDumper.cpp index 3e2626dad118..4c2e39dfa3cc 100644 --- a/contrib/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/contrib/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -1,9 +1,8 @@ //===-- COFFDumper.cpp - COFF-specific dumper -------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -44,13 +43,14 @@ #include "llvm/DebugInfo/CodeView/TypeTableCollection.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/WindowsResource.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/LEB128.h" +#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/Win64EH.h" #include "llvm/Support/raw_ostream.h" @@ -81,8 +81,6 @@ public: void printFileHeaders() override; void printSectionHeaders() override; void printRelocations() override; - void printSymbols() override; - void printDynamicSymbols() override; void printUnwindInfo() override; void printNeededLibraries() override; @@ -95,12 +93,16 @@ public: void printCOFFResources() override; void printCOFFLoadConfig() override; void printCodeViewDebugInfo() override; - void - mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs, - llvm::codeview::MergingTypeTableBuilder &CVTypes) override; + void mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs, + llvm::codeview::MergingTypeTableBuilder &CVTypes, + llvm::codeview::GlobalTypeTableBuilder &GlobalCVIDs, + llvm::codeview::GlobalTypeTableBuilder &GlobalCVTypes, + bool GHash) override; void printStackMap() const override; void printAddrsig() override; private: + void printSymbols() override; + void printDynamicSymbols() override; void printSymbol(const SymbolRef &Sym); void printRelocation(const SectionRef &Section, const RelocationRef &Reloc, uint64_t Bias = 0); @@ -568,29 +570,6 @@ static const EnumEntry<uint8_t> FileChecksumKindNames[] = { LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, SHA256), }; -static const EnumEntry<COFF::ResourceTypeID> ResourceTypeNames[]{ - {"kRT_CURSOR (ID 1)", COFF::RID_Cursor}, - {"kRT_BITMAP (ID 2)", COFF::RID_Bitmap}, - {"kRT_ICON (ID 3)", COFF::RID_Icon}, - {"kRT_MENU (ID 4)", COFF::RID_Menu}, - {"kRT_DIALOG (ID 5)", COFF::RID_Dialog}, - {"kRT_STRING (ID 6)", COFF::RID_String}, - {"kRT_FONTDIR (ID 7)", COFF::RID_FontDir}, - {"kRT_FONT (ID 8)", COFF::RID_Font}, - {"kRT_ACCELERATOR (ID 9)", COFF::RID_Accelerator}, - {"kRT_RCDATA (ID 10)", COFF::RID_RCData}, - {"kRT_MESSAGETABLE (ID 11)", COFF::RID_MessageTable}, - {"kRT_GROUP_CURSOR (ID 12)", COFF::RID_Group_Cursor}, - {"kRT_GROUP_ICON (ID 14)", COFF::RID_Group_Icon}, - {"kRT_VERSION (ID 16)", COFF::RID_Version}, - {"kRT_DLGINCLUDE (ID 17)", COFF::RID_DLGInclude}, - {"kRT_PLUGPLAY (ID 19)", COFF::RID_PlugPlay}, - {"kRT_VXD (ID 20)", COFF::RID_VXD}, - {"kRT_ANICURSOR (ID 21)", COFF::RID_AniCursor}, - {"kRT_ANIICON (ID 22)", COFF::RID_AniIcon}, - {"kRT_HTML (ID 23)", COFF::RID_HTML}, - {"kRT_MANIFEST (ID 24)", COFF::RID_Manifest}}; - template <typename T> static std::error_code getSymbolAuxData(const COFFObjectFile *Obj, COFFSymbolRef Symbol, @@ -613,11 +592,14 @@ void COFFDumper::cacheRelocations() { RelocMap[Section].push_back(Reloc); // Sort relocations by address. - llvm::sort(RelocMap[Section], relocAddressLess); + llvm::sort(RelocMap[Section], [](RelocationRef L, RelocationRef R) { + return L.getOffset() < R.getOffset(); + }); } } -void COFFDumper::printDataDirectory(uint32_t Index, const std::string &FieldName) { +void COFFDumper::printDataDirectory(uint32_t Index, + const std::string &FieldName) { const data_directory *Data; if (Obj->getDataDirectory(Index, Data)) return; @@ -951,8 +933,7 @@ void COFFDumper::initializeFileAndStringTables(BinaryStreamReader &Reader) { void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, const SectionRef &Section) { - StringRef SectionContents; - error(Section.getContents(SectionContents)); + StringRef SectionContents = unwrapOrError(Section.getContents()); StringRef Data = SectionContents; SmallVector<StringRef, 10> FunctionNames; @@ -980,6 +961,11 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, error(consume(Data, SubSectionSize)); ListScope S(W, "Subsection"); + // Dump the subsection as normal even if the ignore bit is set. + if (SubType & SubsectionIgnoreFlag) { + W.printHex("IgnoredSubsectionKind", SubType); + SubType &= ~SubsectionIgnoreFlag; + } W.printEnum("SubSectionType", SubType, makeArrayRef(SubSectionTypes)); W.printHex("SubSectionSize", SubSectionSize); @@ -1228,13 +1214,15 @@ void COFFDumper::printFileNameForOffset(StringRef Label, uint32_t FileOffset) { } void COFFDumper::mergeCodeViewTypes(MergingTypeTableBuilder &CVIDs, - MergingTypeTableBuilder &CVTypes) { + MergingTypeTableBuilder &CVTypes, + GlobalTypeTableBuilder &GlobalCVIDs, + GlobalTypeTableBuilder &GlobalCVTypes, + bool GHash) { for (const SectionRef &S : Obj->sections()) { StringRef SectionName; error(S.getName(SectionName)); if (SectionName == ".debug$T") { - StringRef Data; - error(S.getContents(Data)); + StringRef Data = unwrapOrError(S.getContents()); uint32_t Magic; error(consume(Data, Magic)); if (Magic != 4) @@ -1249,9 +1237,18 @@ void COFFDumper::mergeCodeViewTypes(MergingTypeTableBuilder &CVIDs, } SmallVector<TypeIndex, 128> SourceToDest; Optional<uint32_t> PCHSignature; - if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types, - PCHSignature)) - return error(std::move(EC)); + if (GHash) { + std::vector<GloballyHashedType> Hashes = + GloballyHashedType::hashTypes(Types); + if (auto EC = + mergeTypeAndIdRecords(GlobalCVIDs, GlobalCVTypes, SourceToDest, + Types, Hashes, PCHSignature)) + return error(std::move(EC)); + } else { + if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types, + PCHSignature)) + return error(std::move(EC)); + } } } } @@ -1261,8 +1258,7 @@ void COFFDumper::printCodeViewTypeSection(StringRef SectionName, ListScope D(W, "CodeViewTypes"); W.printNumber("Section", SectionName, Obj->getSectionID(Section)); - StringRef Data; - error(Section.getContents(Data)); + StringRef Data = unwrapOrError(Section.getContents()); if (opts::CodeViewSubsectionBytes) W.printBinaryBlock("Data", Data); @@ -1322,9 +1318,7 @@ void COFFDumper::printSectionHeaders() { if (opts::SectionData && !(Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)) { - StringRef Data; - error(Sec.getContents(Data)); - + StringRef Data = unwrapOrError(Sec.getContents()); W.printBinaryBlock("SectionData", Data); } } @@ -1398,15 +1392,11 @@ void COFFDumper::printSymbols() { void COFFDumper::printDynamicSymbols() { ListScope Group(W, "DynamicSymbols"); } -static ErrorOr<StringRef> +static Expected<StringRef> getSectionName(const llvm::object::COFFObjectFile *Obj, int32_t SectionNumber, const coff_section *Section) { - if (Section) { - StringRef SectionName; - if (std::error_code EC = Obj->getSectionName(Section, SectionName)) - return EC; - return SectionName; - } + if (Section) + return Obj->getSectionName(Section); if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) return StringRef("IMAGE_SYM_DEBUG"); if (SectionNumber == llvm::COFF::IMAGE_SYM_ABSOLUTE) @@ -1431,11 +1421,10 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { if (Obj->getSymbolName(Symbol, SymbolName)) SymbolName = ""; - StringRef SectionName = ""; - ErrorOr<StringRef> Res = - getSectionName(Obj, Symbol.getSectionNumber(), Section); - if (Res) - SectionName = *Res; + StringRef SectionName; + if (Expected<StringRef> NameOrErr = + getSectionName(Obj, Symbol.getSectionNumber(), Section)) + SectionName = *NameOrErr; W.printString("Name", SymbolName); W.printNumber("Value", Symbol.getValue()); @@ -1503,16 +1492,12 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { && Aux->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { const coff_section *Assoc; StringRef AssocName = ""; - std::error_code EC = Obj->getSection(AuxNumber, Assoc); - ErrorOr<StringRef> Res = getSectionName(Obj, AuxNumber, Assoc); - if (Res) - AssocName = *Res; - if (!EC) - EC = Res.getError(); - if (EC) { - AssocName = ""; + if (std::error_code EC = Obj->getSection(AuxNumber, Assoc)) error(EC); - } + Expected<StringRef> Res = getSectionName(Obj, AuxNumber, Assoc); + if (!Res) + error(Res.takeError()); + AssocName = *Res; W.printNumber("AssocSection", AssocName, AuxNumber); } @@ -1559,7 +1544,8 @@ void COFFDumper::printUnwindInfo() { case COFF::IMAGE_FILE_MACHINE_ARMNT: { ARM::WinEH::Decoder Decoder(W, Obj->getMachine() == COFF::IMAGE_FILE_MACHINE_ARM64); - Decoder.dumpProcedureData(*Obj); + // TODO Propagate the error. + consumeError(Decoder.dumpProcedureData(*Obj)); break; } default: @@ -1581,10 +1567,10 @@ void COFFDumper::printNeededLibraries() { Libs.push_back(Name); } - std::stable_sort(Libs.begin(), Libs.end()); + llvm::stable_sort(Libs); for (const auto &L : Libs) { - outs() << " " << L << "\n"; + W.startLine() << L << "\n"; } } @@ -1674,15 +1660,13 @@ void COFFDumper::printCOFFExports() { void COFFDumper::printCOFFDirectives() { for (const SectionRef &Section : Obj->sections()) { - StringRef Contents; StringRef Name; error(Section.getName(Name)); if (Name != ".drectve") continue; - error(Section.getContents(Contents)); - + StringRef Contents = unwrapOrError(Section.getContents()); W.printString("Directive(s)", Contents); } } @@ -1721,8 +1705,7 @@ void COFFDumper::printCOFFResources() { if (!Name.startswith(".rsrc")) continue; - StringRef Ref; - error(S.getContents(Ref)); + StringRef Ref = unwrapOrError(S.getContents()); if ((Name == ".rsrc") || (Name == ".rsrc$01")) { ResourceSectionRef RSF(Ref); @@ -1777,7 +1760,8 @@ void COFFDumper::printResourceDirectoryTable( SmallString<20> IDStr; raw_svector_ostream OS(IDStr); if (i < Table.NumberOfNameEntries) { - ArrayRef<UTF16> RawEntryNameString = unwrapOrError(RSF.getEntryNameString(Entry)); + ArrayRef<UTF16> RawEntryNameString = + unwrapOrError(RSF.getEntryNameString(Entry)); std::vector<UTF16> EndianCorrectedNameString; if (llvm::sys::IsBigEndianHost) { EndianCorrectedNameString.resize(RawEntryNameString.size() + 1); @@ -1793,9 +1777,8 @@ void COFFDumper::printResourceDirectoryTable( OS << EntryNameString; } else { if (Level == "Type") { - ScopedPrinter Printer(OS); - Printer.printEnum("", Entry.Identifier.ID, - makeArrayRef(ResourceTypeNames)); + OS << ": "; + printResourceTypeName(Entry.Identifier.ID, OS); IDStr = IDStr.slice(0, IDStr.find_first_of(")", 0) + 1); } else { OS << ": (ID " << Entry.Identifier.ID << ")"; @@ -1848,18 +1831,16 @@ void COFFDumper::printStackMap() const { if (StackMapSection == object::SectionRef()) return; - StringRef StackMapContents; - StackMapSection.getContents(StackMapContents); - ArrayRef<uint8_t> StackMapContentsArray( - reinterpret_cast<const uint8_t*>(StackMapContents.data()), - StackMapContents.size()); + StringRef StackMapContents = unwrapOrError(StackMapSection.getContents()); + ArrayRef<uint8_t> StackMapContentsArray = + arrayRefFromStringRef(StackMapContents); if (Obj->isLittleEndian()) prettyPrintStackMap( - W, StackMapV2Parser<support::little>(StackMapContentsArray)); + W, StackMapParser<support::little>(StackMapContentsArray)); else - prettyPrintStackMap(W, - StackMapV2Parser<support::big>(StackMapContentsArray)); + prettyPrintStackMap( + W, StackMapParser<support::big>(StackMapContentsArray)); } void COFFDumper::printAddrsig() { @@ -1876,15 +1857,13 @@ void COFFDumper::printAddrsig() { if (AddrsigSection == object::SectionRef()) return; - StringRef AddrsigContents; - AddrsigSection.getContents(AddrsigContents); - ArrayRef<uint8_t> AddrsigContentsArray( - reinterpret_cast<const uint8_t*>(AddrsigContents.data()), - AddrsigContents.size()); + StringRef AddrsigContents = unwrapOrError(AddrsigSection.getContents()); + ArrayRef<uint8_t> AddrsigContentsArray(AddrsigContents.bytes_begin(), + AddrsigContents.size()); ListScope L(W, "Addrsig"); - auto *Cur = reinterpret_cast<const uint8_t *>(AddrsigContents.begin()); - auto *End = reinterpret_cast<const uint8_t *>(AddrsigContents.end()); + const uint8_t *Cur = AddrsigContents.bytes_begin(); + const uint8_t *End = AddrsigContents.bytes_end(); while (Cur != End) { unsigned Size; const char *Err; @@ -1905,16 +1884,10 @@ void COFFDumper::printAddrsig() { } } -void llvm::dumpCodeViewMergedTypes( - ScopedPrinter &Writer, llvm::codeview::MergingTypeTableBuilder &IDTable, - llvm::codeview::MergingTypeTableBuilder &CVTypes) { - // Flatten it first, then run our dumper on it. - SmallString<0> TypeBuf; - CVTypes.ForEachRecord([&](TypeIndex TI, const CVType &Record) { - TypeBuf.append(Record.RecordData.begin(), Record.RecordData.end()); - }); - - TypeTableCollection TpiTypes(CVTypes.records()); +void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer, + ArrayRef<ArrayRef<uint8_t>> IpiRecords, + ArrayRef<ArrayRef<uint8_t>> TpiRecords) { + TypeTableCollection TpiTypes(TpiRecords); { ListScope S(Writer, "MergedTypeStream"); TypeDumpVisitor TDV(TpiTypes, &Writer, opts::CodeViewSubsectionBytes); @@ -1924,7 +1897,7 @@ void llvm::dumpCodeViewMergedTypes( // Flatten the id stream and print it next. The ID stream refers to names from // the type stream. - TypeTableCollection IpiTypes(IDTable.records()); + TypeTableCollection IpiTypes(IpiRecords); { ListScope S(Writer, "MergedIDStream"); TypeDumpVisitor TDV(TpiTypes, &Writer, opts::CodeViewSubsectionBytes); diff --git a/contrib/llvm/tools/llvm-readobj/COFFImportDumper.cpp b/contrib/llvm/tools/llvm-readobj/COFFImportDumper.cpp index 18010c34f0f3..c9d5e82263db 100644 --- a/contrib/llvm/tools/llvm-readobj/COFFImportDumper.cpp +++ b/contrib/llvm/tools/llvm-readobj/COFFImportDumper.cpp @@ -1,9 +1,8 @@ //===-- COFFImportDumper.cpp - COFF import library dumper -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -51,7 +50,7 @@ void dumpCOFFImportFile(const COFFImportFile *File, ScopedPrinter &Writer) { for (const object::BasicSymbolRef &Sym : File->symbols()) { raw_ostream &OS = Writer.startLine(); OS << "Symbol: "; - Sym.printName(OS); + cantFail(Sym.printName(OS)); OS << "\n"; } } diff --git a/contrib/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h b/contrib/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h index d91d764c4d0a..7055510ef2f2 100644 --- a/contrib/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h +++ b/contrib/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h @@ -1,9 +1,8 @@ //===--- DwarfCFIEHPrinter.h - DWARF-based Unwind Information Printer -----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-readobj/ELFDumper.cpp b/contrib/llvm/tools/llvm-readobj/ELFDumper.cpp index 93254717e921..4e1cb7d544e7 100644 --- a/contrib/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/contrib/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -1,9 +1,8 @@ //===- ELFDumper.cpp - ELF-specific dumper --------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -20,6 +19,7 @@ #include "llvm-readobj.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/STLExtras.h" @@ -30,6 +30,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Object/ELF.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ELFTypes.h" @@ -66,13 +67,14 @@ using namespace llvm; using namespace llvm::object; using namespace ELF; -#define LLVM_READOBJ_ENUM_CASE(ns, enum) \ - case ns::enum: return #enum; +#define LLVM_READOBJ_ENUM_CASE(ns, enum) \ + case ns::enum: \ + return #enum; -#define ENUM_ENT(enum, altName) \ +#define ENUM_ENT(enum, altName) \ { #enum, altName, ELF::enum } -#define ENUM_ENT_1(enum) \ +#define ENUM_ENT_1(enum) \ { #enum, #enum, ELF::enum } #define LLVM_READOBJ_PHDR_ENUM(ns, enum) \ @@ -132,14 +134,17 @@ struct DynRegionInfo { const Type *Start = reinterpret_cast<const Type *>(Addr); if (!Start) return {Start, Start}; - if (EntSize != sizeof(Type) || Size % EntSize) - reportError("Invalid entity size"); + if (EntSize != sizeof(Type) || Size % EntSize) { + // TODO: Add a section index to this warning. + reportWarning("invalid section size (" + Twine(Size) + + ") or entity size (" + Twine(EntSize) + ")"); + return {Start, Start}; + } return {Start, Start + (Size / EntSize)}; } }; -template<typename ELFT> -class ELFDumper : public ObjDumper { +template <typename ELFT> class ELFDumper : public ObjDumper { public: ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, ScopedPrinter &Writer); @@ -147,13 +152,14 @@ public: void printSectionHeaders() override; void printRelocations() override; void printDynamicRelocations() override; - void printSymbols() override; - void printDynamicSymbols() override; + void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) override; + void printHashSymbols() override; void printUnwindInfo() override; void printDynamicTable() override; void printNeededLibraries() override; - void printProgramHeaders() override; + void printProgramHeaders(bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) override; void printHashTable() override; void printGnuHashTable() override; void printLoadName() override; @@ -177,6 +183,8 @@ public: void printELFLinkerOptions() override; + const object::ELFObjectFile<ELFT> *getElfObject() const { return ObjF; }; + private: std::unique_ptr<DumpStyle<ELFT>> ELFDumperStyle; @@ -185,24 +193,25 @@ private: DynRegionInfo checkDRI(DynRegionInfo DRI) { const ELFFile<ELFT> *Obj = ObjF->getELFFile(); if (DRI.Addr < Obj->base() || - (const uint8_t *)DRI.Addr + DRI.Size > Obj->base() + Obj->getBufSize()) + reinterpret_cast<const uint8_t *>(DRI.Addr) + DRI.Size > + Obj->base() + Obj->getBufSize()) error(llvm::object::object_error::parse_failed); return DRI; } DynRegionInfo createDRIFrom(const Elf_Phdr *P, uintX_t EntSize) { - return checkDRI({ObjF->getELFFile()->base() + P->p_offset, P->p_filesz, EntSize}); + return checkDRI( + {ObjF->getELFFile()->base() + P->p_offset, P->p_filesz, EntSize}); } DynRegionInfo createDRIFrom(const Elf_Shdr *S) { - return checkDRI({ObjF->getELFFile()->base() + S->sh_offset, S->sh_size, S->sh_entsize}); + return checkDRI( + {ObjF->getELFFile()->base() + S->sh_offset, S->sh_size, S->sh_entsize}); } - void parseDynamicTable(ArrayRef<const Elf_Phdr *> LoadSegments); + void loadDynamicTable(const ELFFile<ELFT> *Obj); + void parseDynamicTable(); - void printValue(uint64_t Type, uint64_t Value); - - StringRef getDynamicString(uint64_t Offset) const; StringRef getSymbolVersion(StringRef StrTab, const Elf_Sym *symb, bool &IsDefault) const; void LoadVersionMap() const; @@ -217,7 +226,7 @@ private: DynRegionInfo DynSymRegion; DynRegionInfo DynamicTable; StringRef DynamicStringTable; - StringRef SOName; + StringRef SOName = "<Not found>"; const Elf_Hash *HashTable = nullptr; const Elf_GnuHash *GnuHashTable = nullptr; const Elf_Shdr *DotSymtabSec = nullptr; @@ -226,9 +235,9 @@ private: StringRef DynSymtabName; ArrayRef<Elf_Word> ShndxTable; - const Elf_Shdr *dot_gnu_version_sec = nullptr; // .gnu.version - const Elf_Shdr *dot_gnu_version_r_sec = nullptr; // .gnu.version_r - const Elf_Shdr *dot_gnu_version_d_sec = nullptr; // .gnu.version_d + const Elf_Shdr *SymbolVersionSection = nullptr; // .gnu.version + const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r + const Elf_Shdr *SymbolVersionDefSection = nullptr; // .gnu.version_d // Records for each version index the corresponding Verdef or Vernaux entry. // This is filled the first time LoadVersionMap() is called. @@ -256,7 +265,18 @@ private: public: Elf_Dyn_Range dynamic_table() const { - return DynamicTable.getAsArrayRef<Elf_Dyn>(); + // A valid .dynamic section contains an array of entries terminated + // with a DT_NULL entry. However, sometimes the section content may + // continue past the DT_NULL entry, so to dump the section correctly, + // we first find the end of the entries by iterating over them. + Elf_Dyn_Range Table = DynamicTable.getAsArrayRef<Elf_Dyn>(); + + size_t Size = 0; + while (Size < Table.size()) + if (Table[Size++].getTag() == DT_NULL) + break; + + return Table.slice(0, Size); } Elf_Sym_Range dynamic_symbols() const { @@ -271,9 +291,14 @@ public: void getSectionNameIndex(const Elf_Sym *Symbol, const Elf_Sym *FirstSym, StringRef &SectionName, unsigned &SectionIndex) const; - StringRef getStaticSymbolName(uint32_t Index) const; + std::string getStaticSymbolName(uint32_t Index) const; + StringRef getSymbolVersionByIndex(StringRef StrTab, + uint32_t VersionSymbolIndex, + bool &IsDefault) const; void printSymbolsHelper(bool IsDynamic) const; + void printDynamicEntry(raw_ostream &OS, uint64_t Type, uint64_t Value) const; + const Elf_Shdr *getDotSymtabSec() const { return DotSymtabSec; } const Elf_Shdr *getDotCGProfileSec() const { return DotCGProfileSec; } const Elf_Shdr *getDotAddrsigSec() const { return DotAddrsigSec; } @@ -283,6 +308,7 @@ public: const DynRegionInfo &getDynRelaRegion() const { return DynRelaRegion; } const DynRegionInfo &getDynRelrRegion() const { return DynRelrRegion; } const DynRegionInfo &getDynPLTRelRegion() const { return DynPLTRelRegion; } + const DynRegionInfo &getDynamicTableRegion() const { return DynamicTable; } const Elf_Hash *getHashTable() const { return HashTable; } const Elf_GnuHash *getGnuHashTable() const { return GnuHashTable; } }; @@ -328,15 +354,25 @@ public: virtual void printGroupSections(const ELFFile<ELFT> *Obj) = 0; virtual void printRelocations(const ELFFile<ELFT> *Obj) = 0; virtual void printSectionHeaders(const ELFFile<ELFT> *Obj) = 0; - virtual void printSymbols(const ELFFile<ELFT> *Obj) = 0; - virtual void printDynamicSymbols(const ELFFile<ELFT> *Obj) = 0; + virtual void printSymbols(const ELFFile<ELFT> *Obj, bool PrintSymbols, + bool PrintDynamicSymbols) = 0; + virtual void printHashSymbols(const ELFFile<ELFT> *Obj) {} + virtual void printDynamic(const ELFFile<ELFT> *Obj) {} virtual void printDynamicRelocations(const ELFFile<ELFT> *Obj) = 0; - virtual void printSymtabMessage(const ELFFile<ELFT> *obj, StringRef Name, + virtual void printSymtabMessage(const ELFFile<ELFT> *Obj, StringRef Name, size_t Offset) {} virtual void printSymbol(const ELFFile<ELFT> *Obj, const Elf_Sym *Symbol, const Elf_Sym *FirstSym, StringRef StrTable, bool IsDynamic) = 0; - virtual void printProgramHeaders(const ELFFile<ELFT> *Obj) = 0; + virtual void printProgramHeaders(const ELFFile<ELFT> *Obj, + bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) = 0; + virtual void printVersionSymbolSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) = 0; + virtual void printVersionDefinitionSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) = 0; + virtual void printVersionDependencySection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) = 0; virtual void printHashHistogram(const ELFFile<ELFT> *Obj) = 0; virtual void printCGProfile(const ELFFile<ELFT> *Obj) = 0; virtual void printAddrsig(const ELFFile<ELFT> *Obj) = 0; @@ -351,24 +387,36 @@ private: }; template <typename ELFT> class GNUStyle : public DumpStyle<ELFT> { - formatted_raw_ostream OS; + formatted_raw_ostream &OS; public: TYPEDEF_ELF_TYPES(ELFT) GNUStyle(ScopedPrinter &W, ELFDumper<ELFT> *Dumper) - : DumpStyle<ELFT>(Dumper), OS(W.getOStream()) {} + : DumpStyle<ELFT>(Dumper), + OS(static_cast<formatted_raw_ostream&>(W.getOStream())) { + assert (&W.getOStream() == &llvm::fouts()); + } void printFileHeaders(const ELFO *Obj) override; void printGroupSections(const ELFFile<ELFT> *Obj) override; void printRelocations(const ELFO *Obj) override; void printSectionHeaders(const ELFO *Obj) override; - void printSymbols(const ELFO *Obj) override; - void printDynamicSymbols(const ELFO *Obj) override; + void printSymbols(const ELFO *Obj, bool PrintSymbols, + bool PrintDynamicSymbols) override; + void printHashSymbols(const ELFO *Obj) override; + void printDynamic(const ELFFile<ELFT> *Obj) override; void printDynamicRelocations(const ELFO *Obj) override; void printSymtabMessage(const ELFO *Obj, StringRef Name, size_t Offset) override; - void printProgramHeaders(const ELFO *Obj) override; + void printProgramHeaders(const ELFO *Obj, bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) override; + void printVersionSymbolSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; + void printVersionDefinitionSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; + void printVersionDependencySection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; void printHashHistogram(const ELFFile<ELFT> *Obj) override; void printCGProfile(const ELFFile<ELFT> *Obj) override; void printAddrsig(const ELFFile<ELFT> *Obj) override; @@ -379,11 +427,11 @@ public: private: struct Field { - StringRef Str; + std::string Str; unsigned Column; Field(StringRef S, unsigned Col) : Str(S), Column(Col) {} - Field(unsigned Col) : Str(""), Column(Col) {} + Field(unsigned Col) : Column(Col) {} }; template <typename T, typename TEnum> @@ -433,6 +481,8 @@ private: void printRelocHeader(unsigned SType); void printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab, const Elf_Rela &R, bool IsRela); + void printRelocation(const ELFO *Obj, const Elf_Sym *Sym, + StringRef SymbolName, const Elf_Rela &R, bool IsRela); void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First, StringRef StrTable, bool IsDynamic) override; std::string getSymbolSectionNdx(const ELFO *Obj, const Elf_Sym *Symbol, @@ -442,6 +492,8 @@ private: bool checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); bool checkVMA(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); bool checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); + void printProgramHeaders(const ELFO *Obj); + void printSectionMapping(const ELFO *Obj); }; template <typename ELFT> class LLVMStyle : public DumpStyle<ELFT> { @@ -456,10 +508,18 @@ public: void printRelocations(const ELFO *Obj) override; void printRelocations(const Elf_Shdr *Sec, const ELFO *Obj); void printSectionHeaders(const ELFO *Obj) override; - void printSymbols(const ELFO *Obj) override; - void printDynamicSymbols(const ELFO *Obj) override; + void printSymbols(const ELFO *Obj, bool PrintSymbols, + bool PrintDynamicSymbols) override; + void printDynamic(const ELFFile<ELFT> *Obj) override; void printDynamicRelocations(const ELFO *Obj) override; - void printProgramHeaders(const ELFO *Obj) override; + void printProgramHeaders(const ELFO *Obj, bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) override; + void printVersionSymbolSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; + void printVersionDefinitionSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; + void printVersionDependencySection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) override; void printHashHistogram(const ELFFile<ELFT> *Obj) override; void printCGProfile(const ELFFile<ELFT> *Obj) override; void printAddrsig(const ELFFile<ELFT> *Obj) override; @@ -471,8 +531,12 @@ public: private: void printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab); void printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel); + void printSymbols(const ELFO *Obj); + void printDynamicSymbols(const ELFO *Obj); void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First, StringRef StrTable, bool IsDynamic) override; + void printProgramHeaders(const ELFO *Obj); + void printSectionMapping(const ELFO *Obj) {} ScopedPrinter &W; }; @@ -516,65 +580,71 @@ std::error_code createELFDumper(const object::ObjectFile *Obj, // Iterate through the versions needed section, and place each Elf_Vernaux // in the VersionMap according to its index. template <class ELFT> -void ELFDumper<ELFT>::LoadVersionNeeds(const Elf_Shdr *sec) const { - unsigned vn_size = sec->sh_size; // Size of section in bytes - unsigned vn_count = sec->sh_info; // Number of Verneed entries - const char *sec_start = (const char *)ObjF->getELFFile()->base() + sec->sh_offset; - const char *sec_end = sec_start + vn_size; +void ELFDumper<ELFT>::LoadVersionNeeds(const Elf_Shdr *Sec) const { + unsigned VerneedSize = Sec->sh_size; // Size of section in bytes + unsigned VerneedEntries = Sec->sh_info; // Number of Verneed entries + const uint8_t *VerneedStart = reinterpret_cast<const uint8_t *>( + ObjF->getELFFile()->base() + Sec->sh_offset); + const uint8_t *VerneedEnd = VerneedStart + VerneedSize; // The first Verneed entry is at the start of the section. - const char *p = sec_start; - for (unsigned i = 0; i < vn_count; i++) { - if (p + sizeof(Elf_Verneed) > sec_end) + const uint8_t *VerneedBuf = VerneedStart; + for (unsigned VerneedIndex = 0; VerneedIndex < VerneedEntries; + ++VerneedIndex) { + if (VerneedBuf + sizeof(Elf_Verneed) > VerneedEnd) report_fatal_error("Section ended unexpectedly while scanning " "version needed records."); - const Elf_Verneed *vn = reinterpret_cast<const Elf_Verneed *>(p); - if (vn->vn_version != ELF::VER_NEED_CURRENT) + const Elf_Verneed *Verneed = + reinterpret_cast<const Elf_Verneed *>(VerneedBuf); + if (Verneed->vn_version != ELF::VER_NEED_CURRENT) report_fatal_error("Unexpected verneed version"); // Iterate through the Vernaux entries - const char *paux = p + vn->vn_aux; - for (unsigned j = 0; j < vn->vn_cnt; j++) { - if (paux + sizeof(Elf_Vernaux) > sec_end) + const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; + for (unsigned VernauxIndex = 0; VernauxIndex < Verneed->vn_cnt; + ++VernauxIndex) { + if (VernauxBuf + sizeof(Elf_Vernaux) > VerneedEnd) report_fatal_error("Section ended unexpected while scanning auxiliary " "version needed records."); - const Elf_Vernaux *vna = reinterpret_cast<const Elf_Vernaux *>(paux); - size_t index = vna->vna_other & ELF::VERSYM_VERSION; - if (index >= VersionMap.size()) - VersionMap.resize(index + 1); - VersionMap[index] = VersionMapEntry(vna); - paux += vna->vna_next; + const Elf_Vernaux *Vernaux = + reinterpret_cast<const Elf_Vernaux *>(VernauxBuf); + size_t Index = Vernaux->vna_other & ELF::VERSYM_VERSION; + if (Index >= VersionMap.size()) + VersionMap.resize(Index + 1); + VersionMap[Index] = VersionMapEntry(Vernaux); + VernauxBuf += Vernaux->vna_next; } - p += vn->vn_next; + VerneedBuf += Verneed->vn_next; } } // Iterate through the version definitions, and place each Elf_Verdef // in the VersionMap according to its index. template <class ELFT> -void ELFDumper<ELFT>::LoadVersionDefs(const Elf_Shdr *sec) const { - unsigned vd_size = sec->sh_size; // Size of section in bytes - unsigned vd_count = sec->sh_info; // Number of Verdef entries - const char *sec_start = (const char *)ObjF->getELFFile()->base() + sec->sh_offset; - const char *sec_end = sec_start + vd_size; +void ELFDumper<ELFT>::LoadVersionDefs(const Elf_Shdr *Sec) const { + unsigned VerdefSize = Sec->sh_size; // Size of section in bytes + unsigned VerdefEntries = Sec->sh_info; // Number of Verdef entries + const uint8_t *VerdefStart = reinterpret_cast<const uint8_t *>( + ObjF->getELFFile()->base() + Sec->sh_offset); + const uint8_t *VerdefEnd = VerdefStart + VerdefSize; // The first Verdef entry is at the start of the section. - const char *p = sec_start; - for (unsigned i = 0; i < vd_count; i++) { - if (p + sizeof(Elf_Verdef) > sec_end) + const uint8_t *VerdefBuf = VerdefStart; + for (unsigned VerdefIndex = 0; VerdefIndex < VerdefEntries; ++VerdefIndex) { + if (VerdefBuf + sizeof(Elf_Verdef) > VerdefEnd) report_fatal_error("Section ended unexpectedly while scanning " "version definitions."); - const Elf_Verdef *vd = reinterpret_cast<const Elf_Verdef *>(p); - if (vd->vd_version != ELF::VER_DEF_CURRENT) + const Elf_Verdef *Verdef = reinterpret_cast<const Elf_Verdef *>(VerdefBuf); + if (Verdef->vd_version != ELF::VER_DEF_CURRENT) report_fatal_error("Unexpected verdef version"); - size_t index = vd->vd_ndx & ELF::VERSYM_VERSION; - if (index >= VersionMap.size()) - VersionMap.resize(index + 1); - VersionMap[index] = VersionMapEntry(vd); - p += vd->vd_next; + size_t Index = Verdef->vd_ndx & ELF::VERSYM_VERSION; + if (Index >= VersionMap.size()) + VersionMap.resize(Index + 1); + VersionMap[Index] = VersionMapEntry(Verdef); + VerdefBuf += Verdef->vd_next; } } template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() const { // If there is no dynamic symtab or version table, there is nothing to do. - if (!DynSymRegion.Addr || !dot_gnu_version_sec) + if (!DynSymRegion.Addr || !SymbolVersionSection) return; // Has the VersionMap already been loaded? @@ -586,243 +656,111 @@ template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() const { VersionMap.push_back(VersionMapEntry()); VersionMap.push_back(VersionMapEntry()); - if (dot_gnu_version_d_sec) - LoadVersionDefs(dot_gnu_version_d_sec); - - if (dot_gnu_version_r_sec) - LoadVersionNeeds(dot_gnu_version_r_sec); -} - -template <typename ELFO, class ELFT> -static void printVersionSymbolSection(ELFDumper<ELFT> *Dumper, const ELFO *Obj, - const typename ELFO::Elf_Shdr *Sec, - ScopedPrinter &W) { - DictScope SS(W, "Version symbols"); - if (!Sec) - return; - StringRef Name = unwrapOrError(Obj->getSectionName(Sec)); - W.printNumber("Section Name", Name, Sec->sh_name); - W.printHex("Address", Sec->sh_addr); - W.printHex("Offset", Sec->sh_offset); - W.printNumber("Link", Sec->sh_link); - - const uint8_t *P = (const uint8_t *)Obj->base() + Sec->sh_offset; - StringRef StrTable = Dumper->getDynamicStringTable(); - - // Same number of entries in the dynamic symbol table (DT_SYMTAB). - ListScope Syms(W, "Symbols"); - for (const typename ELFO::Elf_Sym &Sym : Dumper->dynamic_symbols()) { - DictScope S(W, "Symbol"); - std::string FullSymbolName = - Dumper->getFullSymbolName(&Sym, StrTable, true /* IsDynamic */); - W.printNumber("Version", *P); - W.printString("Name", FullSymbolName); - P += sizeof(typename ELFO::Elf_Half); - } -} - -static const EnumEntry<unsigned> SymVersionFlags[] = { - {"Base", "BASE", VER_FLG_BASE}, - {"Weak", "WEAK", VER_FLG_WEAK}, - {"Info", "INFO", VER_FLG_INFO}}; - -template <typename ELFO, class ELFT> -static void printVersionDefinitionSection(ELFDumper<ELFT> *Dumper, - const ELFO *Obj, - const typename ELFO::Elf_Shdr *Sec, - ScopedPrinter &W) { - using VerDef = typename ELFO::Elf_Verdef; - using VerdAux = typename ELFO::Elf_Verdaux; - - DictScope SD(W, "SHT_GNU_verdef"); - if (!Sec) - return; - - // The number of entries in the section SHT_GNU_verdef - // is determined by DT_VERDEFNUM tag. - unsigned VerDefsNum = 0; - for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) { - if (Dyn.d_tag == DT_VERDEFNUM) { - VerDefsNum = Dyn.d_un.d_val; - break; - } - } - - const uint8_t *SecStartAddress = - (const uint8_t *)Obj->base() + Sec->sh_offset; - const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size; - const uint8_t *P = SecStartAddress; - const typename ELFO::Elf_Shdr *StrTab = - unwrapOrError(Obj->getSection(Sec->sh_link)); - - while (VerDefsNum--) { - if (P + sizeof(VerDef) > SecEndAddress) - report_fatal_error("invalid offset in the section"); - - auto *VD = reinterpret_cast<const VerDef *>(P); - DictScope Def(W, "Definition"); - W.printNumber("Version", VD->vd_version); - W.printEnum("Flags", VD->vd_flags, makeArrayRef(SymVersionFlags)); - W.printNumber("Index", VD->vd_ndx); - W.printNumber("Hash", VD->vd_hash); - W.printString("Name", - StringRef((const char *)(Obj->base() + StrTab->sh_offset + - VD->getAux()->vda_name))); - if (!VD->vd_cnt) - report_fatal_error("at least one definition string must exist"); - if (VD->vd_cnt > 2) - report_fatal_error("more than one predecessor is not expected"); - - if (VD->vd_cnt == 2) { - const uint8_t *PAux = P + VD->vd_aux + VD->getAux()->vda_next; - const VerdAux *Aux = reinterpret_cast<const VerdAux *>(PAux); - W.printString("Predecessor", - StringRef((const char *)(Obj->base() + StrTab->sh_offset + - Aux->vda_name))); - } - - P += VD->vd_next; - } -} - -template <typename ELFO, class ELFT> -static void printVersionDependencySection(ELFDumper<ELFT> *Dumper, - const ELFO *Obj, - const typename ELFO::Elf_Shdr *Sec, - ScopedPrinter &W) { - using VerNeed = typename ELFO::Elf_Verneed; - using VernAux = typename ELFO::Elf_Vernaux; - - DictScope SD(W, "SHT_GNU_verneed"); - if (!Sec) - return; - - unsigned VerNeedNum = 0; - for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) { - if (Dyn.d_tag == DT_VERNEEDNUM) { - VerNeedNum = Dyn.d_un.d_val; - break; - } - } - - const uint8_t *SecData = (const uint8_t *)Obj->base() + Sec->sh_offset; - const typename ELFO::Elf_Shdr *StrTab = - unwrapOrError(Obj->getSection(Sec->sh_link)); - - const uint8_t *P = SecData; - for (unsigned I = 0; I < VerNeedNum; ++I) { - const VerNeed *Need = reinterpret_cast<const VerNeed *>(P); - DictScope Entry(W, "Dependency"); - W.printNumber("Version", Need->vn_version); - W.printNumber("Count", Need->vn_cnt); - W.printString("FileName", - StringRef((const char *)(Obj->base() + StrTab->sh_offset + - Need->vn_file))); - - const uint8_t *PAux = P + Need->vn_aux; - for (unsigned J = 0; J < Need->vn_cnt; ++J) { - const VernAux *Aux = reinterpret_cast<const VernAux *>(PAux); - DictScope Entry(W, "Entry"); - W.printNumber("Hash", Aux->vna_hash); - W.printEnum("Flags", Aux->vna_flags, makeArrayRef(SymVersionFlags)); - W.printNumber("Index", Aux->vna_other); - W.printString("Name", - StringRef((const char *)(Obj->base() + StrTab->sh_offset + - Aux->vna_name))); - PAux += Aux->vna_next; - } - P += Need->vn_next; - } -} - -template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() { - // Dump version symbol section. - printVersionSymbolSection(this, ObjF->getELFFile(), dot_gnu_version_sec, W); - - // Dump version definition section. - printVersionDefinitionSection(this, ObjF->getELFFile(), dot_gnu_version_d_sec, W); + if (SymbolVersionDefSection) + LoadVersionDefs(SymbolVersionDefSection); - // Dump version dependency section. - printVersionDependencySection(this, ObjF->getELFFile(), dot_gnu_version_r_sec, W); + if (SymbolVersionNeedSection) + LoadVersionNeeds(SymbolVersionNeedSection); } template <typename ELFT> StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab, - const Elf_Sym *symb, + const Elf_Sym *Sym, bool &IsDefault) const { // This is a dynamic symbol. Look in the GNU symbol version table. - if (!dot_gnu_version_sec) { + if (!SymbolVersionSection) { // No version table. IsDefault = false; - return StringRef(""); + return ""; } // Determine the position in the symbol table of this entry. - size_t entry_index = (reinterpret_cast<uintptr_t>(symb) - + size_t EntryIndex = (reinterpret_cast<uintptr_t>(Sym) - reinterpret_cast<uintptr_t>(DynSymRegion.Addr)) / sizeof(Elf_Sym); - // Get the corresponding version index entry - const Elf_Versym *vs = unwrapOrError( - ObjF->getELFFile()->template getEntry<Elf_Versym>(dot_gnu_version_sec, entry_index)); - size_t version_index = vs->vs_index & ELF::VERSYM_VERSION; + // Get the corresponding version index entry. + const Elf_Versym *Versym = + unwrapOrError(ObjF->getELFFile()->template getEntry<Elf_Versym>( + SymbolVersionSection, EntryIndex)); + return this->getSymbolVersionByIndex(StrTab, Versym->vs_index, IsDefault); +} + +static std::string maybeDemangle(StringRef Name) { + return opts::Demangle ? demangle(Name) : Name.str(); +} + +template <typename ELFT> +std::string ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + StringRef StrTable = + unwrapOrError(Obj->getStringTableForSymtab(*DotSymtabSec)); + Elf_Sym_Range Syms = unwrapOrError(Obj->symbols(DotSymtabSec)); + if (Index >= Syms.size()) + reportError("Invalid symbol index"); + const Elf_Sym *Sym = &Syms[Index]; + return maybeDemangle(unwrapOrError(Sym->getName(StrTable))); +} + +template <typename ELFT> +StringRef ELFDumper<ELFT>::getSymbolVersionByIndex(StringRef StrTab, + uint32_t SymbolVersionIndex, + bool &IsDefault) const { + size_t VersionIndex = SymbolVersionIndex & VERSYM_VERSION; // Special markers for unversioned symbols. - if (version_index == ELF::VER_NDX_LOCAL || - version_index == ELF::VER_NDX_GLOBAL) { + if (VersionIndex == VER_NDX_LOCAL || VersionIndex == VER_NDX_GLOBAL) { IsDefault = false; - return StringRef(""); + return ""; } - // Lookup this symbol in the version table + // Lookup this symbol in the version table. LoadVersionMap(); - if (version_index >= VersionMap.size() || VersionMap[version_index].isNull()) + if (VersionIndex >= VersionMap.size() || VersionMap[VersionIndex].isNull()) reportError("Invalid version entry"); - const VersionMapEntry &entry = VersionMap[version_index]; + const VersionMapEntry &Entry = VersionMap[VersionIndex]; - // Get the version name string - size_t name_offset; - if (entry.isVerdef()) { + // Get the version name string. + size_t NameOffset; + if (Entry.isVerdef()) { // The first Verdaux entry holds the name. - name_offset = entry.getVerdef()->getAux()->vda_name; - IsDefault = !(vs->vs_index & ELF::VERSYM_HIDDEN); + NameOffset = Entry.getVerdef()->getAux()->vda_name; + IsDefault = !(SymbolVersionIndex & VERSYM_HIDDEN); } else { - name_offset = entry.getVernaux()->vna_name; + NameOffset = Entry.getVernaux()->vna_name; IsDefault = false; } - if (name_offset >= StrTab.size()) + if (NameOffset >= StrTab.size()) reportError("Invalid string offset"); - return StringRef(StrTab.data() + name_offset); -} - -template <typename ELFT> -StringRef ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const { - const ELFFile<ELFT> *Obj = ObjF->getELFFile(); - StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*DotSymtabSec)); - Elf_Sym_Range Syms = unwrapOrError(Obj->symbols(DotSymtabSec)); - if (Index >= Syms.size()) - reportError("Invalid symbol index"); - const Elf_Sym *Sym = &Syms[Index]; - return unwrapOrError(Sym->getName(StrTable)); + return StrTab.data() + NameOffset; } template <typename ELFT> std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable, bool IsDynamic) const { - StringRef SymbolName = unwrapOrError(Symbol->getName(StrTable)); + std::string SymbolName = + maybeDemangle(unwrapOrError(Symbol->getName(StrTable))); + + if (SymbolName.empty() && Symbol->getType() == ELF::STT_SECTION) { + unsigned SectionIndex; + StringRef SectionName; + Elf_Sym_Range Syms = + unwrapOrError(ObjF->getELFFile()->symbols(DotSymtabSec)); + getSectionNameIndex(Symbol, Syms.begin(), SectionName, SectionIndex); + return SectionName; + } + if (!IsDynamic) return SymbolName; - std::string FullSymbolName(SymbolName); - bool IsDefault; StringRef Version = getSymbolVersion(StrTable, &*Symbol, IsDefault); if (!Version.empty()) { - FullSymbolName += (IsDefault ? "@@" : "@"); - FullSymbolName += Version; + SymbolName += (IsDefault ? "@@" : "@"); + SymbolName += Version; } - return FullSymbolName; + return SymbolName; } template <typename ELFT> @@ -914,6 +852,11 @@ static const EnumEntry<unsigned> ElfOSABI[] = { {"Standalone", "Standalone App", ELF::ELFOSABI_STANDALONE} }; +static const EnumEntry<unsigned> SymVersionFlags[] = { + {"Base", "BASE", VER_FLG_BASE}, + {"Weak", "WEAK", VER_FLG_WEAK}, + {"Info", "INFO", VER_FLG_INFO}}; + static const EnumEntry<unsigned> AMDGPUElfOSABI[] = { {"AMDGPU_HSA", "AMDGPU - HSA", ELF::ELFOSABI_AMDGPU_HSA}, {"AMDGPU_PAL", "AMDGPU - PAL", ELF::ELFOSABI_AMDGPU_PAL}, @@ -1103,16 +1046,6 @@ static const EnumEntry<unsigned> ElfSymbolVisibilities[] = { {"HIDDEN", "HIDDEN", ELF::STV_HIDDEN}, {"PROTECTED", "PROTECTED", ELF::STV_PROTECTED}}; -static const EnumEntry<unsigned> ElfSymbolTypes[] = { - {"None", "NOTYPE", ELF::STT_NOTYPE}, - {"Object", "OBJECT", ELF::STT_OBJECT}, - {"Function", "FUNC", ELF::STT_FUNC}, - {"Section", "SECTION", ELF::STT_SECTION}, - {"File", "FILE", ELF::STT_FILE}, - {"Common", "COMMON", ELF::STT_COMMON}, - {"TLS", "TLS", ELF::STT_TLS}, - {"GNU_IFunc", "IFUNC", ELF::STT_GNU_IFUNC}}; - static const EnumEntry<unsigned> AMDGPUSymbolTypes[] = { { "AMDGPU_HSA_KERNEL", ELF::STT_AMDGPU_HSA_KERNEL } }; @@ -1205,14 +1138,12 @@ static const char *getElfSegmentType(unsigned Arch, unsigned Type) { // program header type. switch (Arch) { case ELF::EM_ARM: - switch (Type) { - LLVM_READOBJ_ENUM_CASE(ELF, PT_ARM_EXIDX); - } + switch (Type) { LLVM_READOBJ_ENUM_CASE(ELF, PT_ARM_EXIDX); } break; case ELF::EM_MIPS: case ELF::EM_MIPS_RS3_LE: switch (Type) { - LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_REGINFO); + LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_REGINFO); LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_RTPROC); LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_OPTIONS); LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_ABIFLAGS); @@ -1233,14 +1164,15 @@ static const char *getElfSegmentType(unsigned Arch, unsigned Type) { LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_EH_FRAME); LLVM_READOBJ_ENUM_CASE(ELF, PT_SUNW_UNWIND); - LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_STACK); - LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_RELRO); + LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_STACK); + LLVM_READOBJ_ENUM_CASE(ELF, PT_GNU_RELRO); - LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_RANDOMIZE); - LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_WXNEEDED); - LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_BOOTDATA); + LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_RANDOMIZE); + LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_WXNEEDED); + LLVM_READOBJ_ENUM_CASE(ELF, PT_OPENBSD_BOOTDATA); - default: return ""; + default: + return ""; } } @@ -1368,7 +1300,11 @@ static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = { 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_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_XNACK), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_SRAM_ECC) }; @@ -1420,68 +1356,118 @@ static const char *getElfMipsOptionsOdkType(unsigned Odk) { } template <typename ELFT> -ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, - ScopedPrinter &Writer) - : ObjDumper(Writer), ObjF(ObjF) { - SmallVector<const Elf_Phdr *, 4> LoadSegments; - const ELFFile<ELFT> *Obj = ObjF->getELFFile(); +void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) { + // Try to locate the PT_DYNAMIC header. + const Elf_Phdr *DynamicPhdr = nullptr; for (const Elf_Phdr &Phdr : unwrapOrError(Obj->program_headers())) { - if (Phdr.p_type == ELF::PT_DYNAMIC) { - DynamicTable = createDRIFrom(&Phdr, sizeof(Elf_Dyn)); + if (Phdr.p_type != ELF::PT_DYNAMIC) continue; - } - if (Phdr.p_type != ELF::PT_LOAD || Phdr.p_filesz == 0) + DynamicPhdr = &Phdr; + break; + } + + // Try to locate the .dynamic section in the sections header table. + const Elf_Shdr *DynamicSec = nullptr; + for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { + if (Sec.sh_type != ELF::SHT_DYNAMIC) continue; - LoadSegments.push_back(&Phdr); + DynamicSec = &Sec; + break; + } + + // Information in the section header has priority over the information + // in a PT_DYNAMIC header. + // Ignore sh_entsize and use the expected value for entry size explicitly. + // This allows us to dump the dynamic sections with a broken sh_entsize + // field. + if (DynamicSec) { + DynamicTable = checkDRI({ObjF->getELFFile()->base() + DynamicSec->sh_offset, + DynamicSec->sh_size, sizeof(Elf_Dyn)}); + parseDynamicTable(); + } + + // If we have a PT_DYNAMIC header, we will either check the found dynamic + // section or take the dynamic table data directly from the header. + if (!DynamicPhdr) + return; + + if (DynamicPhdr->p_offset + DynamicPhdr->p_filesz > + ObjF->getMemoryBufferRef().getBufferSize()) + reportError( + "PT_DYNAMIC segment offset + size exceeds the size of the file"); + + if (!DynamicSec) { + DynamicTable = createDRIFrom(DynamicPhdr, sizeof(Elf_Dyn)); + parseDynamicTable(); + return; } + StringRef Name = unwrapOrError(Obj->getSectionName(DynamicSec)); + if (DynamicSec->sh_addr + DynamicSec->sh_size > + DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz || + DynamicSec->sh_addr < DynamicPhdr->p_vaddr) + reportWarning("The SHT_DYNAMIC section '" + Name + + "' is not contained within the " + "PT_DYNAMIC segment"); + + if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr) + reportWarning("The SHT_DYNAMIC section '" + Name + + "' is not at the start of " + "PT_DYNAMIC segment"); +} + +template <typename ELFT> +ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, + ScopedPrinter &Writer) + : ObjDumper(Writer), ObjF(ObjF) { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { switch (Sec.sh_type) { case ELF::SHT_SYMTAB: - if (DotSymtabSec != nullptr) - reportError("Multiple SHT_SYMTAB"); - DotSymtabSec = &Sec; + if (!DotSymtabSec) + DotSymtabSec = &Sec; break; case ELF::SHT_DYNSYM: - if (DynSymRegion.Size) - reportError("Multiple SHT_DYNSYM"); - DynSymRegion = createDRIFrom(&Sec); - // This is only used (if Elf_Shdr present)for naming section in GNU style - DynSymtabName = unwrapOrError(Obj->getSectionName(&Sec)); - DynamicStringTable = unwrapOrError(Obj->getStringTableForSymtab(Sec)); + if (!DynSymRegion.Size) { + DynSymRegion = createDRIFrom(&Sec); + // This is only used (if Elf_Shdr present)for naming section in GNU + // style + DynSymtabName = unwrapOrError(Obj->getSectionName(&Sec)); + + if (Expected<StringRef> E = Obj->getStringTableForSymtab(Sec)) + DynamicStringTable = *E; + else + warn(E.takeError()); + } break; case ELF::SHT_SYMTAB_SHNDX: ShndxTable = unwrapOrError(Obj->getSHNDXTable(Sec)); break; case ELF::SHT_GNU_versym: - if (dot_gnu_version_sec != nullptr) - reportError("Multiple SHT_GNU_versym"); - dot_gnu_version_sec = &Sec; + if (!SymbolVersionSection) + SymbolVersionSection = &Sec; break; case ELF::SHT_GNU_verdef: - if (dot_gnu_version_d_sec != nullptr) - reportError("Multiple SHT_GNU_verdef"); - dot_gnu_version_d_sec = &Sec; + if (!SymbolVersionDefSection) + SymbolVersionDefSection = &Sec; break; case ELF::SHT_GNU_verneed: - if (dot_gnu_version_r_sec != nullptr) - reportError("Multiple SHT_GNU_verneed"); - dot_gnu_version_r_sec = &Sec; + if (!SymbolVersionNeedSection) + SymbolVersionNeedSection = &Sec; break; case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: - if (DotCGProfileSec != nullptr) - reportError("Multiple .llvm.call-graph-profile"); - DotCGProfileSec = &Sec; + if (!DotCGProfileSec) + DotCGProfileSec = &Sec; break; case ELF::SHT_LLVM_ADDRSIG: - if (DotAddrsigSec != nullptr) - reportError("Multiple .llvm_addrsig"); - DotAddrsigSec = &Sec; + if (!DotAddrsigSec) + DotAddrsigSec = &Sec; break; } } - parseDynamicTable(LoadSegments); + loadDynamicTable(Obj); if (opts::Output == opts::GNU) ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this)); @@ -1489,13 +1475,84 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this)); } -template <typename ELFT> -void ELFDumper<ELFT>::parseDynamicTable( - ArrayRef<const Elf_Phdr *> LoadSegments) { - auto toMappedAddr = [&](uint64_t VAddr) -> const uint8_t * { +static const char *getTypeString(unsigned Arch, uint64_t Type) { +#define DYNAMIC_TAG(n, v) + switch (Arch) { + + case EM_AARCH64: + switch (Type) { +#define AARCH64_DYNAMIC_TAG(name, value) \ + case DT_##name: \ + return #name; +#include "llvm/BinaryFormat/DynamicTags.def" +#undef AARCH64_DYNAMIC_TAG + } + break; + + case EM_HEXAGON: + switch (Type) { +#define HEXAGON_DYNAMIC_TAG(name, value) \ + case DT_##name: \ + return #name; +#include "llvm/BinaryFormat/DynamicTags.def" +#undef HEXAGON_DYNAMIC_TAG + } + break; + + case EM_MIPS: + switch (Type) { +#define MIPS_DYNAMIC_TAG(name, value) \ + case DT_##name: \ + return #name; +#include "llvm/BinaryFormat/DynamicTags.def" +#undef MIPS_DYNAMIC_TAG + } + break; + + case EM_PPC64: + switch (Type) { +#define PPC64_DYNAMIC_TAG(name, value) \ + case DT_##name: \ + return #name; +#include "llvm/BinaryFormat/DynamicTags.def" +#undef PPC64_DYNAMIC_TAG + } + break; + } +#undef DYNAMIC_TAG + switch (Type) { +// Now handle all dynamic tags except the architecture specific ones +#define AARCH64_DYNAMIC_TAG(name, value) +#define MIPS_DYNAMIC_TAG(name, value) +#define HEXAGON_DYNAMIC_TAG(name, value) +#define PPC64_DYNAMIC_TAG(name, value) +// Also ignore marker tags such as DT_HIOS (maps to DT_VERNEEDNUM), etc. +#define DYNAMIC_TAG_MARKER(name, value) +#define DYNAMIC_TAG(name, value) \ + case DT_##name: \ + return #name; +#include "llvm/BinaryFormat/DynamicTags.def" +#undef DYNAMIC_TAG +#undef AARCH64_DYNAMIC_TAG +#undef MIPS_DYNAMIC_TAG +#undef HEXAGON_DYNAMIC_TAG +#undef PPC64_DYNAMIC_TAG +#undef DYNAMIC_TAG_MARKER + default: + return "unknown"; + } +} + +template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() { + auto toMappedAddr = [&](uint64_t Tag, uint64_t VAddr) -> const uint8_t * { auto MappedAddrOrError = ObjF->getELFFile()->toMappedAddr(VAddr); - if (!MappedAddrOrError) - report_fatal_error(MappedAddrOrError.takeError()); + if (!MappedAddrOrError) { + reportWarning("Unable to parse DT_" + + Twine(getTypeString( + ObjF->getELFFile()->getHeader()->e_machine, Tag)) + + ": " + llvm::toString(MappedAddrOrError.takeError())); + return nullptr; + } return MappedAddrOrError.get(); }; @@ -1505,25 +1562,26 @@ void ELFDumper<ELFT>::parseDynamicTable( for (const Elf_Dyn &Dyn : dynamic_table()) { switch (Dyn.d_tag) { case ELF::DT_HASH: - HashTable = - reinterpret_cast<const Elf_Hash *>(toMappedAddr(Dyn.getPtr())); + HashTable = reinterpret_cast<const Elf_Hash *>( + toMappedAddr(Dyn.getTag(), Dyn.getPtr())); break; case ELF::DT_GNU_HASH: - GnuHashTable = - reinterpret_cast<const Elf_GnuHash *>(toMappedAddr(Dyn.getPtr())); + GnuHashTable = reinterpret_cast<const Elf_GnuHash *>( + toMappedAddr(Dyn.getTag(), Dyn.getPtr())); break; case ELF::DT_STRTAB: - StringTableBegin = (const char *)toMappedAddr(Dyn.getPtr()); + StringTableBegin = reinterpret_cast<const char *>( + toMappedAddr(Dyn.getTag(), Dyn.getPtr())); break; case ELF::DT_STRSZ: StringTableSize = Dyn.getVal(); break; case ELF::DT_SYMTAB: - DynSymRegion.Addr = toMappedAddr(Dyn.getPtr()); + DynSymRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); DynSymRegion.EntSize = sizeof(Elf_Sym); break; case ELF::DT_RELA: - DynRelaRegion.Addr = toMappedAddr(Dyn.getPtr()); + DynRelaRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); break; case ELF::DT_RELASZ: DynRelaRegion.Size = Dyn.getVal(); @@ -1535,7 +1593,7 @@ void ELFDumper<ELFT>::parseDynamicTable( SONameOffset = Dyn.getVal(); break; case ELF::DT_REL: - DynRelRegion.Addr = toMappedAddr(Dyn.getPtr()); + DynRelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); break; case ELF::DT_RELSZ: DynRelRegion.Size = Dyn.getVal(); @@ -1545,7 +1603,7 @@ void ELFDumper<ELFT>::parseDynamicTable( break; case ELF::DT_RELR: case ELF::DT_ANDROID_RELR: - DynRelrRegion.Addr = toMappedAddr(Dyn.getPtr()); + DynRelrRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); break; case ELF::DT_RELRSZ: case ELF::DT_ANDROID_RELRSZ: @@ -1565,7 +1623,7 @@ void ELFDumper<ELFT>::parseDynamicTable( Twine((uint64_t)Dyn.getVal())); break; case ELF::DT_JMPREL: - DynPLTRelRegion.Addr = toMappedAddr(Dyn.getPtr()); + DynPLTRelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); break; case ELF::DT_PLTRELSZ: DynPLTRelRegion.Size = Dyn.getVal(); @@ -1574,8 +1632,8 @@ void ELFDumper<ELFT>::parseDynamicTable( } if (StringTableBegin) DynamicStringTable = StringRef(StringTableBegin, StringTableSize); - if (SONameOffset) - SOName = getDynamicString(SONameOffset); + if (SONameOffset && SONameOffset < DynamicStringTable.size()) + SOName = DynamicStringTable.data() + SONameOffset; } template <typename ELFT> @@ -1593,37 +1651,52 @@ typename ELFDumper<ELFT>::Elf_Relr_Range ELFDumper<ELFT>::dyn_relrs() const { return DynRelrRegion.getAsArrayRef<Elf_Relr>(); } -template<class ELFT> -void ELFDumper<ELFT>::printFileHeaders() { +template <class ELFT> void ELFDumper<ELFT>::printFileHeaders() { ELFDumperStyle->printFileHeaders(ObjF->getELFFile()); } -template<class ELFT> -void ELFDumper<ELFT>::printSectionHeaders() { +template <class ELFT> void ELFDumper<ELFT>::printSectionHeaders() { ELFDumperStyle->printSectionHeaders(ObjF->getELFFile()); } -template<class ELFT> -void ELFDumper<ELFT>::printRelocations() { +template <class ELFT> void ELFDumper<ELFT>::printRelocations() { ELFDumperStyle->printRelocations(ObjF->getELFFile()); } -template <class ELFT> void ELFDumper<ELFT>::printProgramHeaders() { - ELFDumperStyle->printProgramHeaders(ObjF->getELFFile()); +template <class ELFT> +void ELFDumper<ELFT>::printProgramHeaders( + bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) { + ELFDumperStyle->printProgramHeaders(ObjF->getELFFile(), PrintProgramHeaders, + PrintSectionMapping); +} + +template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() { + // Dump version symbol section. + ELFDumperStyle->printVersionSymbolSection(ObjF->getELFFile(), + SymbolVersionSection); + + // Dump version definition section. + ELFDumperStyle->printVersionDefinitionSection(ObjF->getELFFile(), + SymbolVersionDefSection); + + // Dump version dependency section. + ELFDumperStyle->printVersionDependencySection(ObjF->getELFFile(), + SymbolVersionNeedSection); } template <class ELFT> void ELFDumper<ELFT>::printDynamicRelocations() { ELFDumperStyle->printDynamicRelocations(ObjF->getELFFile()); } -template<class ELFT> -void ELFDumper<ELFT>::printSymbols() { - ELFDumperStyle->printSymbols(ObjF->getELFFile()); +template <class ELFT> +void ELFDumper<ELFT>::printSymbols(bool PrintSymbols, + bool PrintDynamicSymbols) { + ELFDumperStyle->printSymbols(ObjF->getELFFile(), PrintSymbols, + PrintDynamicSymbols); } -template<class ELFT> -void ELFDumper<ELFT>::printDynamicSymbols() { - ELFDumperStyle->printDynamicSymbols(ObjF->getELFFile()); +template <class ELFT> void ELFDumper<ELFT>::printHashSymbols() { + ELFDumperStyle->printHashSymbols(ObjF->getELFFile()); } template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() { @@ -1642,61 +1715,7 @@ template <class ELFT> void ELFDumper<ELFT>::printELFLinkerOptions() { ELFDumperStyle->printELFLinkerOptions(ObjF->getELFFile()); } -static const char *getTypeString(unsigned Arch, uint64_t Type) { -#define DYNAMIC_TAG(n, v) - switch (Arch) { - case EM_HEXAGON: - switch (Type) { -#define HEXAGON_DYNAMIC_TAG(name, value) \ - case DT_##name: \ - return #name; -#include "llvm/BinaryFormat/DynamicTags.def" -#undef HEXAGON_DYNAMIC_TAG - } - break; - - case EM_MIPS: - switch (Type) { -#define MIPS_DYNAMIC_TAG(name, value) \ - case DT_##name: \ - return #name; -#include "llvm/BinaryFormat/DynamicTags.def" -#undef MIPS_DYNAMIC_TAG - } - break; - - case EM_PPC64: - switch(Type) { -#define PPC64_DYNAMIC_TAG(name, value) \ - case DT_##name: \ - return #name; -#include "llvm/BinaryFormat/DynamicTags.def" -#undef PPC64_DYNAMIC_TAG - } - break; - } -#undef DYNAMIC_TAG - switch (Type) { -// Now handle all dynamic tags except the architecture specific ones -#define MIPS_DYNAMIC_TAG(name, value) -#define HEXAGON_DYNAMIC_TAG(name, value) -#define PPC64_DYNAMIC_TAG(name, value) -// Also ignore marker tags such as DT_HIOS (maps to DT_VERNEEDNUM), etc. -#define DYNAMIC_TAG_MARKER(name, value) -#define DYNAMIC_TAG(name, value) \ - case DT_##name: \ - return #name; -#include "llvm/BinaryFormat/DynamicTags.def" -#undef DYNAMIC_TAG -#undef MIPS_DYNAMIC_TAG -#undef HEXAGON_DYNAMIC_TAG -#undef PPC64_DYNAMIC_TAG -#undef DYNAMIC_TAG_MARKER - default: return "unknown"; - } -} - -#define LLVM_READOBJ_DT_FLAG_ENT(prefix, enum) \ +#define LLVM_READOBJ_DT_FLAG_ENT(prefix, enum) \ { #enum, prefix##_##enum } static const EnumEntry<unsigned> ElfDynamicDTFlags[] = { @@ -1724,6 +1743,7 @@ static const EnumEntry<unsigned> ElfDynamicDTFlags1[] = { LLVM_READOBJ_DT_FLAG_ENT(DF_1, CONFALT), LLVM_READOBJ_DT_FLAG_ENT(DF_1, ENDFILTEE), LLVM_READOBJ_DT_FLAG_ENT(DF_1, DISPRELDNE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, DISPRELPND), LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODIRECT), LLVM_READOBJ_DT_FLAG_ENT(DF_1, IGNMULDEF), LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOKSYMS), @@ -1776,20 +1796,97 @@ void printFlags(T Value, ArrayRef<EnumEntry<TFlag>> Flags, raw_ostream &OS) { } template <class ELFT> -StringRef ELFDumper<ELFT>::getDynamicString(uint64_t Value) const { - if (Value >= DynamicStringTable.size()) - reportError("Invalid dynamic string table reference"); - return StringRef(DynamicStringTable.data() + Value); -} - -static void printLibrary(raw_ostream &OS, const Twine &Tag, const Twine &Name) { - OS << Tag << ": [" << Name << "]"; -} +void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, + uint64_t Value) const { + const char *ConvChar = + (opts::Output == opts::GNU) ? "0x%" PRIx64 : "0x%" PRIX64; + + // Handle custom printing of architecture specific tags + switch (ObjF->getELFFile()->getHeader()->e_machine) { + case EM_AARCH64: + switch (Type) { + case DT_AARCH64_BTI_PLT: + case DT_AARCH64_PAC_PLT: + OS << Value; + return; + default: + break; + } + break; + case EM_HEXAGON: + switch (Type) { + case DT_HEXAGON_VER: + OS << Value; + return; + case DT_HEXAGON_SYMSZ: + case DT_HEXAGON_PLT: + OS << format(ConvChar, Value); + return; + default: + break; + } + break; + case EM_MIPS: + switch (Type) { + case DT_MIPS_RLD_VERSION: + case DT_MIPS_LOCAL_GOTNO: + case DT_MIPS_SYMTABNO: + case DT_MIPS_UNREFEXTNO: + OS << Value; + return; + case DT_MIPS_TIME_STAMP: + case DT_MIPS_ICHECKSUM: + case DT_MIPS_IVERSION: + case DT_MIPS_BASE_ADDRESS: + case DT_MIPS_MSYM: + case DT_MIPS_CONFLICT: + case DT_MIPS_LIBLIST: + case DT_MIPS_CONFLICTNO: + case DT_MIPS_LIBLISTNO: + case DT_MIPS_GOTSYM: + case DT_MIPS_HIPAGENO: + case DT_MIPS_RLD_MAP: + case DT_MIPS_DELTA_CLASS: + case DT_MIPS_DELTA_CLASS_NO: + case DT_MIPS_DELTA_INSTANCE: + case DT_MIPS_DELTA_RELOC: + case DT_MIPS_DELTA_RELOC_NO: + case DT_MIPS_DELTA_SYM: + case DT_MIPS_DELTA_SYM_NO: + case DT_MIPS_DELTA_CLASSSYM: + case DT_MIPS_DELTA_CLASSSYM_NO: + case DT_MIPS_CXX_FLAGS: + case DT_MIPS_PIXIE_INIT: + case DT_MIPS_SYMBOL_LIB: + case DT_MIPS_LOCALPAGE_GOTIDX: + case DT_MIPS_LOCAL_GOTIDX: + case DT_MIPS_HIDDEN_GOTIDX: + case DT_MIPS_PROTECTED_GOTIDX: + case DT_MIPS_OPTIONS: + case DT_MIPS_INTERFACE: + case DT_MIPS_DYNSTR_ALIGN: + case DT_MIPS_INTERFACE_SIZE: + case DT_MIPS_RLD_TEXT_RESOLVE_ADDR: + case DT_MIPS_PERF_SUFFIX: + case DT_MIPS_COMPACT_SIZE: + case DT_MIPS_GP_VALUE: + case DT_MIPS_AUX_DYNAMIC: + case DT_MIPS_PLTGOT: + case DT_MIPS_RWPLT: + case DT_MIPS_RLD_MAP_REL: + OS << format(ConvChar, Value); + return; + case DT_MIPS_FLAGS: + printFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags), OS); + return; + default: + break; + } + break; + default: + break; + } -template <class ELFT> -void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) { - raw_ostream &OS = W.getOStream(); - const char* ConvChar = (opts::Output == opts::GNU) ? "0x%" PRIx64 : "0x%" PRIX64; switch (Type) { case DT_PLTREL: if (Value == DT_REL) { @@ -1818,22 +1915,12 @@ void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) { case DT_VERSYM: case DT_GNU_HASH: case DT_NULL: - case DT_MIPS_BASE_ADDRESS: - case DT_MIPS_GOTSYM: - case DT_MIPS_RLD_MAP: - case DT_MIPS_RLD_MAP_REL: - case DT_MIPS_PLTGOT: - case DT_MIPS_OPTIONS: OS << format(ConvChar, Value); break; case DT_RELACOUNT: case DT_RELCOUNT: case DT_VERDEFNUM: case DT_VERNEEDNUM: - case DT_MIPS_RLD_VERSION: - case DT_MIPS_LOCAL_GOTNO: - case DT_MIPS_SYMTABNO: - case DT_MIPS_UNREFEXTNO: OS << Value; break; case DT_PLTRELSZ: @@ -1851,24 +1938,30 @@ void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) { OS << Value << " (bytes)"; break; case DT_NEEDED: - printLibrary(OS, "Shared library", getDynamicString(Value)); - break; case DT_SONAME: - printLibrary(OS, "Library soname", getDynamicString(Value)); - break; case DT_AUXILIARY: - printLibrary(OS, "Auxiliary library", getDynamicString(Value)); - break; + case DT_USED: case DT_FILTER: - printLibrary(OS, "Filter library", getDynamicString(Value)); - break; case DT_RPATH: - case DT_RUNPATH: - OS << getDynamicString(Value); - break; - case DT_MIPS_FLAGS: - printFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags), OS); + case DT_RUNPATH: { + const std::map<uint64_t, const char*> TagNames = { + {DT_NEEDED, "Shared library"}, + {DT_SONAME, "Library soname"}, + {DT_AUXILIARY, "Auxiliary library"}, + {DT_USED, "Not needed object"}, + {DT_FILTER, "Filter library"}, + {DT_RPATH, "Library rpath"}, + {DT_RUNPATH, "Library runpath"}, + }; + OS << TagNames.at(Type) << ": "; + if (DynamicStringTable.empty()) + OS << "<String table is empty or was not found> "; + else if (Value < DynamicStringTable.size()) + OS << "[" << StringRef(DynamicStringTable.data() + Value) << "]"; + else + OS << "<Invalid offset 0x" << utohexstr(Value) << ">"; break; + } case DT_FLAGS: printFlags(Value, makeArrayRef(ElfDynamicDTFlags), OS); break; @@ -1881,14 +1974,9 @@ void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) { } } -template<class ELFT> -void ELFDumper<ELFT>::printUnwindInfo() { - const unsigned Machine = ObjF->getELFFile()->getHeader()->e_machine; - if (Machine == EM_386 || Machine == EM_X86_64) { - DwarfCFIEH::PrinterContext<ELFT> Ctx(W, ObjF); - return Ctx.printUnwindInformation(); - } - W.startLine() << "UnwindInfo not implemented.\n"; +template <class ELFT> void ELFDumper<ELFT>::printUnwindInfo() { + DwarfCFIEH::PrinterContext<ELFT> Ctx(W, ObjF); + Ctx.printUnwindInformation(); } namespace { @@ -1898,73 +1986,40 @@ template <> void ELFDumper<ELF32LE>::printUnwindInfo() { const unsigned Machine = Obj->getHeader()->e_machine; if (Machine == EM_ARM) { ARM::EHABI::PrinterContext<ELF32LE> Ctx(W, Obj, DotSymtabSec); - return Ctx.PrintUnwindInformation(); + Ctx.PrintUnwindInformation(); } - W.startLine() << "UnwindInfo not implemented.\n"; + DwarfCFIEH::PrinterContext<ELF32LE> Ctx(W, ObjF); + Ctx.printUnwindInformation(); } } // end anonymous namespace -template<class ELFT> -void ELFDumper<ELFT>::printDynamicTable() { - auto I = dynamic_table().begin(); - auto E = dynamic_table().end(); - - if (I == E) - return; - - --E; - while (I != E && E->getTag() == ELF::DT_NULL) - --E; - if (E->getTag() != ELF::DT_NULL) - ++E; - ++E; - - ptrdiff_t Total = std::distance(I, E); - if (Total == 0) - return; - - raw_ostream &OS = W.getOStream(); - W.startLine() << "DynamicSection [ (" << Total << " entries)\n"; - - bool Is64 = ELFT::Is64Bits; - - W.startLine() - << " Tag" << (Is64 ? " " : " ") << "Type" - << " " << "Name/Value\n"; - while (I != E) { - const Elf_Dyn &Entry = *I; - uintX_t Tag = Entry.getTag(); - ++I; - W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, opts::Output != opts::GNU) << " " - << format("%-21s", getTypeString(ObjF->getELFFile()->getHeader()->e_machine, Tag)); - printValue(Tag, Entry.getVal()); - OS << "\n"; - } - - W.startLine() << "]\n"; +template <class ELFT> void ELFDumper<ELFT>::printDynamicTable() { + ELFDumperStyle->printDynamic(ObjF->getELFFile()); } -template<class ELFT> -void ELFDumper<ELFT>::printNeededLibraries() { +template <class ELFT> void ELFDumper<ELFT>::printNeededLibraries() { ListScope D(W, "NeededLibraries"); using LibsTy = std::vector<StringRef>; LibsTy Libs; for (const auto &Entry : dynamic_table()) - if (Entry.d_tag == ELF::DT_NEEDED) - Libs.push_back(getDynamicString(Entry.d_un.d_val)); + if (Entry.d_tag == ELF::DT_NEEDED) { + uint64_t Value = Entry.d_un.d_val; + if (Value < DynamicStringTable.size()) + Libs.push_back(StringRef(DynamicStringTable.data() + Value)); + else + Libs.push_back("<Library name index out of range>"); + } - std::stable_sort(Libs.begin(), Libs.end()); + llvm::stable_sort(Libs); for (const auto &L : Libs) - W.startLine() << L << "\n"; + W.startLine() << L << "\n"; } - -template <typename ELFT> -void ELFDumper<ELFT>::printHashTable() { +template <typename ELFT> void ELFDumper<ELFT>::printHashTable() { DictScope D(W, "HashTable"); if (!HashTable) return; @@ -1974,8 +2029,7 @@ void ELFDumper<ELFT>::printHashTable() { W.printList("Chains", HashTable->chains()); } -template <typename ELFT> -void ELFDumper<ELFT>::printGnuHashTable() { +template <typename ELFT> void ELFDumper<ELFT>::printGnuHashTable() { DictScope D(W, "GnuHashTable"); if (!GnuHashTable) return; @@ -1996,8 +2050,7 @@ template <typename ELFT> void ELFDumper<ELFT>::printLoadName() { W.printString("LoadName", SOName); } -template <class ELFT> -void ELFDumper<ELFT>::printAttributes() { +template <class ELFT> void ELFDumper<ELFT>::printAttributes() { W.startLine() << "Attributes not implemented.\n"; } @@ -2486,7 +2539,7 @@ template <class ELFT> void ELFDumper<ELFT>::printStackMap() const { unwrapOrError(Obj->getSectionContents(StackMapSection)); prettyPrintStackMap( - W, StackMapV2Parser<ELFT::TargetEndianness>(StackMapContentsArray)); + W, StackMapParser<ELFT::TargetEndianness>(StackMapContentsArray)); } template <class ELFT> void ELFDumper<ELFT>::printGroupSections() { @@ -2527,7 +2580,8 @@ static std::string getSectionHeaderTableIndexString(const ELFFile<ELFT> *Obj) { ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(Obj->sections()); if (Arr.empty()) return "65535 (corrupt: out of range)"; - return to_string(ElfHeader->e_shstrndx) + " (" + to_string(Arr[0].sh_link) + ")"; + return to_string(ElfHeader->e_shstrndx) + " (" + to_string(Arr[0].sh_link) + + ")"; } template <class ELFT> void GNUStyle<ELFT>::printFileHeaders(const ELFO *Obj) { @@ -2599,7 +2653,7 @@ struct GroupMember { struct GroupSection { StringRef Name; - StringRef Signature; + std::string Signature; uint64_t ShName; uint64_t Index; uint32_t Link; @@ -2630,13 +2684,13 @@ std::vector<GroupSection> getGroups(const ELFFile<ELFT> *Obj) { StringRef Name = unwrapOrError(Obj->getSectionName(&Sec)); StringRef Signature = StrTable.data() + Sym->st_name; - Ret.push_back({Name, - Signature, - Sec.sh_name, + Ret.push_back({Name, + maybeDemangle(Signature), + Sec.sh_name, I - 1, Sec.sh_link, Sec.sh_info, - Data[0], + Data[0], {}}); std::vector<GroupMember> &GM = Ret.back().Members; @@ -2691,53 +2745,57 @@ template <class ELFT> void GNUStyle<ELFT>::printGroupSections(const ELFO *Obj) { template <class ELFT> void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab, const Elf_Rela &R, bool IsRela) { - std::string Offset, Info, Addend, Value; - SmallString<32> RelocName; - StringRef TargetName; - const Elf_Sym *Sym = nullptr; - unsigned Width = ELFT::Is64Bits ? 16 : 8; - unsigned Bias = ELFT::Is64Bits ? 8 : 0; - - // First two fields are bit width dependent. The rest of them are after are - // fixed width. - Field Fields[5] = {0, 10 + Bias, 19 + 2 * Bias, 42 + 2 * Bias, 53 + 2 * Bias}; - Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName); - Sym = unwrapOrError(Obj->getRelocationSymbol(&R, SymTab)); + const Elf_Sym *Sym = unwrapOrError(Obj->getRelocationSymbol(&R, SymTab)); + std::string TargetName; if (Sym && Sym->getType() == ELF::STT_SECTION) { const Elf_Shdr *Sec = unwrapOrError( Obj->getSection(Sym, SymTab, this->dumper()->getShndxTable())); TargetName = unwrapOrError(Obj->getSectionName(Sec)); } else if (Sym) { StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTab)); - TargetName = unwrapOrError(Sym->getName(StrTable)); + TargetName = this->dumper()->getFullSymbolName( + Sym, StrTable, SymTab->sh_type == SHT_DYNSYM /* IsDynamic */); } + printRelocation(Obj, Sym, TargetName, R, IsRela); +} - if (Sym && IsRela) { - if (R.r_addend < 0) - Addend = " - "; - else - Addend = " + "; - } +template <class ELFT> +void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Sym *Sym, + StringRef SymbolName, const Elf_Rela &R, + bool IsRela) { + // First two fields are bit width dependent. The rest of them are fixed width. + unsigned Bias = ELFT::Is64Bits ? 8 : 0; + Field Fields[5] = {0, 10 + Bias, 19 + 2 * Bias, 42 + 2 * Bias, 53 + 2 * Bias}; + unsigned Width = ELFT::Is64Bits ? 16 : 8; - Offset = to_string(format_hex_no_prefix(R.r_offset, Width)); - Info = to_string(format_hex_no_prefix(R.r_info, Width)); + Fields[0].Str = to_string(format_hex_no_prefix(R.r_offset, Width)); + Fields[1].Str = to_string(format_hex_no_prefix(R.r_info, Width)); - int64_t RelAddend = R.r_addend; - if (IsRela) - Addend += to_hexString(std::abs(RelAddend), false); - - if (Sym) - Value = to_string(format_hex_no_prefix(Sym->getValue(), Width)); - - Fields[0].Str = Offset; - Fields[1].Str = Info; - Fields[2].Str = RelocName; - Fields[3].Str = Value; - Fields[4].Str = TargetName; - for (auto &field : Fields) - printField(field); - OS << Addend; - OS << "\n"; + SmallString<32> RelocName; + Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName); + Fields[2].Str = RelocName.c_str(); + + if (Sym && (!SymbolName.empty() || Sym->getValue() != 0)) + Fields[3].Str = to_string(format_hex_no_prefix(Sym->getValue(), Width)); + + Fields[4].Str = SymbolName; + for (const Field &F : Fields) + printField(F); + + std::string Addend; + if (IsRela) { + int64_t RelAddend = R.r_addend; + if (!SymbolName.empty()) { + if (R.r_addend < 0) { + Addend = " - "; + RelAddend = std::abs(RelAddend); + } else + Addend = " + "; + } + + Addend += to_hexString(RelAddend, false); + } + OS << Addend << "\n"; } template <class ELFT> void GNUStyle<ELFT>::printRelocHeader(unsigned SType) { @@ -2764,10 +2822,8 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocHeader(unsigned SType) { template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) { bool HasRelocSections = false; for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { - if (Sec.sh_type != ELF::SHT_REL && - Sec.sh_type != ELF::SHT_RELA && - Sec.sh_type != ELF::SHT_RELR && - Sec.sh_type != ELF::SHT_ANDROID_REL && + if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA && + Sec.sh_type != ELF::SHT_RELR && Sec.sh_type != ELF::SHT_ANDROID_REL && Sec.sh_type != ELF::SHT_ANDROID_RELA && Sec.sh_type != ELF::SHT_ANDROID_RELR) continue; @@ -2832,7 +2888,21 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) { OS << "\nThere are no relocations in this file.\n"; } -std::string getSectionTypeString(unsigned Arch, unsigned Type) { +// Print the offset of a particular section from anyone of the ranges: +// [SHT_LOOS, SHT_HIOS], [SHT_LOPROC, SHT_HIPROC], [SHT_LOUSER, SHT_HIUSER]. +// If 'Type' does not fall within any of those ranges, then a string is +// returned as '<unknown>' followed by the type value. +static std::string getSectionTypeOffsetString(unsigned Type) { + if (Type >= SHT_LOOS && Type <= SHT_HIOS) + return "LOOS+0x" + to_hexString(Type - SHT_LOOS); + else if (Type >= SHT_LOPROC && Type <= SHT_HIPROC) + return "LOPROC+0x" + to_hexString(Type - SHT_LOPROC); + else if (Type >= SHT_LOUSER && Type <= SHT_HIUSER) + return "LOUSER+0x" + to_hexString(Type - SHT_LOUSER); + return "0x" + to_hexString(Type) + ": <unknown>"; +} + +static std::string getSectionTypeString(unsigned Arch, unsigned Type) { using namespace ELF; switch (Arch) { @@ -2863,10 +2933,10 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) { return "MIPS_REGINFO"; case SHT_MIPS_OPTIONS: return "MIPS_OPTIONS"; + case SHT_MIPS_DWARF: + return "MIPS_DWARF"; case SHT_MIPS_ABIFLAGS: return "MIPS_ABIFLAGS"; - case SHT_MIPS_DWARF: - return "SHT_MIPS_DWARF"; } break; } @@ -2905,6 +2975,10 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) { return "GROUP"; case SHT_SYMTAB_SHNDX: return "SYMTAB SECTION INDICES"; + case SHT_ANDROID_REL: + return "ANDROID_REL"; + case SHT_ANDROID_RELA: + return "ANDROID_RELA"; case SHT_RELR: case SHT_ANDROID_RELR: return "RELR"; @@ -2916,6 +2990,8 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) { return "LLVM_CALL_GRAPH_PROFILE"; case SHT_LLVM_ADDRSIG: return "LLVM_ADDRSIG"; + case SHT_LLVM_DEPENDENT_LIBRARIES: + return "LLVM_DEPENDENT_LIBRARIES"; // FIXME: Parse processor specific GNU attributes case SHT_GNU_ATTRIBUTES: return "ATTRIBUTES"; @@ -2928,69 +3004,65 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) { case SHT_GNU_versym: return "VERSYM"; default: - return ""; + return getSectionTypeOffsetString(Type); } return ""; } template <class ELFT> -void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { - size_t SectionIndex = 0; - std::string Number, Type, Size, Address, Offset, Flags, Link, Info, EntrySize, - Alignment; - unsigned Bias; - unsigned Width; - - if (ELFT::Is64Bits) { - Bias = 0; - Width = 16; - } else { - Bias = 8; - Width = 8; - } +static StringRef getSectionName(const typename ELFT::Shdr &Sec, + const ELFObjectFile<ELFT> &ElfObj, + ArrayRef<typename ELFT::Shdr> Sections) { + const ELFFile<ELFT> &Obj = *ElfObj.getELFFile(); + uint32_t Index = Obj.getHeader()->e_shstrndx; + if (Index == ELF::SHN_XINDEX) + Index = Sections[0].sh_link; + if (!Index) // no section string table. + return ""; + // TODO: Test a case when the sh_link of the section with index 0 is broken. + if (Index >= Sections.size()) + reportError(ElfObj.getFileName(), + createError("section header string table index " + + Twine(Index) + " does not exist")); + StringRef Data = toStringRef(unwrapOrError( + Obj.template getSectionContentsAsArray<uint8_t>(&Sections[Index]))); + return unwrapOrError(Obj.getSectionName(&Sec, Data)); +} +template <class ELFT> +void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { + unsigned Bias = ELFT::Is64Bits ? 0 : 8; ArrayRef<Elf_Shdr> Sections = unwrapOrError(Obj->sections()); OS << "There are " << to_string(Sections.size()) << " section headers, starting at offset " << "0x" << to_hexString(Obj->getHeader()->e_shoff, false) << ":\n\n"; OS << "Section Headers:\n"; - Field Fields[11] = {{"[Nr]", 2}, - {"Name", 7}, - {"Type", 25}, - {"Address", 41}, - {"Off", 58 - Bias}, - {"Size", 65 - Bias}, - {"ES", 72 - Bias}, - {"Flg", 75 - Bias}, - {"Lk", 79 - Bias}, - {"Inf", 82 - Bias}, - {"Al", 86 - Bias}}; - for (auto &f : Fields) - printField(f); + Field Fields[11] = { + {"[Nr]", 2}, {"Name", 7}, {"Type", 25}, + {"Address", 41}, {"Off", 58 - Bias}, {"Size", 65 - Bias}, + {"ES", 72 - Bias}, {"Flg", 75 - Bias}, {"Lk", 79 - Bias}, + {"Inf", 82 - Bias}, {"Al", 86 - Bias}}; + for (auto &F : Fields) + printField(F); OS << "\n"; + const ELFObjectFile<ELFT> *ElfObj = this->dumper()->getElfObject(); + size_t SectionIndex = 0; for (const Elf_Shdr &Sec : Sections) { - Number = to_string(SectionIndex); - Fields[0].Str = Number; - Fields[1].Str = unwrapOrError(Obj->getSectionName(&Sec)); - Type = getSectionTypeString(Obj->getHeader()->e_machine, Sec.sh_type); - Fields[2].Str = Type; - Address = to_string(format_hex_no_prefix(Sec.sh_addr, Width)); - Fields[3].Str = Address; - Offset = to_string(format_hex_no_prefix(Sec.sh_offset, 6)); - Fields[4].Str = Offset; - Size = to_string(format_hex_no_prefix(Sec.sh_size, 6)); - Fields[5].Str = Size; - EntrySize = to_string(format_hex_no_prefix(Sec.sh_entsize, 2)); - Fields[6].Str = EntrySize; - Flags = getGNUFlags(Sec.sh_flags); - Fields[7].Str = Flags; - Link = to_string(Sec.sh_link); - Fields[8].Str = Link; - Info = to_string(Sec.sh_info); - Fields[9].Str = Info; - Alignment = to_string(Sec.sh_addralign); - Fields[10].Str = Alignment; + Fields[0].Str = to_string(SectionIndex); + Fields[1].Str = getSectionName(Sec, *ElfObj, Sections); + Fields[2].Str = + getSectionTypeString(Obj->getHeader()->e_machine, Sec.sh_type); + Fields[3].Str = + to_string(format_hex_no_prefix(Sec.sh_addr, ELFT::Is64Bits ? 16 : 8)); + Fields[4].Str = to_string(format_hex_no_prefix(Sec.sh_offset, 6)); + Fields[5].Str = to_string(format_hex_no_prefix(Sec.sh_size, 6)); + Fields[6].Str = to_string(format_hex_no_prefix(Sec.sh_entsize, 2)); + Fields[7].Str = getGNUFlags(Sec.sh_flags); + Fields[8].Str = to_string(Sec.sh_link); + Fields[9].Str = to_string(Sec.sh_info); + Fields[10].Str = to_string(Sec.sh_addralign); + OS.PadToColumn(Fields[0].Column); OS << "[" << right_justify(Fields[0].Str, 2) << "]"; for (int i = 1; i < 7; i++) @@ -3043,9 +3115,10 @@ std::string GNUStyle<ELFT>::getSymbolSectionNdx(const ELFO *Obj, case ELF::SHN_COMMON: return "COM"; case ELF::SHN_XINDEX: - SectionIndex = unwrapOrError(object::getExtendedSymbolTableIndex<ELFT>( - Symbol, FirstSym, this->dumper()->getShndxTable())); - LLVM_FALLTHROUGH; + return to_string( + format_decimal(unwrapOrError(object::getExtendedSymbolTableIndex<ELFT>( + Symbol, FirstSym, this->dumper()->getShndxTable())), + 3)); default: // Find if: // Processor specific @@ -3072,7 +3145,6 @@ void GNUStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, bool IsDynamic) { static int Idx = 0; static bool Dynamic = true; - size_t Width; // If this function was called with a different value from IsDynamic // from last call, happens when we move from dynamic to static symbol @@ -3081,111 +3153,87 @@ void GNUStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, Idx = 0; Dynamic = false; } - std::string Num, Name, Value, Size, Binding, Type, Visibility, Section; - unsigned Bias = 0; - if (ELFT::Is64Bits) { - Bias = 8; - Width = 16; - } else { - Bias = 0; - Width = 8; - } + + unsigned Bias = ELFT::Is64Bits ? 8 : 0; Field Fields[8] = {0, 8, 17 + Bias, 23 + Bias, 31 + Bias, 38 + Bias, 47 + Bias, 51 + Bias}; - Num = to_string(format_decimal(Idx++, 6)) + ":"; - Value = to_string(format_hex_no_prefix(Symbol->st_value, Width)); - Size = to_string(format_decimal(Symbol->st_size, 5)); + Fields[0].Str = to_string(format_decimal(Idx++, 6)) + ":"; + Fields[1].Str = to_string( + format_hex_no_prefix(Symbol->st_value, ELFT::Is64Bits ? 16 : 8)); + Fields[2].Str = to_string(format_decimal(Symbol->st_size, 5)); + unsigned char SymbolType = Symbol->getType(); if (Obj->getHeader()->e_machine == ELF::EM_AMDGPU && SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS) - Type = printEnum(SymbolType, makeArrayRef(AMDGPUSymbolTypes)); + Fields[3].Str = printEnum(SymbolType, makeArrayRef(AMDGPUSymbolTypes)); else - Type = printEnum(SymbolType, makeArrayRef(ElfSymbolTypes)); - unsigned Vis = Symbol->getVisibility(); - Binding = printEnum(Symbol->getBinding(), makeArrayRef(ElfSymbolBindings)); - Visibility = printEnum(Vis, makeArrayRef(ElfSymbolVisibilities)); - Section = getSymbolSectionNdx(Obj, Symbol, FirstSym); - Name = this->dumper()->getFullSymbolName(Symbol, StrTable, IsDynamic); - Fields[0].Str = Num; - Fields[1].Str = Value; - Fields[2].Str = Size; - Fields[3].Str = Type; - Fields[4].Str = Binding; - Fields[5].Str = Visibility; - Fields[6].Str = Section; - Fields[7].Str = Name; + Fields[3].Str = printEnum(SymbolType, makeArrayRef(ElfSymbolTypes)); + + Fields[4].Str = + printEnum(Symbol->getBinding(), makeArrayRef(ElfSymbolBindings)); + Fields[5].Str = + printEnum(Symbol->getVisibility(), makeArrayRef(ElfSymbolVisibilities)); + Fields[6].Str = getSymbolSectionNdx(Obj, Symbol, FirstSym); + Fields[7].Str = + this->dumper()->getFullSymbolName(Symbol, StrTable, IsDynamic); for (auto &Entry : Fields) printField(Entry); OS << "\n"; } + template <class ELFT> void GNUStyle<ELFT>::printHashedSymbol(const ELFO *Obj, const Elf_Sym *FirstSym, uint32_t Sym, StringRef StrTable, uint32_t Bucket) { - std::string Num, Buc, Name, Value, Size, Binding, Type, Visibility, Section; - unsigned Width, Bias = 0; - if (ELFT::Is64Bits) { - Bias = 8; - Width = 16; - } else { - Bias = 0; - Width = 8; - } + unsigned Bias = ELFT::Is64Bits ? 8 : 0; Field Fields[9] = {0, 6, 11, 20 + Bias, 25 + Bias, 34 + Bias, 41 + Bias, 49 + Bias, 53 + Bias}; - Num = to_string(format_decimal(Sym, 5)); - Buc = to_string(format_decimal(Bucket, 3)) + ":"; + Fields[0].Str = to_string(format_decimal(Sym, 5)); + Fields[1].Str = to_string(format_decimal(Bucket, 3)) + ":"; const auto Symbol = FirstSym + Sym; - Value = to_string(format_hex_no_prefix(Symbol->st_value, Width)); - Size = to_string(format_decimal(Symbol->st_size, 5)); + Fields[2].Str = to_string( + format_hex_no_prefix(Symbol->st_value, ELFT::Is64Bits ? 18 : 8)); + Fields[3].Str = to_string(format_decimal(Symbol->st_size, 5)); + unsigned char SymbolType = Symbol->getType(); if (Obj->getHeader()->e_machine == ELF::EM_AMDGPU && SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS) - Type = printEnum(SymbolType, makeArrayRef(AMDGPUSymbolTypes)); + Fields[4].Str = printEnum(SymbolType, makeArrayRef(AMDGPUSymbolTypes)); else - Type = printEnum(SymbolType, makeArrayRef(ElfSymbolTypes)); - unsigned Vis = Symbol->getVisibility(); - Binding = printEnum(Symbol->getBinding(), makeArrayRef(ElfSymbolBindings)); - Visibility = printEnum(Vis, makeArrayRef(ElfSymbolVisibilities)); - Section = getSymbolSectionNdx(Obj, Symbol, FirstSym); - Name = this->dumper()->getFullSymbolName(Symbol, StrTable, true); - Fields[0].Str = Num; - Fields[1].Str = Buc; - Fields[2].Str = Value; - Fields[3].Str = Size; - Fields[4].Str = Type; - Fields[5].Str = Binding; - Fields[6].Str = Visibility; - Fields[7].Str = Section; - Fields[8].Str = Name; + Fields[4].Str = printEnum(SymbolType, makeArrayRef(ElfSymbolTypes)); + + Fields[5].Str = + printEnum(Symbol->getBinding(), makeArrayRef(ElfSymbolBindings)); + Fields[6].Str = + printEnum(Symbol->getVisibility(), makeArrayRef(ElfSymbolVisibilities)); + Fields[7].Str = getSymbolSectionNdx(Obj, Symbol, FirstSym); + Fields[8].Str = this->dumper()->getFullSymbolName(Symbol, StrTable, true); + for (auto &Entry : Fields) printField(Entry); OS << "\n"; } -template <class ELFT> void GNUStyle<ELFT>::printSymbols(const ELFO *Obj) { - if (opts::DynamicSymbols) +template <class ELFT> +void GNUStyle<ELFT>::printSymbols(const ELFO *Obj, bool PrintSymbols, + bool PrintDynamicSymbols) { + if (!PrintSymbols && !PrintDynamicSymbols) return; + // GNU readelf prints both the .dynsym and .symtab with --symbols. this->dumper()->printSymbolsHelper(true); - this->dumper()->printSymbolsHelper(false); + if (PrintSymbols) + this->dumper()->printSymbolsHelper(false); } -template <class ELFT> -void GNUStyle<ELFT>::printDynamicSymbols(const ELFO *Obj) { +template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) { if (this->dumper()->getDynamicStringTable().empty()) return; auto StringTable = this->dumper()->getDynamicStringTable(); auto DynSyms = this->dumper()->dynamic_symbols(); - auto GnuHash = this->dumper()->getGnuHashTable(); - auto SysVHash = this->dumper()->getHashTable(); - - // If no hash or .gnu.hash found, try using symbol table - if (GnuHash == nullptr && SysVHash == nullptr) - this->dumper()->printSymbolsHelper(true); // Try printing .hash - if (this->dumper()->getHashTable()) { + if (auto SysVHash = this->dumper()->getHashTable()) { OS << "\n Symbol table of .hash for image:\n"; if (ELFT::Is64Bits) OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; @@ -3193,14 +3241,12 @@ void GNUStyle<ELFT>::printDynamicSymbols(const ELFO *Obj) { OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; OS << "\n"; - uint32_t NBuckets = SysVHash->nbucket; - uint32_t NChains = SysVHash->nchain; auto Buckets = SysVHash->buckets(); auto Chains = SysVHash->chains(); - for (uint32_t Buc = 0; Buc < NBuckets; Buc++) { + for (uint32_t Buc = 0; Buc < SysVHash->nbucket; Buc++) { if (Buckets[Buc] == ELF::STN_UNDEF) continue; - for (uint32_t Ch = Buckets[Buc]; Ch < NChains; Ch = Chains[Ch]) { + for (uint32_t Ch = Buckets[Buc]; Ch < SysVHash->nchain; Ch = Chains[Ch]) { if (Ch == ELF::STN_UNDEF) break; printHashedSymbol(Obj, &DynSyms[0], Ch, StringTable, Buc); @@ -3209,16 +3255,15 @@ void GNUStyle<ELFT>::printDynamicSymbols(const ELFO *Obj) { } // Try printing .gnu.hash - if (GnuHash) { + if (auto GnuHash = this->dumper()->getGnuHashTable()) { OS << "\n Symbol table of .gnu.hash for image:\n"; if (ELFT::Is64Bits) OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; else OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; OS << "\n"; - uint32_t NBuckets = GnuHash->nbuckets; auto Buckets = GnuHash->buckets(); - for (uint32_t Buc = 0; Buc < NBuckets; Buc++) { + for (uint32_t Buc = 0; Buc < GnuHash->nbuckets; Buc++) { if (Buckets[Buc] == ELF::STN_UNDEF) continue; uint32_t Index = Buckets[Buc]; @@ -3266,8 +3311,8 @@ bool GNUStyle<ELFT>::checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { (IsSpecial && Phdr.p_type != ELF::PT_TLS) ? 0 : Sec.sh_size; if (Sec.sh_offset >= Phdr.p_offset) return ((Sec.sh_offset + SectionSize <= Phdr.p_filesz + Phdr.p_offset) - /*only non-zero sized sections at end*/ && - (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz)); + /*only non-zero sized sections at end*/ + && (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz)); return false; } @@ -3302,12 +3347,21 @@ bool GNUStyle<ELFT>::checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { } template <class ELFT> +void GNUStyle<ELFT>::printProgramHeaders( + const ELFO *Obj, bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) { + if (PrintProgramHeaders) + printProgramHeaders(Obj); + + // Display the section mapping along with the program headers, unless + // -section-mapping is explicitly set to false. + if (PrintSectionMapping != cl::BOU_FALSE) + printSectionMapping(Obj); +} + +template <class ELFT> void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { unsigned Bias = ELFT::Is64Bits ? 8 : 0; - unsigned Width = ELFT::Is64Bits ? 18 : 10; - unsigned SizeWidth = ELFT::Is64Bits ? 8 : 7; - std::string Type, Offset, VMA, LMA, FileSz, MemSz, Flag, Align; - const Elf_Ehdr *Header = Obj->getHeader(); Field Fields[8] = {2, 17, 26, 37 + Bias, 48 + Bias, 56 + Bias, 64 + Bias, 68 + Bias}; @@ -3323,23 +3377,18 @@ void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { else OS << " Type Offset VirtAddr PhysAddr FileSiz " << "MemSiz Flg Align\n"; + + unsigned Width = ELFT::Is64Bits ? 18 : 10; + unsigned SizeWidth = ELFT::Is64Bits ? 8 : 7; for (const auto &Phdr : unwrapOrError(Obj->program_headers())) { - Type = getElfPtType(Header->e_machine, Phdr.p_type); - Offset = to_string(format_hex(Phdr.p_offset, 8)); - VMA = to_string(format_hex(Phdr.p_vaddr, Width)); - LMA = to_string(format_hex(Phdr.p_paddr, Width)); - FileSz = to_string(format_hex(Phdr.p_filesz, SizeWidth)); - MemSz = to_string(format_hex(Phdr.p_memsz, SizeWidth)); - Flag = printPhdrFlags(Phdr.p_flags); - Align = to_string(format_hex(Phdr.p_align, 1)); - Fields[0].Str = Type; - Fields[1].Str = Offset; - Fields[2].Str = VMA; - Fields[3].Str = LMA; - Fields[4].Str = FileSz; - Fields[5].Str = MemSz; - Fields[6].Str = Flag; - Fields[7].Str = Align; + Fields[0].Str = getElfPtType(Header->e_machine, Phdr.p_type); + Fields[1].Str = to_string(format_hex(Phdr.p_offset, 8)); + Fields[2].Str = to_string(format_hex(Phdr.p_vaddr, Width)); + Fields[3].Str = to_string(format_hex(Phdr.p_paddr, Width)); + Fields[4].Str = to_string(format_hex(Phdr.p_filesz, SizeWidth)); + Fields[5].Str = to_string(format_hex(Phdr.p_memsz, SizeWidth)); + Fields[6].Str = printPhdrFlags(Phdr.p_flags); + Fields[7].Str = to_string(format_hex(Phdr.p_align, 1)); for (auto Field : Fields) printField(Field); if (Phdr.p_type == ELF::PT_INTERP) { @@ -3348,7 +3397,12 @@ void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { } OS << "\n"; } +} + +template <class ELFT> +void GNUStyle<ELFT>::printSectionMapping(const ELFO *Obj) { OS << "\n Section to Segment mapping:\n Segment Sections...\n"; + DenseSet<const Elf_Shdr *> BelongsToSegment; int Phnum = 0; for (const Elf_Phdr &Phdr : unwrapOrError(Obj->program_headers())) { std::string Sections; @@ -3363,58 +3417,66 @@ void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { Phdr.p_type != ELF::PT_TLS; if (!TbssInNonTLS && checkTLSSections(Phdr, Sec) && checkoffsets(Phdr, Sec) && checkVMA(Phdr, Sec) && - checkPTDynamic(Phdr, Sec) && (Sec.sh_type != ELF::SHT_NULL)) + checkPTDynamic(Phdr, Sec) && (Sec.sh_type != ELF::SHT_NULL)) { Sections += unwrapOrError(Obj->getSectionName(&Sec)).str() + " "; + BelongsToSegment.insert(&Sec); + } } OS << Sections << "\n"; OS.flush(); } + + // Display sections that do not belong to a segment. + std::string Sections; + for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { + if (BelongsToSegment.find(&Sec) == BelongsToSegment.end()) + Sections += unwrapOrError(Obj->getSectionName(&Sec)).str() + ' '; + } + if (!Sections.empty()) { + OS << " None " << Sections << '\n'; + OS.flush(); + } } template <class ELFT> void GNUStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela R, bool IsRela) { - SmallString<32> RelocName; - StringRef SymbolName; - unsigned Width = ELFT::Is64Bits ? 16 : 8; - unsigned Bias = ELFT::Is64Bits ? 8 : 0; - // First two fields are bit width dependent. The rest of them are after are - // fixed width. - Field Fields[5] = {0, 10 + Bias, 19 + 2 * Bias, 42 + 2 * Bias, 53 + 2 * Bias}; - uint32_t SymIndex = R.getSymbol(Obj->isMips64EL()); const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex; - Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName); - SymbolName = - unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable())); - std::string Addend, Info, Offset, Value; - Offset = to_string(format_hex_no_prefix(R.r_offset, Width)); - Info = to_string(format_hex_no_prefix(R.r_info, Width)); - Value = to_string(format_hex_no_prefix(Sym->getValue(), Width)); - int64_t RelAddend = R.r_addend; - if (!SymbolName.empty() && IsRela) { - if (R.r_addend < 0) - Addend = " - "; - else - Addend = " + "; - } + std::string SymbolName = maybeDemangle( + unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable()))); + printRelocation(Obj, Sym, SymbolName, R, IsRela); +} - if (SymbolName.empty() && Sym->getValue() == 0) - Value = ""; +template <class ELFT> void GNUStyle<ELFT>::printDynamic(const ELFO *Obj) { + Elf_Dyn_Range Table = this->dumper()->dynamic_table(); + if (Table.empty()) + return; - if (IsRela) - Addend += to_string(format_hex_no_prefix(std::abs(RelAddend), 1)); + const DynRegionInfo &DynamicTableRegion = + this->dumper()->getDynamicTableRegion(); + OS << "Dynamic section at offset " + << format_hex(reinterpret_cast<const uint8_t *>(DynamicTableRegion.Addr) - + Obj->base(), + 1) + << " contains " << Table.size() << " entries:\n"; - Fields[0].Str = Offset; - Fields[1].Str = Info; - Fields[2].Str = RelocName.c_str(); - Fields[3].Str = Value; - Fields[4].Str = SymbolName; - for (auto &Field : Fields) - printField(Field); - OS << Addend; - OS << "\n"; + bool Is64 = ELFT::Is64Bits; + if (Is64) + OS << " Tag Type Name/Value\n"; + else + OS << " Tag Type Name/Value\n"; + for (auto Entry : Table) { + uintX_t Tag = Entry.getTag(); + std::string TypeString = std::string("(") + + getTypeString(Obj->getHeader()->e_machine, Tag) + + ")"; + OS << " " << format_hex(Tag, Is64 ? 18 : 10) + << format(" %-20s ", TypeString.c_str()); + this->dumper()->printDynamicEntry(OS, Tag, Entry.getVal()); + OS << "\n"; + } } template <class ELFT> @@ -3427,7 +3489,8 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { OS << "\n'RELA' relocation section at offset " << format_hex(reinterpret_cast<const uint8_t *>(DynRelaRegion.Addr) - Obj->base(), - 1) << " contains " << DynRelaRegion.Size << " bytes:\n"; + 1) + << " contains " << DynRelaRegion.Size << " bytes:\n"; printRelocHeader(ELF::SHT_RELA); for (const Elf_Rela &Rela : this->dumper()->dyn_relas()) printDynamicRelocation(Obj, Rela, true); @@ -3436,7 +3499,8 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { OS << "\n'REL' relocation section at offset " << format_hex(reinterpret_cast<const uint8_t *>(DynRelRegion.Addr) - Obj->base(), - 1) << " contains " << DynRelRegion.Size << " bytes:\n"; + 1) + << " contains " << DynRelRegion.Size << " bytes:\n"; printRelocHeader(ELF::SHT_REL); for (const Elf_Rel &Rel : this->dumper()->dyn_rels()) { Elf_Rela Rela; @@ -3450,7 +3514,8 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { OS << "\n'RELR' relocation section at offset " << format_hex(reinterpret_cast<const uint8_t *>(DynRelrRegion.Addr) - Obj->base(), - 1) << " contains " << DynRelrRegion.Size << " bytes:\n"; + 1) + << " contains " << DynRelrRegion.Size << " bytes:\n"; printRelocHeader(ELF::SHT_REL); Elf_Relr_Range Relrs = this->dumper()->dyn_relrs(); std::vector<Elf_Rela> RelrRelas = unwrapOrError(Obj->decode_relrs(Relrs)); @@ -3462,7 +3527,8 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { OS << "\n'PLT' relocation section at offset " << format_hex(reinterpret_cast<const uint8_t *>(DynPLTRelRegion.Addr) - Obj->base(), - 1) << " contains " << DynPLTRelRegion.Size << " bytes:\n"; + 1) + << " contains " << DynPLTRelRegion.Size << " bytes:\n"; } if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) { printRelocHeader(ELF::SHT_RELA); @@ -3480,18 +3546,189 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { } } +template <class ELFT> +static void printGNUVersionSectionProlog(formatted_raw_ostream &OS, + const Twine &Name, unsigned EntriesNum, + const ELFFile<ELFT> *Obj, + const typename ELFT::Shdr *Sec) { + StringRef SecName = unwrapOrError(Obj->getSectionName(Sec)); + OS << Name << " section '" << SecName << "' " + << "contains " << EntriesNum << " entries:\n"; + + const typename ELFT::Shdr *SymTab = + unwrapOrError(Obj->getSection(Sec->sh_link)); + StringRef SymTabName = unwrapOrError(Obj->getSectionName(SymTab)); + OS << " Addr: " << format_hex_no_prefix(Sec->sh_addr, 16) + << " Offset: " << format_hex(Sec->sh_offset, 8) + << " Link: " << Sec->sh_link << " (" << SymTabName << ")\n"; +} + +template <class ELFT> +void GNUStyle<ELFT>::printVersionSymbolSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + if (!Sec) + return; + + unsigned Entries = Sec->sh_size / sizeof(Elf_Versym); + printGNUVersionSectionProlog(OS, "Version symbols", Entries, Obj, Sec); + + const uint8_t *VersymBuf = + reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); + const ELFDumper<ELFT> *Dumper = this->dumper(); + StringRef StrTable = Dumper->getDynamicStringTable(); + + // readelf prints 4 entries per line. + for (uint64_t VersymRow = 0; VersymRow < Entries; VersymRow += 4) { + OS << " " << format_hex_no_prefix(VersymRow, 3) << ":"; + + for (uint64_t VersymIndex = 0; + (VersymIndex < 4) && (VersymIndex + VersymRow) < Entries; + ++VersymIndex) { + const Elf_Versym *Versym = + reinterpret_cast<const Elf_Versym *>(VersymBuf); + switch (Versym->vs_index) { + case 0: + OS << " 0 (*local*) "; + break; + case 1: + OS << " 1 (*global*) "; + break; + default: + OS << format("%4x%c", Versym->vs_index & VERSYM_VERSION, + Versym->vs_index & VERSYM_HIDDEN ? 'h' : ' '); + + bool IsDefault = true; + std::string VersionName = Dumper->getSymbolVersionByIndex( + StrTable, Versym->vs_index, IsDefault); + + if (!VersionName.empty()) + VersionName = "(" + VersionName + ")"; + else + VersionName = "(*invalid*)"; + OS << left_justify(VersionName, 13); + } + VersymBuf += sizeof(Elf_Versym); + } + OS << '\n'; + } + OS << '\n'; +} + +static std::string versionFlagToString(unsigned Flags) { + if (Flags == 0) + return "none"; + + std::string Ret; + auto AddFlag = [&Ret, &Flags](unsigned Flag, StringRef Name) { + if (!(Flags & Flag)) + return; + if (!Ret.empty()) + Ret += " | "; + Ret += Name; + Flags &= ~Flag; + }; + + AddFlag(VER_FLG_BASE, "BASE"); + AddFlag(VER_FLG_WEAK, "WEAK"); + AddFlag(VER_FLG_INFO, "INFO"); + AddFlag(~0, "<unknown>"); + return Ret; +} + +template <class ELFT> +void GNUStyle<ELFT>::printVersionDefinitionSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + if (!Sec) + return; + + unsigned VerDefsNum = Sec->sh_info; + printGNUVersionSectionProlog(OS, "Version definition", VerDefsNum, Obj, Sec); + + const Elf_Shdr *StrTabSec = unwrapOrError(Obj->getSection(Sec->sh_link)); + StringRef StringTable( + reinterpret_cast<const char *>(Obj->base() + StrTabSec->sh_offset), + (size_t)StrTabSec->sh_size); + + const uint8_t *VerdefBuf = unwrapOrError(Obj->getSectionContents(Sec)).data(); + const uint8_t *Begin = VerdefBuf; + + while (VerDefsNum--) { + const Elf_Verdef *Verdef = reinterpret_cast<const Elf_Verdef *>(VerdefBuf); + OS << format(" 0x%04x: Rev: %u Flags: %s Index: %u Cnt: %u", + VerdefBuf - Begin, (unsigned)Verdef->vd_version, + versionFlagToString(Verdef->vd_flags).c_str(), + (unsigned)Verdef->vd_ndx, (unsigned)Verdef->vd_cnt); + + const uint8_t *VerdauxBuf = VerdefBuf + Verdef->vd_aux; + const Elf_Verdaux *Verdaux = + reinterpret_cast<const Elf_Verdaux *>(VerdauxBuf); + OS << format(" Name: %s\n", + StringTable.drop_front(Verdaux->vda_name).data()); + + for (unsigned I = 1; I < Verdef->vd_cnt; ++I) { + VerdauxBuf += Verdaux->vda_next; + Verdaux = reinterpret_cast<const Elf_Verdaux *>(VerdauxBuf); + OS << format(" 0x%04x: Parent %u: %s\n", VerdauxBuf - Begin, I, + StringTable.drop_front(Verdaux->vda_name).data()); + } + + VerdefBuf += Verdef->vd_next; + } + OS << '\n'; +} + +template <class ELFT> +void GNUStyle<ELFT>::printVersionDependencySection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + if (!Sec) + return; + + unsigned VerneedNum = Sec->sh_info; + printGNUVersionSectionProlog(OS, "Version needs", VerneedNum, Obj, Sec); + + ArrayRef<uint8_t> SecData = unwrapOrError(Obj->getSectionContents(Sec)); + + const Elf_Shdr *StrTabSec = unwrapOrError(Obj->getSection(Sec->sh_link)); + StringRef StringTable = { + reinterpret_cast<const char *>(Obj->base() + StrTabSec->sh_offset), + (size_t)StrTabSec->sh_size}; + + const uint8_t *VerneedBuf = SecData.data(); + for (unsigned I = 0; I < VerneedNum; ++I) { + const Elf_Verneed *Verneed = + reinterpret_cast<const Elf_Verneed *>(VerneedBuf); + + OS << format(" 0x%04x: Version: %u File: %s Cnt: %u\n", + reinterpret_cast<const uint8_t *>(Verneed) - SecData.begin(), + (unsigned)Verneed->vn_version, + StringTable.drop_front(Verneed->vn_file).data(), + (unsigned)Verneed->vn_cnt); + + const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; + for (unsigned J = 0; J < Verneed->vn_cnt; ++J) { + const Elf_Vernaux *Vernaux = + reinterpret_cast<const Elf_Vernaux *>(VernauxBuf); + + OS << format(" 0x%04x: Name: %s Flags: %s Version: %u\n", + reinterpret_cast<const uint8_t *>(Vernaux) - SecData.begin(), + StringTable.drop_front(Vernaux->vna_name).data(), + versionFlagToString(Vernaux->vna_flags).c_str(), + (unsigned)Vernaux->vna_other); + VernauxBuf += Vernaux->vna_next; + } + VerneedBuf += Verneed->vn_next; + } + OS << '\n'; +} + // Hash histogram shows statistics of how efficient the hash was for the // dynamic symbol table. The table shows number of hash buckets for different // lengths of chains as absolute number and percentage of the total buckets. // Additionally cumulative coverage of symbols for each set of buckets. template <class ELFT> void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { - - const Elf_Hash *HashTable = this->dumper()->getHashTable(); - const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable(); - // Print histogram for .hash section - if (HashTable) { + if (const Elf_Hash *HashTable = this->dumper()->getHashTable()) { size_t NBucket = HashTable->nbucket; size_t NChain = HashTable->nchain; ArrayRef<Elf_Word> Buckets = HashTable->buckets(); @@ -3535,7 +3772,7 @@ void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { } // Print histogram for .gnu.hash section - if (GnuHashTable) { + if (const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable()) { size_t NBucket = GnuHashTable->nbuckets; ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets(); unsigned NumSyms = this->dumper()->dynamic_symbols().size(); @@ -3595,6 +3832,24 @@ void GNUStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) { OS << "GNUStyle::printAddrsig not implemented\n"; } +static StringRef getGenericNoteTypeName(const uint32_t NT) { + static const struct { + uint32_t ID; + const char *Name; + } Notes[] = { + {ELF::NT_VERSION, "NT_VERSION (version)"}, + {ELF::NT_ARCH, "NT_ARCH (architecture)"}, + {ELF::NT_GNU_BUILD_ATTRIBUTE_OPEN, "OPEN"}, + {ELF::NT_GNU_BUILD_ATTRIBUTE_FUNC, "func"}, + }; + + for (const auto &Note : Notes) + if (Note.ID == NT) + return Note.Name; + + return ""; +} + static std::string getGNUNoteTypeName(const uint32_t NT) { static const struct { uint32_t ID; @@ -3649,14 +3904,11 @@ static std::string getAMDNoteTypeName(const uint32_t NT) { static const struct { uint32_t ID; const char *Name; - } Notes[] = { - {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)"} - }; + } Notes[] = {{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)"}}; for (const auto &Note : Notes) if (Note.ID == NT) @@ -3683,6 +3935,16 @@ static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, ArrayRef<uint8_t> Data) { std::string str; raw_string_ostream OS(str); + uint32_t PrData; + auto DumpBit = [&](uint32_t Flag, StringRef Name) { + if (PrData & Flag) { + PrData &= ~Flag; + OS << Name; + if (PrData) + OS << ", "; + } + }; + switch (Type) { default: OS << format("<application-specific type 0x%x>", Type); @@ -3701,41 +3963,101 @@ static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, if (DataSize) OS << format(" <corrupt length: 0x%x>", DataSize); return OS.str(); + case GNU_PROPERTY_AARCH64_FEATURE_1_AND: case GNU_PROPERTY_X86_FEATURE_1_AND: - OS << "X86 features: "; - if (DataSize != 4 && DataSize != 8) { + OS << ((Type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) ? "aarch64 feature: " + : "x86 feature: "); + if (DataSize != 4) { OS << format("<corrupt length: 0x%x>", DataSize); return OS.str(); } - uint64_t CFProtection = - (DataSize == 4) - ? support::endian::read32<ELFT::TargetEndianness>(Data.data()) - : support::endian::read64<ELFT::TargetEndianness>(Data.data()); - if (CFProtection == 0) { - OS << "none"; + PrData = support::endian::read32<ELFT::TargetEndianness>(Data.data()); + if (PrData == 0) { + OS << "<None>"; return OS.str(); } - if (CFProtection & GNU_PROPERTY_X86_FEATURE_1_IBT) { - OS << "IBT"; - CFProtection &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; - if (CFProtection) - OS << ", "; + if (Type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) { + DumpBit(GNU_PROPERTY_AARCH64_FEATURE_1_BTI, "BTI"); + DumpBit(GNU_PROPERTY_AARCH64_FEATURE_1_PAC, "PAC"); + } else { + DumpBit(GNU_PROPERTY_X86_FEATURE_1_IBT, "IBT"); + DumpBit(GNU_PROPERTY_X86_FEATURE_1_SHSTK, "SHSTK"); } - if (CFProtection & GNU_PROPERTY_X86_FEATURE_1_SHSTK) { - OS << "SHSTK"; - CFProtection &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK; - if (CFProtection) - OS << ", "; + 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(); } - if (CFProtection) - OS << format("<unknown flags: 0x%llx>", CFProtection); + 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 " + << (Type == GNU_PROPERTY_X86_FEATURE_2_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_FEATURE_2_X86, "x86"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_X87, "x87"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_MMX, "MMX"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_XMM, "XMM"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_YMM, "YMM"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_ZMM, "ZMM"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_FXSR, "FXSR"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_XSAVE, "XSAVE"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_XSAVEOPT, "XSAVEOPT"); + DumpBit(GNU_PROPERTY_X86_FEATURE_2_XSAVEC, "XSAVEC"); + if (PrData) + OS << format("<unknown flags: 0x%x>", PrData); return OS.str(); } } template <typename ELFT> -static SmallVector<std::string, 4> -getGNUPropertyList(ArrayRef<uint8_t> Arr) { +static SmallVector<std::string, 4> getGNUPropertyList(ArrayRef<uint8_t> Arr) { using Elf_Word = typename ELFT::Word; SmallVector<std::string, 4> Properties; @@ -3770,12 +4092,11 @@ struct GNUAbiTag { bool IsValid; }; -template <typename ELFT> -static GNUAbiTag getGNUAbiTag(ArrayRef<uint8_t> Desc) { +template <typename ELFT> static GNUAbiTag getGNUAbiTag(ArrayRef<uint8_t> Desc) { typedef typename ELFT::Word Elf_Word; - ArrayRef<Elf_Word> Words(reinterpret_cast<const Elf_Word*>(Desc.begin()), - reinterpret_cast<const Elf_Word*>(Desc.end())); + ArrayRef<Elf_Word> Words(reinterpret_cast<const Elf_Word *>(Desc.begin()), + reinterpret_cast<const Elf_Word *>(Desc.end())); if (Words.size() < 4) return {"", "", /*IsValid=*/false}; @@ -3846,24 +4167,13 @@ static AMDNote getAMDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) { default: return {"", ""}; case ELF::NT_AMD_AMDGPU_HSA_METADATA: - return {"HSA Metadata", - std::string(reinterpret_cast<const char *>(Desc.data()), - Desc.size())}; + return { + "HSA Metadata", + std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())}; case ELF::NT_AMD_AMDGPU_ISA: - return {"ISA Version", - std::string(reinterpret_cast<const char *>(Desc.data()), - Desc.size())}; - case ELF::NT_AMD_AMDGPU_PAL_METADATA: - const uint32_t *PALMetadataBegin = - reinterpret_cast<const uint32_t *>(Desc.data()); - const uint32_t *PALMetadataEnd = PALMetadataBegin + Desc.size(); - std::vector<uint32_t> PALMetadata(PALMetadataBegin, PALMetadataEnd); - std::string PALMetadataString; - auto Error = AMDGPU::PALMD::toString(PALMetadata, PALMetadataString); - if (Error) { - return {"PAL Metadata", "Invalid"}; - } - return {"PAL Metadata", PALMetadataString}; + return { + "ISA Version", + std::string(reinterpret_cast<const char *>(Desc.data()), Desc.size())}; } } @@ -3877,36 +4187,28 @@ static AMDGPUNote getAMDGPUNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) { switch (NoteType) { default: return {"", ""}; - case ELF::NT_AMDGPU_METADATA: + case ELF::NT_AMDGPU_METADATA: { auto MsgPackString = StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size()); - msgpack::Reader MsgPackReader(MsgPackString); - auto OptMsgPackNodeOrErr = msgpack::Node::read(MsgPackReader); - if (errorToBool(OptMsgPackNodeOrErr.takeError())) - return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"}; - auto &OptMsgPackNode = *OptMsgPackNodeOrErr; - if (!OptMsgPackNode) + msgpack::Document MsgPackDoc; + if (!MsgPackDoc.readFromBlob(MsgPackString, /*Multi=*/false)) return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"}; - auto &MsgPackNode = *OptMsgPackNode; AMDGPU::HSAMD::V3::MetadataVerifier Verifier(true); - if (!Verifier.verify(*MsgPackNode)) + if (!Verifier.verify(MsgPackDoc.getRoot())) return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"}; std::string HSAMetadataString; raw_string_ostream StrOS(HSAMetadataString); - yaml::Output YOut(StrOS); - YOut << MsgPackNode; + MsgPackDoc.toYAML(StrOS); return {"AMDGPU Metadata", StrOS.str()}; } + } } template <class ELFT> void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { - const Elf_Ehdr *e = Obj->getHeader(); - bool IsCore = e->e_type == ELF::ET_CORE; - auto PrintHeader = [&](const typename ELFT::Off Offset, const typename ELFT::Addr Size) { OS << "Displaying notes found at file offset " << format_hex(Offset, 10) @@ -3938,12 +4240,16 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { if (!N.Type.empty()) OS << " " << N.Type << ":\n " << N.Value << '\n'; } else { - OS << "Unknown note type: (" << format_hex(Type, 10) << ')'; + StringRef NoteType = getGenericNoteTypeName(Type); + if (!NoteType.empty()) + OS << NoteType; + else + OS << "Unknown note type: (" << format_hex(Type, 10) << ')'; } OS << '\n'; }; - if (IsCore) { + if (Obj->getHeader()->e_type == ELF::ET_CORE) { for (const auto &P : unwrapOrError(Obj->program_headers())) { if (P.p_type != PT_NOTE) continue; @@ -3992,7 +4298,10 @@ void GNUStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) { << format_hex_no_prefix(Parser.getGp(), 8 + Bias) << "\n\n"; OS << " Reserved entries:\n"; - OS << " Address Access Initial Purpose\n"; + if (ELFT::Is64Bits) + OS << " Address Access Initial Purpose\n"; + else + OS << " Address Access Initial Purpose\n"; PrintEntry(Parser.getGotLazyResolver(), "Lazy resolver"); if (Parser.getGotModulePointer()) PrintEntry(Parser.getGotModulePointer(), "Module pointer (GNU extension)"); @@ -4000,7 +4309,10 @@ void GNUStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) { if (!Parser.getLocalEntries().empty()) { OS << "\n"; OS << " Local entries:\n"; - OS << " Address Access Initial\n"; + if (ELFT::Is64Bits) + OS << " Address Access Initial\n"; + else + OS << " Address Access Initial\n"; for (auto &E : Parser.getLocalEntries()) PrintEntry(&E, ""); } @@ -4011,7 +4323,11 @@ void GNUStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) { if (!Parser.getGlobalEntries().empty()) { OS << "\n"; OS << " Global entries:\n"; - OS << " Address Access Initial Sym.Val. Type Ndx Name\n"; + if (ELFT::Is64Bits) + OS << " Address Access Initial Sym.Val." + << " Type Ndx Name\n"; + else + OS << " Address Access Initial Sym.Val. Type Ndx Name\n"; for (auto &E : Parser.getGlobalEntries()) { const Elf_Sym *Sym = Parser.getGotSym(&E); std::string SymName = this->dumper()->getFullSymbolName( @@ -4045,7 +4361,7 @@ void GNUStyle<ELFT>::printMipsPLT(const MipsGOTParser<ELFT> &Parser) { size_t Bias = ELFT::Is64Bits ? 8 : 0; auto PrintEntry = [&](const Elf_Addr *E, StringRef Purpose) { OS.PadToColumn(2); - OS << format_hex_no_prefix(Parser.getGotAddress(E), 8 + Bias); + OS << format_hex_no_prefix(Parser.getPltAddress(E), 8 + Bias); OS.PadToColumn(11 + Bias); OS << format_hex_no_prefix(*E, 8 + Bias); OS.PadToColumn(20 + 2 * Bias); @@ -4058,7 +4374,7 @@ void GNUStyle<ELFT>::printMipsPLT(const MipsGOTParser<ELFT> &Parser) { OS << " Address Initial Purpose\n"; PrintEntry(Parser.getPltLazyResolver(), "PLT lazy resolver"); if (Parser.getPltModulePointer()) - PrintEntry(Parser.getGotModulePointer(), "Module pointer"); + PrintEntry(Parser.getPltModulePointer(), "Module pointer"); if (!Parser.getPltEntries().empty()) { OS << "\n"; @@ -4070,7 +4386,7 @@ void GNUStyle<ELFT>::printMipsPLT(const MipsGOTParser<ELFT> &Parser) { Sym, this->dumper()->getDynamicStringTable(), false); OS.PadToColumn(2); - OS << to_string(format_hex_no_prefix(Parser.getGotAddress(&E), 8 + Bias)); + OS << to_string(format_hex_no_prefix(Parser.getPltAddress(&E), 8 + Bias)); OS.PadToColumn(11 + Bias); OS << to_string(format_hex_no_prefix(E, 8 + Bias)); OS.PadToColumn(20 + 2 * Bias); @@ -4087,21 +4403,21 @@ void GNUStyle<ELFT>::printMipsPLT(const MipsGOTParser<ELFT> &Parser) { } template <class ELFT> void LLVMStyle<ELFT>::printFileHeaders(const ELFO *Obj) { - const Elf_Ehdr *e = Obj->getHeader(); + const Elf_Ehdr *E = Obj->getHeader(); { DictScope D(W, "ElfHeader"); { DictScope D(W, "Ident"); - W.printBinary("Magic", makeArrayRef(e->e_ident).slice(ELF::EI_MAG0, 4)); - W.printEnum("Class", e->e_ident[ELF::EI_CLASS], makeArrayRef(ElfClass)); - W.printEnum("DataEncoding", e->e_ident[ELF::EI_DATA], + W.printBinary("Magic", makeArrayRef(E->e_ident).slice(ELF::EI_MAG0, 4)); + W.printEnum("Class", E->e_ident[ELF::EI_CLASS], makeArrayRef(ElfClass)); + W.printEnum("DataEncoding", E->e_ident[ELF::EI_DATA], makeArrayRef(ElfDataEncoding)); - W.printNumber("FileVersion", e->e_ident[ELF::EI_VERSION]); + W.printNumber("FileVersion", E->e_ident[ELF::EI_VERSION]); auto OSABI = makeArrayRef(ElfOSABI); - if (e->e_ident[ELF::EI_OSABI] >= ELF::ELFOSABI_FIRST_ARCH && - e->e_ident[ELF::EI_OSABI] <= ELF::ELFOSABI_LAST_ARCH) { - switch (e->e_machine) { + if (E->e_ident[ELF::EI_OSABI] >= ELF::ELFOSABI_FIRST_ARCH && + E->e_ident[ELF::EI_OSABI] <= ELF::ELFOSABI_LAST_ARCH) { + switch (E->e_machine) { case ELF::EM_AMDGPU: OSABI = makeArrayRef(AMDGPUElfOSABI); break; @@ -4113,34 +4429,35 @@ template <class ELFT> void LLVMStyle<ELFT>::printFileHeaders(const ELFO *Obj) { break; } } - W.printEnum("OS/ABI", e->e_ident[ELF::EI_OSABI], OSABI); - W.printNumber("ABIVersion", e->e_ident[ELF::EI_ABIVERSION]); - W.printBinary("Unused", makeArrayRef(e->e_ident).slice(ELF::EI_PAD)); + W.printEnum("OS/ABI", E->e_ident[ELF::EI_OSABI], OSABI); + W.printNumber("ABIVersion", E->e_ident[ELF::EI_ABIVERSION]); + W.printBinary("Unused", makeArrayRef(E->e_ident).slice(ELF::EI_PAD)); } - W.printEnum("Type", e->e_type, makeArrayRef(ElfObjectFileType)); - W.printEnum("Machine", e->e_machine, makeArrayRef(ElfMachineType)); - W.printNumber("Version", e->e_version); - W.printHex("Entry", e->e_entry); - W.printHex("ProgramHeaderOffset", e->e_phoff); - W.printHex("SectionHeaderOffset", e->e_shoff); - if (e->e_machine == EM_MIPS) - W.printFlags("Flags", e->e_flags, makeArrayRef(ElfHeaderMipsFlags), + W.printEnum("Type", E->e_type, makeArrayRef(ElfObjectFileType)); + W.printEnum("Machine", E->e_machine, makeArrayRef(ElfMachineType)); + W.printNumber("Version", E->e_version); + W.printHex("Entry", E->e_entry); + W.printHex("ProgramHeaderOffset", E->e_phoff); + W.printHex("SectionHeaderOffset", E->e_shoff); + if (E->e_machine == EM_MIPS) + 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), + 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) - W.printFlags("Flags", e->e_flags, makeArrayRef(ElfHeaderRISCVFlags)); + else if (E->e_machine == EM_RISCV) + W.printFlags("Flags", E->e_flags, makeArrayRef(ElfHeaderRISCVFlags)); else - W.printFlags("Flags", e->e_flags); - W.printNumber("HeaderSize", e->e_ehsize); - W.printNumber("ProgramHeaderEntrySize", e->e_phentsize); - W.printNumber("ProgramHeaderCount", e->e_phnum); - W.printNumber("SectionHeaderEntrySize", e->e_shentsize); + W.printFlags("Flags", E->e_flags); + W.printNumber("HeaderSize", E->e_ehsize); + W.printNumber("ProgramHeaderEntrySize", E->e_phentsize); + W.printNumber("ProgramHeaderCount", E->e_phnum); + W.printNumber("SectionHeaderEntrySize", E->e_shentsize); W.printString("SectionHeaderCount", getSectionHeadersNumString(Obj)); - W.printString("StringTableSectionIndex", getSectionHeaderTableIndexString(Obj)); + W.printString("StringTableSectionIndex", + getSectionHeaderTableIndexString(Obj)); } } @@ -4185,10 +4502,8 @@ template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const ELFO *Obj) { for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { ++SectionNumber; - if (Sec.sh_type != ELF::SHT_REL && - Sec.sh_type != ELF::SHT_RELA && - Sec.sh_type != ELF::SHT_RELR && - Sec.sh_type != ELF::SHT_ANDROID_REL && + if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA && + Sec.sh_type != ELF::SHT_RELR && Sec.sh_type != ELF::SHT_ANDROID_REL && Sec.sh_type != ELF::SHT_ANDROID_RELA && Sec.sh_type != ELF::SHT_ANDROID_RELR) continue; @@ -4249,7 +4564,7 @@ void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab) { SmallString<32> RelocName; Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); - StringRef TargetName; + std::string TargetName; const Elf_Sym *Sym = unwrapOrError(Obj->getRelocationSymbol(&Rel, SymTab)); if (Sym && Sym->getType() == ELF::STT_SECTION) { const Elf_Shdr *Sec = unwrapOrError( @@ -4257,7 +4572,8 @@ void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, Elf_Rela Rel, TargetName = unwrapOrError(Obj->getSectionName(Sec)); } else if (Sym) { StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTab)); - TargetName = unwrapOrError(Sym->getName(StrTable)); + TargetName = this->dumper()->getFullSymbolName( + Sym, StrTable, SymTab->sh_type == SHT_DYNSYM /* IsDynamic */); } if (opts::ExpandRelocs) { @@ -4270,8 +4586,8 @@ void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, Elf_Rela Rel, } else { raw_ostream &OS = W.startLine(); OS << W.hex(Rel.r_offset) << " " << RelocName << " " - << (!TargetName.empty() ? TargetName : "-") << " " - << W.hex(Rel.r_addend) << "\n"; + << (!TargetName.empty() ? TargetName : "-") << " " << W.hex(Rel.r_addend) + << "\n"; } } @@ -4280,13 +4596,12 @@ void LLVMStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { ListScope SectionsD(W, "Sections"); int SectionIndex = -1; - for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { - ++SectionIndex; - - StringRef Name = unwrapOrError(Obj->getSectionName(&Sec)); - + ArrayRef<Elf_Shdr> Sections = unwrapOrError(Obj->sections()); + const ELFObjectFile<ELFT> *ElfObj = this->dumper()->getElfObject(); + for (const Elf_Shdr &Sec : Sections) { + StringRef Name = getSectionName(Sec, *ElfObj, Sections); DictScope SectionD(W, "Section"); - W.printNumber("Index", SectionIndex); + W.printNumber("Index", ++SectionIndex); W.printNumber("Name", Name, Sec.sh_name); W.printHex( "Type", @@ -4350,8 +4665,9 @@ void LLVMStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { if (opts::SectionData && Sec.sh_type != ELF::SHT_NOBITS) { ArrayRef<uint8_t> Data = unwrapOrError(Obj->getSectionContents(&Sec)); - W.printBinaryBlock("SectionData", - StringRef((const char *)Data.data(), Data.size())); + W.printBinaryBlock( + "SectionData", + StringRef(reinterpret_cast<const char *>(Data.data()), Data.size())); } } } @@ -4402,6 +4718,15 @@ void LLVMStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, W.printHex("Section", SectionName, SectionIndex); } +template <class ELFT> +void LLVMStyle<ELFT>::printSymbols(const ELFO *Obj, bool PrintSymbols, + bool PrintDynamicSymbols) { + if (PrintSymbols) + printSymbols(Obj); + if (PrintDynamicSymbols) + printDynamicSymbols(Obj); +} + template <class ELFT> void LLVMStyle<ELFT>::printSymbols(const ELFO *Obj) { ListScope Group(W, "Symbols"); this->dumper()->printSymbolsHelper(false); @@ -4413,6 +4738,31 @@ void LLVMStyle<ELFT>::printDynamicSymbols(const ELFO *Obj) { this->dumper()->printSymbolsHelper(true); } +template <class ELFT> void LLVMStyle<ELFT>::printDynamic(const ELFFile<ELFT> *Obj) { + Elf_Dyn_Range Table = this->dumper()->dynamic_table(); + if (Table.empty()) + return; + + raw_ostream &OS = W.getOStream(); + W.startLine() << "DynamicSection [ (" << Table.size() << " entries)\n"; + + bool Is64 = ELFT::Is64Bits; + if (Is64) + W.startLine() << " Tag Type Name/Value\n"; + else + W.startLine() << " Tag Type Name/Value\n"; + for (auto Entry : Table) { + uintX_t Tag = Entry.getTag(); + W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, true) << " " + << format("%-21s", + getTypeString(Obj->getHeader()->e_machine, Tag)); + this->dumper()->printDynamicEntry(OS, Tag, Entry.getVal()); + OS << "\n"; + } + + W.startLine() << "]\n"; +} + template <class ELFT> void LLVMStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { const DynRegionInfo &DynRelRegion = this->dumper()->getDynRelRegion(); @@ -4459,11 +4809,11 @@ template <class ELFT> void LLVMStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel) { SmallString<32> RelocName; Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); - StringRef SymbolName; + std::string SymbolName; uint32_t SymIndex = Rel.getSymbol(Obj->isMips64EL()); const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex; - SymbolName = - unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable())); + SymbolName = maybeDemangle( + unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable()))); if (opts::ExpandRelocs) { DictScope Group(W, "Relocation"); W.printHex("Offset", Rel.r_offset); @@ -4473,12 +4823,22 @@ void LLVMStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel) { } else { raw_ostream &OS = W.startLine(); OS << W.hex(Rel.r_offset) << " " << RelocName << " " - << (!SymbolName.empty() ? SymbolName : "-") << " " - << W.hex(Rel.r_addend) << "\n"; + << (!SymbolName.empty() ? SymbolName : "-") << " " << W.hex(Rel.r_addend) + << "\n"; } } template <class ELFT> +void LLVMStyle<ELFT>::printProgramHeaders( + const ELFO *Obj, bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) { + if (PrintProgramHeaders) + printProgramHeaders(Obj); + if (PrintSectionMapping == cl::BOU_TRUE) + printSectionMapping(Obj); +} + +template <class ELFT> void LLVMStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { ListScope L(W, "ProgramHeaders"); @@ -4498,6 +4858,125 @@ void LLVMStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { } template <class ELFT> +void LLVMStyle<ELFT>::printVersionSymbolSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + DictScope SS(W, "Version symbols"); + if (!Sec) + return; + + StringRef SecName = unwrapOrError(Obj->getSectionName(Sec)); + W.printNumber("Section Name", SecName, Sec->sh_name); + W.printHex("Address", Sec->sh_addr); + W.printHex("Offset", Sec->sh_offset); + W.printNumber("Link", Sec->sh_link); + + const uint8_t *VersymBuf = + reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); + const ELFDumper<ELFT> *Dumper = this->dumper(); + StringRef StrTable = Dumper->getDynamicStringTable(); + + // Same number of entries in the dynamic symbol table (DT_SYMTAB). + ListScope Syms(W, "Symbols"); + for (const Elf_Sym &Sym : Dumper->dynamic_symbols()) { + DictScope S(W, "Symbol"); + const Elf_Versym *Versym = reinterpret_cast<const Elf_Versym *>(VersymBuf); + std::string FullSymbolName = + Dumper->getFullSymbolName(&Sym, StrTable, true /* IsDynamic */); + W.printNumber("Version", Versym->vs_index & VERSYM_VERSION); + W.printString("Name", FullSymbolName); + VersymBuf += sizeof(Elf_Versym); + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printVersionDefinitionSection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + DictScope SD(W, "SHT_GNU_verdef"); + if (!Sec) + return; + + const uint8_t *SecStartAddress = + reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); + const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size; + const uint8_t *VerdefBuf = SecStartAddress; + const Elf_Shdr *StrTab = unwrapOrError(Obj->getSection(Sec->sh_link)); + + unsigned VerDefsNum = Sec->sh_info; + while (VerDefsNum--) { + if (VerdefBuf + sizeof(Elf_Verdef) > SecEndAddress) + // FIXME: report_fatal_error is not a good way to report error. We should + // emit a parsing error here and below. + report_fatal_error("invalid offset in the section"); + + const Elf_Verdef *Verdef = reinterpret_cast<const Elf_Verdef *>(VerdefBuf); + DictScope Def(W, "Definition"); + W.printNumber("Version", Verdef->vd_version); + W.printEnum("Flags", Verdef->vd_flags, makeArrayRef(SymVersionFlags)); + W.printNumber("Index", Verdef->vd_ndx); + W.printNumber("Hash", Verdef->vd_hash); + W.printString("Name", StringRef(reinterpret_cast<const char *>( + Obj->base() + StrTab->sh_offset + + Verdef->getAux()->vda_name))); + if (!Verdef->vd_cnt) + report_fatal_error("at least one definition string must exist"); + if (Verdef->vd_cnt > 2) + report_fatal_error("more than one predecessor is not expected"); + + if (Verdef->vd_cnt == 2) { + const uint8_t *VerdauxBuf = + VerdefBuf + Verdef->vd_aux + Verdef->getAux()->vda_next; + const Elf_Verdaux *Verdaux = + reinterpret_cast<const Elf_Verdaux *>(VerdauxBuf); + W.printString("Predecessor", + StringRef(reinterpret_cast<const char *>( + Obj->base() + StrTab->sh_offset + Verdaux->vda_name))); + } + VerdefBuf += Verdef->vd_next; + } +} + +template <class ELFT> +void LLVMStyle<ELFT>::printVersionDependencySection(const ELFFile<ELFT> *Obj, + const Elf_Shdr *Sec) { + DictScope SD(W, "SHT_GNU_verneed"); + if (!Sec) + return; + + const uint8_t *SecData = + reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); + const Elf_Shdr *StrTab = unwrapOrError(Obj->getSection(Sec->sh_link)); + + const uint8_t *VerneedBuf = SecData; + unsigned VerneedNum = Sec->sh_info; + for (unsigned I = 0; I < VerneedNum; ++I) { + const Elf_Verneed *Verneed = + reinterpret_cast<const Elf_Verneed *>(VerneedBuf); + DictScope Entry(W, "Dependency"); + W.printNumber("Version", Verneed->vn_version); + W.printNumber("Count", Verneed->vn_cnt); + W.printString("FileName", + StringRef(reinterpret_cast<const char *>( + Obj->base() + StrTab->sh_offset + Verneed->vn_file))); + + const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; + ListScope L(W, "Entries"); + for (unsigned J = 0; J < Verneed->vn_cnt; ++J) { + const Elf_Vernaux *Vernaux = + reinterpret_cast<const Elf_Vernaux *>(VernauxBuf); + DictScope Entry(W, "Entry"); + W.printNumber("Hash", Vernaux->vna_hash); + W.printEnum("Flags", Vernaux->vna_flags, makeArrayRef(SymVersionFlags)); + W.printNumber("Index", Vernaux->vna_other); + W.printString("Name", + StringRef(reinterpret_cast<const char *>( + Obj->base() + StrTab->sh_offset + Vernaux->vna_name))); + VernauxBuf += Vernaux->vna_next; + } + VerneedBuf += Verneed->vn_next; + } +} + +template <class ELFT> void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { W.startLine() << "Hash Histogram not implemented!\n"; } @@ -4542,8 +5021,7 @@ void LLVMStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) { } template <typename ELFT> -static void printGNUNoteLLVMStyle(uint32_t NoteType, - ArrayRef<uint8_t> Desc, +static void printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc, ScopedPrinter &W) { switch (NoteType) { default: @@ -4576,8 +5054,6 @@ static void printGNUNoteLLVMStyle(uint32_t NoteType, template <class ELFT> void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { ListScope L(W, "Notes"); - const Elf_Ehdr *e = Obj->getHeader(); - bool IsCore = e->e_type == ELF::ET_CORE; auto PrintHeader = [&](const typename ELFT::Off Offset, const typename ELFT::Addr Size) { @@ -4609,11 +5085,16 @@ void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { if (!N.Type.empty()) W.printString(N.Type, N.Value); } else { - W.getOStream() << "Unknown note type: (" << format_hex(Type, 10) << ')'; + StringRef NoteType = getGenericNoteTypeName(Type); + if (!NoteType.empty()) + W.printString("Type", NoteType); + else + W.printString("Type", + "Unknown (" + to_string(format_hex(Type, 10)) + ")"); } }; - if (IsCore) { + if (Obj->getHeader()->e_type == ELF::ET_CORE) { for (const auto &P : unwrapOrError(Obj->program_headers())) { if (P.p_type != PT_NOTE) continue; diff --git a/contrib/llvm/tools/llvm-readobj/Error.cpp b/contrib/llvm/tools/llvm-readobj/Error.cpp index 03d349440e6b..1010f18a58c8 100644 --- a/contrib/llvm/tools/llvm-readobj/Error.cpp +++ b/contrib/llvm/tools/llvm-readobj/Error.cpp @@ -1,9 +1,8 @@ //===- Error.cpp - system_error extensions for llvm-readobj -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-readobj/Error.h b/contrib/llvm/tools/llvm-readobj/Error.h index f3e24bbe5dbf..f390e1b96f8a 100644 --- a/contrib/llvm/tools/llvm-readobj/Error.h +++ b/contrib/llvm/tools/llvm-readobj/Error.h @@ -1,9 +1,8 @@ //===- Error.h - system_error extensions for llvm-readobj -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-readobj/MachODumper.cpp b/contrib/llvm/tools/llvm-readobj/MachODumper.cpp index 35e4cfcb6b10..32a3866eb2f2 100644 --- a/contrib/llvm/tools/llvm-readobj/MachODumper.cpp +++ b/contrib/llvm/tools/llvm-readobj/MachODumper.cpp @@ -1,9 +1,8 @@ -//===-- MachODump.cpp - Object file dumping utility for llvm --------------===// +//===- MachODumper.cpp - Object file dumping utility for llvm -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -34,8 +33,6 @@ public: void printFileHeaders() override; void printSectionHeaders() override; void printRelocations() override; - void printSymbols() override; - void printDynamicSymbols() override; void printUnwindInfo() override; void printStackMap() const override; @@ -53,6 +50,8 @@ private: template<class MachHeader> void printFileHeaders(const MachHeader &Header); + void printSymbols() override; + void printDynamicSymbols() override; void printSymbol(const SymbolRef &Symbol); void printRelocation(const RelocationRef &Reloc); @@ -163,6 +162,7 @@ static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesARM[] = { static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesARM64[] = { LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM64_ALL), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM64E), }; static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesSPARC[] = { @@ -483,15 +483,8 @@ void MachODumper::printSectionHeaders(const MachOObjectFile *Obj) { } } - if (opts::SectionData) { - bool IsBSS = Section.isBSS(); - if (!IsBSS) { - StringRef Data; - error(Section.getContents(Data)); - - W.printBinaryBlock("SectionData", Data); - } - } + if (opts::SectionData && !Section.isBSS()) + W.printBinaryBlock("SectionData", unwrapOrError(Section.getContents())); } } @@ -660,18 +653,16 @@ void MachODumper::printStackMap() const { if (StackMapSection == object::SectionRef()) return; - StringRef StackMapContents; - StackMapSection.getContents(StackMapContents); - ArrayRef<uint8_t> StackMapContentsArray( - reinterpret_cast<const uint8_t*>(StackMapContents.data()), - StackMapContents.size()); + StringRef StackMapContents = unwrapOrError(StackMapSection.getContents()); + ArrayRef<uint8_t> StackMapContentsArray = + arrayRefFromStringRef(StackMapContents); if (Obj->isLittleEndian()) prettyPrintStackMap( - W, StackMapV2Parser<support::little>(StackMapContentsArray)); + W, StackMapParser<support::little>(StackMapContentsArray)); else - prettyPrintStackMap(W, - StackMapV2Parser<support::big>(StackMapContentsArray)); + prettyPrintStackMap( + W, StackMapParser<support::big>(StackMapContentsArray)); } void MachODumper::printNeededLibraries() { @@ -695,10 +686,10 @@ void MachODumper::printNeededLibraries() { } } - std::stable_sort(Libs.begin(), Libs.end()); + llvm::stable_sort(Libs); for (const auto &L : Libs) { - outs() << " " << L << "\n"; + W.startLine() << L << "\n"; } } diff --git a/contrib/llvm/tools/llvm-readobj/ObjDumper.cpp b/contrib/llvm/tools/llvm-readobj/ObjDumper.cpp index a725140c9d33..0a9e22c8a71c 100644 --- a/contrib/llvm/tools/llvm-readobj/ObjDumper.cpp +++ b/contrib/llvm/tools/llvm-readobj/ObjDumper.cpp @@ -1,9 +1,8 @@ //===-- ObjDumper.cpp - Base dumper class -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -17,8 +16,10 @@ #include "llvm-readobj.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Error.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/raw_ostream.h" +#include <map> namespace llvm { @@ -32,116 +33,127 @@ static void printAsPrintable(raw_ostream &W, const uint8_t *Start, size_t Len) { W << (isPrint(Start[i]) ? static_cast<char>(Start[i]) : '.'); } -static Expected<object::SectionRef> -getSecNameOrIndexAsSecRef(const object::ObjectFile *Obj, StringRef SecName) { - char *StrPtr; - long SectionIndex = strtol(SecName.data(), &StrPtr, 10); - object::SectionRef Section; - long SecIndex; - if (Obj->isELF()) - SecIndex = 0; - else - SecIndex = 1; - for (object::SectionRef SecRef : Obj->sections()) { - if (*StrPtr) { - StringRef SectionName; - - if (std::error_code E = SecRef.getName(SectionName)) - return errorCodeToError(E); - - if (SectionName == SecName) - return SecRef; - } else if (SecIndex == SectionIndex) - return SecRef; +static std::vector<object::SectionRef> +getSectionRefsByNameOrIndex(const object::ObjectFile *Obj, + ArrayRef<std::string> Sections) { + std::vector<object::SectionRef> Ret; + std::map<std::string, bool> SecNames; + std::map<unsigned, bool> SecIndices; + unsigned SecIndex; + for (StringRef Section : Sections) { + if (!Section.getAsInteger(0, SecIndex)) + SecIndices.emplace(SecIndex, false); + else + SecNames.emplace(Section, false); + } + SecIndex = Obj->isELF() ? 0 : 1; + for (object::SectionRef SecRef : Obj->sections()) { + StringRef SecName; + error(SecRef.getName(SecName)); + auto NameIt = SecNames.find(SecName); + if (NameIt != SecNames.end()) + NameIt->second = true; + auto IndexIt = SecIndices.find(SecIndex); + if (IndexIt != SecIndices.end()) + IndexIt->second = true; + if (NameIt != SecNames.end() || IndexIt != SecIndices.end()) + Ret.push_back(SecRef); SecIndex++; } - return make_error<StringError>("invalid section reference", - object::object_error::parse_failed); + + for (const std::pair<std::string, bool> &S : SecNames) + if (!S.second) + reportWarning(formatv("could not find section '{0}'", S.first).str()); + for (std::pair<unsigned, bool> S : SecIndices) + if (!S.second) + reportWarning(formatv("could not find section {0}", S.first).str()); + + return Ret; } -void ObjDumper::printSectionAsString(const object::ObjectFile *Obj, - StringRef SecName) { - Expected<object::SectionRef> SectionRefOrError = - getSecNameOrIndexAsSecRef(Obj, SecName); - if (!SectionRefOrError) - error(std::move(SectionRefOrError)); - object::SectionRef Section = *SectionRefOrError; - StringRef SectionName; - - if (std::error_code E = Section.getName(SectionName)) - error(E); - W.startLine() << "String dump of section '" << SectionName << "':\n"; - - StringRef SectionContent; - Section.getContents(SectionContent); - - 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; +void ObjDumper::printSectionsAsString(const object::ObjectFile *Obj, + ArrayRef<std::string> Sections) { + bool First = true; + for (object::SectionRef Section : + getSectionRefsByNameOrIndex(Obj, Sections)) { + StringRef SectionName; + error(Section.getName(SectionName)); + if (!First) + W.startLine() << '\n'; + First = false; + W.startLine() << "String dump of section '" << SectionName << "':\n"; + + StringRef SectionContent = unwrapOrError(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; } - W.startLine() << format("[%6tx] ", CurrentWord - SecContent); - printAsPrintable(W.startLine(), CurrentWord, WordSize); - W.startLine() << '\n'; - CurrentWord += WordSize + 1; } } -void ObjDumper::printSectionAsHex(const object::ObjectFile *Obj, - StringRef SecName) { - Expected<object::SectionRef> SectionRefOrError = - getSecNameOrIndexAsSecRef(Obj, SecName); - if (!SectionRefOrError) - error(std::move(SectionRefOrError)); - object::SectionRef Section = *SectionRefOrError; - StringRef SectionName; - - if (std::error_code E = Section.getName(SectionName)) - error(E); - W.startLine() << "Hex dump of section '" << SectionName << "':\n"; - - StringRef SectionContent; - Section.getContents(SectionContent); - const uint8_t *SecContent = SectionContent.bytes_begin(); - const uint8_t *SecEnd = SecContent + SectionContent.size(); - - for (const uint8_t *SecPtr = SecContent; SecPtr < SecEnd; SecPtr += 16) { - const uint8_t *TmpSecPtr = SecPtr; - uint8_t i; - uint8_t k; - - W.startLine() << format_hex(SecPtr - SecContent, 10); - W.startLine() << ' '; - for (i = 0; TmpSecPtr < SecEnd && i < 4; ++i) { - for (k = 0; TmpSecPtr < SecEnd && k < 4; k++, TmpSecPtr++) { - uint8_t Val = *(reinterpret_cast<const uint8_t *>(TmpSecPtr)); - W.startLine() << format_hex_no_prefix(Val, 2); - } +void ObjDumper::printSectionsAsHex(const object::ObjectFile *Obj, + ArrayRef<std::string> Sections) { + bool First = true; + for (object::SectionRef Section : + getSectionRefsByNameOrIndex(Obj, Sections)) { + StringRef SectionName; + error(Section.getName(SectionName)); + if (!First) + W.startLine() << '\n'; + First = false; + W.startLine() << "Hex dump of section '" << SectionName << "':\n"; + + StringRef SectionContent = unwrapOrError(Section.getContents()); + const uint8_t *SecContent = SectionContent.bytes_begin(); + const uint8_t *SecEnd = SecContent + SectionContent.size(); + + for (const uint8_t *SecPtr = SecContent; SecPtr < SecEnd; SecPtr += 16) { + const uint8_t *TmpSecPtr = SecPtr; + uint8_t i; + uint8_t k; + + W.startLine() << format_hex(Section.getAddress() + (SecPtr - SecContent), + 10); W.startLine() << ' '; - } + for (i = 0; TmpSecPtr < SecEnd && i < 4; ++i) { + for (k = 0; TmpSecPtr < SecEnd && k < 4; k++, TmpSecPtr++) { + uint8_t Val = *(reinterpret_cast<const uint8_t *>(TmpSecPtr)); + W.startLine() << format_hex_no_prefix(Val, 2); + } + W.startLine() << ' '; + } - // We need to print the correct amount of spaces to match the format. - // We are adding the (4 - i) last rows that are 8 characters each. - // Then, the (4 - i) spaces that are in between the rows. - // Least, if we cut in a middle of a row, we add the remaining characters, - // which is (8 - (k * 2)) - if (i < 4) - W.startLine() << format("%*c", (4 - i) * 8 + (4 - i) + (8 - (k * 2)), - ' '); - - TmpSecPtr = SecPtr; - for (i = 0; TmpSecPtr + i < SecEnd && i < 16; ++i) - W.startLine() << (isPrint(TmpSecPtr[i]) ? static_cast<char>(TmpSecPtr[i]) - : '.'); - - W.startLine() << '\n'; + // We need to print the correct amount of spaces to match the format. + // We are adding the (4 - i) last rows that are 8 characters each. + // Then, the (4 - i) spaces that are in between the rows. + // Least, if we cut in a middle of a row, we add the remaining characters, + // which is (8 - (k * 2)). + if (i < 4) + W.startLine() << format("%*c", (4 - i) * 8 + (4 - i) + (8 - (k * 2)), + ' '); + + TmpSecPtr = SecPtr; + for (i = 0; TmpSecPtr + i < SecEnd && i < 16; ++i) + W.startLine() << (isPrint(TmpSecPtr[i]) + ? static_cast<char>(TmpSecPtr[i]) + : '.'); + + W.startLine() << '\n'; + } } } diff --git a/contrib/llvm/tools/llvm-readobj/ObjDumper.h b/contrib/llvm/tools/llvm-readobj/ObjDumper.h index 13de563469ab..aaabfa2ca2e8 100644 --- a/contrib/llvm/tools/llvm-readobj/ObjDumper.h +++ b/contrib/llvm/tools/llvm-readobj/ObjDumper.h @@ -1,9 +1,8 @@ //===-- ObjDumper.h ---------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -15,6 +14,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Support/CommandLine.h" namespace llvm { namespace object { @@ -22,8 +22,9 @@ class COFFImportFile; class ObjectFile; } namespace codeview { +class GlobalTypeTableBuilder; class MergingTypeTableBuilder; -} +} // namespace codeview class ScopedPrinter; @@ -35,18 +36,30 @@ public: virtual void printFileHeaders() = 0; virtual void printSectionHeaders() = 0; virtual void printRelocations() = 0; - virtual void printSymbols() = 0; - virtual void printDynamicSymbols() = 0; + virtual void printSymbols(bool PrintSymbols, bool PrintDynamicSymbols) { + if (PrintSymbols) + printSymbols(); + if (PrintDynamicSymbols) + printDynamicSymbols(); + } + virtual void printProgramHeaders(bool PrintProgramHeaders, + cl::boolOrDefault PrintSectionMapping) { + if (PrintProgramHeaders) + printProgramHeaders(); + if (PrintSectionMapping == cl::BOU_TRUE) + printSectionMapping(); + } + virtual void printUnwindInfo() = 0; // Only implemented for ELF at this time. virtual void printDynamicRelocations() { } virtual void printDynamicTable() { } virtual void printNeededLibraries() { } - virtual void printProgramHeaders() { } virtual void printSectionAsHex(StringRef SectionName) {} virtual void printHashTable() { } virtual void printGnuHashTable() { } + virtual void printHashSymbols() {} virtual void printLoadName() {} virtual void printVersionInfo() {} virtual void printGroupSections() {} @@ -76,7 +89,10 @@ public: virtual void printCodeViewDebugInfo() { } virtual void mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs, - llvm::codeview::MergingTypeTableBuilder &CVTypes) {} + llvm::codeview::MergingTypeTableBuilder &CVTypes, + llvm::codeview::GlobalTypeTableBuilder &GlobalCVIDs, + llvm::codeview::GlobalTypeTableBuilder &GlobalCVTypes, + bool GHash) {} // Only implemented for MachO. virtual void printMachODataInCode() { } @@ -88,11 +104,19 @@ public: virtual void printStackMap() const = 0; - void printSectionAsString(const object::ObjectFile *Obj, StringRef SecName); - void printSectionAsHex(const object::ObjectFile *Obj, StringRef SecName); + void printSectionsAsString(const object::ObjectFile *Obj, + ArrayRef<std::string> Sections); + void printSectionsAsHex(const object::ObjectFile *Obj, + ArrayRef<std::string> Sections); protected: ScopedPrinter &W; + +private: + virtual void printSymbols() {} + virtual void printDynamicSymbols() {} + virtual void printProgramHeaders() {} + virtual void printSectionMapping() {} }; std::error_code createCOFFDumper(const object::ObjectFile *Obj, @@ -111,12 +135,16 @@ std::error_code createWasmDumper(const object::ObjectFile *Obj, ScopedPrinter &Writer, std::unique_ptr<ObjDumper> &Result); +std::error_code createXCOFFDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result); + void dumpCOFFImportFile(const object::COFFImportFile *File, ScopedPrinter &Writer); -void dumpCodeViewMergedTypes( - ScopedPrinter &Writer, llvm::codeview::MergingTypeTableBuilder &IDTable, - llvm::codeview::MergingTypeTableBuilder &TypeTable); +void dumpCodeViewMergedTypes(ScopedPrinter &Writer, + ArrayRef<ArrayRef<uint8_t>> IpiRecords, + ArrayRef<ArrayRef<uint8_t>> TpiRecords); } // namespace llvm diff --git a/contrib/llvm/tools/llvm-readobj/StackMapPrinter.h b/contrib/llvm/tools/llvm-readobj/StackMapPrinter.h index 77a054b178a5..ef7575640268 100644 --- a/contrib/llvm/tools/llvm-readobj/StackMapPrinter.h +++ b/contrib/llvm/tools/llvm-readobj/StackMapPrinter.h @@ -1,9 +1,8 @@ //===-------- StackMapPrinter.h - Pretty-print stackmaps --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -48,24 +47,24 @@ void prettyPrintStackMap(ScopedPrinter &W, const StackMapParserT &SMP) { OS << " #" << ++LocationIndex << ": "; switch (Loc.getKind()) { case StackMapParserT::LocationKind::Register: - OS << "Register R#" << Loc.getDwarfRegNum() << "\n"; + OS << "Register R#" << Loc.getDwarfRegNum(); break; case StackMapParserT::LocationKind::Direct: - OS << "Direct R#" << Loc.getDwarfRegNum() << " + " << Loc.getOffset() - << "\n"; + OS << "Direct R#" << Loc.getDwarfRegNum() << " + " << Loc.getOffset(); break; case StackMapParserT::LocationKind::Indirect: OS << "Indirect [R#" << Loc.getDwarfRegNum() << " + " << Loc.getOffset() - << "]\n"; + << "]"; break; case StackMapParserT::LocationKind::Constant: - OS << "Constant " << Loc.getSmallConstant() << "\n"; + OS << "Constant " << Loc.getSmallConstant(); break; case StackMapParserT::LocationKind::ConstantIndex: OS << "ConstantIndex #" << Loc.getConstantIndex() << " (" - << SMP.getConstant(Loc.getConstantIndex()).getValue() << ")\n"; + << SMP.getConstant(Loc.getConstantIndex()).getValue() << ")"; break; } + OS << ", size: " << Loc.getSizeInBytes() << "\n"; } raw_ostream &OS = W.startLine(); diff --git a/contrib/llvm/tools/llvm-readobj/WasmDumper.cpp b/contrib/llvm/tools/llvm-readobj/WasmDumper.cpp index 79d3db4e2d29..041a9a15bdb6 100644 --- a/contrib/llvm/tools/llvm-readobj/WasmDumper.cpp +++ b/contrib/llvm/tools/llvm-readobj/WasmDumper.cpp @@ -1,9 +1,8 @@ //===-- WasmDumper.cpp - Wasm-specific object file dumper -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -33,11 +32,25 @@ static const EnumEntry<unsigned> WasmSymbolTypes[] = { static const EnumEntry<uint32_t> WasmSectionTypes[] = { #define ENUM_ENTRY(X) \ { #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(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE), - ENUM_ENTRY(DATA), + 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(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE), + ENUM_ENTRY(DATA), ENUM_ENTRY(DATACOUNT), +#undef ENUM_ENTRY +}; + +static const EnumEntry<unsigned> WasmSymbolFlags[] = { +#define ENUM_ENTRY(X) \ + { #X, wasm::WASM_SYMBOL_##X } + ENUM_ENTRY(BINDING_GLOBAL), + ENUM_ENTRY(BINDING_WEAK), + ENUM_ENTRY(BINDING_LOCAL), + ENUM_ENTRY(VISIBILITY_DEFAULT), + ENUM_ENTRY(VISIBILITY_HIDDEN), + ENUM_ENTRY(UNDEFINED), + ENUM_ENTRY(EXPORTED), + ENUM_ENTRY(EXPLICIT_NAME), #undef ENUM_ENTRY }; @@ -49,8 +62,6 @@ public: void printFileHeaders() override; void printSectionHeaders() override; void printRelocations() override; - void printSymbols() override; - void printDynamicSymbols() override { llvm_unreachable("unimplemented"); } void printUnwindInfo() override { llvm_unreachable("unimplemented"); } void printStackMap() const override { llvm_unreachable("unimplemented"); } @@ -59,6 +70,9 @@ protected: void printRelocation(const SectionRef &Section, const RelocationRef &Reloc); private: + void printSymbols() override; + void printDynamicSymbols() override { llvm_unreachable("unimplemented"); } + const WasmObjectFile *Obj; }; @@ -80,11 +94,11 @@ void WasmDumper::printRelocation(const SectionRef &Section, bool HasAddend = false; switch (RelocType) { - case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB: - case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB: - case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32: - case wasm::R_WEBASSEMBLY_FUNCTION_OFFSET_I32: - case wasm::R_WEBASSEMBLY_SECTION_OFFSET_I32: + case wasm::R_WASM_MEMORY_ADDR_LEB: + case wasm::R_WASM_MEMORY_ADDR_SLEB: + case wasm::R_WASM_MEMORY_ADDR_I32: + case wasm::R_WASM_FUNCTION_OFFSET_I32: + case wasm::R_WASM_SECTION_OFFSET_I32: HasAddend = true; break; default: @@ -209,7 +223,19 @@ void WasmDumper::printSymbol(const SymbolRef &Sym) { WasmSymbol Symbol = Obj->getWasmSymbol(Sym.getRawDataRefImpl()); W.printString("Name", Symbol.Info.Name); W.printEnum("Type", Symbol.Info.Kind, makeArrayRef(WasmSymbolTypes)); - W.printHex("Flags", Symbol.Info.Flags); + W.printFlags("Flags", Symbol.Info.Flags, makeArrayRef(WasmSymbolFlags)); + + if (Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) { + W.printString("ImportName", Symbol.Info.ImportName); + W.printString("ImportModule", Symbol.Info.ImportModule); + } + if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) { + W.printHex("ElementIndex", Symbol.Info.ElementIndex); + } else if (!(Symbol.Info.Flags & wasm::WASM_SYMBOL_UNDEFINED)) { + W.printHex("Offset", Symbol.Info.DataRef.Offset); + W.printHex("Segment", Symbol.Info.DataRef.Segment); + W.printHex("Size", Symbol.Info.DataRef.Size); + } } } // namespace @@ -219,7 +245,7 @@ namespace llvm { std::error_code createWasmDumper(const object::ObjectFile *Obj, ScopedPrinter &Writer, std::unique_ptr<ObjDumper> &Result) { - const WasmObjectFile *WasmObj = dyn_cast<WasmObjectFile>(Obj); + const auto *WasmObj = dyn_cast<WasmObjectFile>(Obj); assert(WasmObj && "createWasmDumper called with non-wasm object"); Result.reset(new WasmDumper(WasmObj, Writer)); diff --git a/contrib/llvm/tools/llvm-readobj/Win64EHDumper.cpp b/contrib/llvm/tools/llvm-readobj/Win64EHDumper.cpp index f7e56b361542..e64b8f157180 100644 --- a/contrib/llvm/tools/llvm-readobj/Win64EHDumper.cpp +++ b/contrib/llvm/tools/llvm-readobj/Win64EHDumper.cpp @@ -1,9 +1,8 @@ //===- Win64EHDumper.cpp - Win64 EH Printer ---------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-readobj/Win64EHDumper.h b/contrib/llvm/tools/llvm-readobj/Win64EHDumper.h index 772f68bf283f..97458c916bec 100644 --- a/contrib/llvm/tools/llvm-readobj/Win64EHDumper.h +++ b/contrib/llvm/tools/llvm-readobj/Win64EHDumper.h @@ -1,9 +1,8 @@ //===- Win64EHDumper.h - Win64 EH Printing ----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-readobj/WindowsResourceDumper.cpp b/contrib/llvm/tools/llvm-readobj/WindowsResourceDumper.cpp index 1f568a963671..13989f696d9d 100644 --- a/contrib/llvm/tools/llvm-readobj/WindowsResourceDumper.cpp +++ b/contrib/llvm/tools/llvm-readobj/WindowsResourceDumper.cpp @@ -1,9 +1,8 @@ //===-- WindowsResourceDumper.cpp - Windows Resource printer --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-readobj/WindowsResourceDumper.h b/contrib/llvm/tools/llvm-readobj/WindowsResourceDumper.h index ca6da4046605..6a5878804eb1 100644 --- a/contrib/llvm/tools/llvm-readobj/WindowsResourceDumper.h +++ b/contrib/llvm/tools/llvm-readobj/WindowsResourceDumper.h @@ -1,9 +1,8 @@ //===- WindowsResourceDumper.h - Windows Resource printer -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/contrib/llvm/tools/llvm-readobj/XCOFFDumper.cpp b/contrib/llvm/tools/llvm-readobj/XCOFFDumper.cpp new file mode 100644 index 000000000000..6f260f91537f --- /dev/null +++ b/contrib/llvm/tools/llvm-readobj/XCOFFDumper.cpp @@ -0,0 +1,190 @@ +//===-- XCOFFDumper.cpp - XCOFF dumping utility -----------------*- 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 implements an XCOFF specific dumper for llvm-readobj. +// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "ObjDumper.h" +#include "llvm-readobj.h" +#include "llvm/Object/XCOFFObjectFile.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace object; + +namespace { + +class XCOFFDumper : public ObjDumper { +public: + XCOFFDumper(const XCOFFObjectFile &Obj, ScopedPrinter &Writer) + : ObjDumper(Writer), Obj(Obj) {} + + void printFileHeaders() override; + void printSectionHeaders() override; + void printRelocations() override; + void printSymbols() override; + void printDynamicSymbols() override; + void printUnwindInfo() override; + void printStackMap() const override; + void printNeededLibraries() override; + +private: + template <typename T> void printSectionHeaders(ArrayRef<T> Sections); + + const XCOFFObjectFile &Obj; + + // Least significant 3 bits are reserved. + static constexpr unsigned SectionFlagsReservedMask = 0x7; +}; +} // anonymous namespace + +void XCOFFDumper::printFileHeaders() { + DictScope DS(W, "FileHeader"); + W.printHex("Magic", Obj.getMagic()); + W.printNumber("NumberOfSections", Obj.getNumberOfSections()); + + // Negative timestamp values are reserved for future use. + int32_t TimeStamp = Obj.getTimeStamp(); + if (TimeStamp > 0) { + // This handling of the time stamp assumes that the host system's time_t is + // compatible with AIX time_t. If a platform is not compatible, the lit + // tests will let us know. + time_t TimeDate = TimeStamp; + + char FormattedTime[21] = {}; + size_t BytesWritten = + strftime(FormattedTime, 21, "%Y-%m-%dT%H:%M:%SZ", gmtime(&TimeDate)); + if (BytesWritten) + W.printHex("TimeStamp", FormattedTime, TimeStamp); + else + W.printHex("Timestamp", TimeStamp); + } else { + W.printHex("TimeStamp", TimeStamp == 0 ? "None" : "Reserved Value", + TimeStamp); + } + + // The number of symbol table entries is an unsigned value in 64-bit objects + // and a signed value (with negative values being 'reserved') in 32-bit + // objects. + if (Obj.is64Bit()) { + W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset64()); + W.printNumber("SymbolTableEntries", Obj.getNumberOfSymbolTableEntries64()); + } else { + W.printHex("SymbolTableOffset", Obj.getSymbolTableOffset32()); + int32_t SymTabEntries = Obj.getRawNumberOfSymbolTableEntries32(); + if (SymTabEntries >= 0) + W.printNumber("SymbolTableEntries", SymTabEntries); + else + W.printHex("SymbolTableEntries", "Reserved Value", SymTabEntries); + } + + W.printHex("OptionalHeaderSize", Obj.getOptionalHeaderSize()); + W.printHex("Flags", Obj.getFlags()); + + // TODO FIXME Add support for the auxiliary header (if any) once + // XCOFFObjectFile has the necessary support. +} + +void XCOFFDumper::printSectionHeaders() { + if (Obj.is64Bit()) + printSectionHeaders(Obj.sections64()); + else + printSectionHeaders(Obj.sections32()); +} + +void XCOFFDumper::printRelocations() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printSymbols() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printDynamicSymbols() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printUnwindInfo() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printStackMap() const { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +void XCOFFDumper::printNeededLibraries() { + llvm_unreachable("Unimplemented functionality for XCOFFDumper"); +} + +static const EnumEntry<XCOFF::SectionTypeFlags> SectionTypeFlagsNames[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(STYP_PAD), ECase(STYP_DWARF), ECase(STYP_TEXT), + ECase(STYP_DATA), ECase(STYP_BSS), ECase(STYP_EXCEPT), + ECase(STYP_INFO), ECase(STYP_TDATA), ECase(STYP_TBSS), + ECase(STYP_LOADER), ECase(STYP_DEBUG), ECase(STYP_TYPCHK), + ECase(STYP_OVRFLO) +#undef ECase +}; + +template <typename T> +void XCOFFDumper::printSectionHeaders(ArrayRef<T> Sections) { + ListScope Group(W, "Sections"); + + uint16_t Index = 1; + for (const T &Sec : Sections) { + DictScope SecDS(W, "Section"); + + W.printNumber("Index", Index++); + W.printString("Name", Sec.getName()); + + W.printHex("PhysicalAddress", Sec.PhysicalAddress); + W.printHex("VirtualAddress", Sec.VirtualAddress); + W.printHex("Size", Sec.SectionSize); + W.printHex("RawDataOffset", Sec.FileOffsetToRawData); + W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo); + W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo); + + // TODO Need to add overflow handling when NumberOfX == _OVERFLOW_MARKER + // in 32-bit object files. + W.printNumber("NumberOfRelocations", Sec.NumberOfRelocations); + W.printNumber("NumberOfLineNumbers", Sec.NumberOfLineNumbers); + + // The most significant 16-bits represent the DWARF section subtype. For + // now we just dump the section type flags. + uint16_t Flags = Sec.Flags & 0xffffu; + if (Flags & SectionFlagsReservedMask) + W.printHex("Flags", "Reserved", Flags); + else + W.printEnum("Type", Flags, makeArrayRef(SectionTypeFlagsNames)); + } + + if (opts::SectionRelocations) + report_fatal_error("Dumping section relocations is unimplemented"); + + if (opts::SectionSymbols) + report_fatal_error("Dumping symbols is unimplemented"); + + if (opts::SectionData) + report_fatal_error("Dumping section data is unimplemented"); +} + +namespace llvm { +std::error_code createXCOFFDumper(const object::ObjectFile *Obj, + ScopedPrinter &Writer, + std::unique_ptr<ObjDumper> &Result) { + const XCOFFObjectFile *XObj = dyn_cast<XCOFFObjectFile>(Obj); + if (!XObj) + return readobj_error::unsupported_obj_file_format; + + Result.reset(new XCOFFDumper(*XObj, Writer)); + return readobj_error::success; +} +} // namespace llvm diff --git a/contrib/llvm/tools/llvm-readobj/llvm-readobj.cpp b/contrib/llvm/tools/llvm-readobj/llvm-readobj.cpp index 81ce7a590364..1bd5bb74bf29 100644 --- a/contrib/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/contrib/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -1,9 +1,8 @@ //===- llvm-readobj.cpp - Dump contents of an Object File -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -23,6 +22,7 @@ #include "Error.h" #include "ObjDumper.h" #include "WindowsResourceDumper.h" +#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFFImportFile.h" @@ -39,6 +39,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/WithColor.h" using namespace llvm; using namespace llvm::object; @@ -48,7 +49,7 @@ namespace opts { cl::desc("<input object files>"), cl::ZeroOrMore); - // -all, -a + // --all, -a cl::opt<bool> All("all", cl::desc("Equivalent to setting: --file-headers, --program-headers, " @@ -65,7 +66,7 @@ namespace opts { cl::alias HeadersShort("e", cl::desc("Alias for --headers"), cl::aliasopt(Headers)); - // -wide, -W + // --wide, -W cl::opt<bool> WideOutput("wide", cl::desc("Ignored for compatibility with GNU readelf"), cl::Hidden); @@ -73,7 +74,7 @@ namespace opts { cl::desc("Alias for --wide"), cl::aliasopt(WideOutput)); - // -file-headers, -file-header, -h + // --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"), @@ -82,7 +83,7 @@ namespace opts { cl::desc("Alias for --file-headers"), cl::aliasopt(FileHeaders)); - // -section-headers, -sections, -S + // --section-headers, --sections, -S // Also -s in llvm-readobj mode. cl::opt<bool> SectionHeaders("section-headers", cl::desc("Display all section headers.")); @@ -92,22 +93,27 @@ namespace opts { cl::desc("Alias for --section-headers"), cl::aliasopt(SectionHeaders), cl::NotHidden); - // -section-relocations - // Also -sr in llvm-readobj mode. + // --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. + // --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. + // --section-data + // Also --sd in llvm-readobj mode. cl::opt<bool> SectionData("section-data", cl::desc("Display section data for each section shown.")); - // -relocations, -relocs, -r + // --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"), @@ -115,36 +121,43 @@ namespace opts { cl::alias RelocationsGNU("relocs", cl::desc("Alias for --relocations"), cl::aliasopt(Relocations)); - // -notes, -n + // --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 + // --dyn-relocations cl::opt<bool> DynRelocs("dyn-relocations", cl::desc("Display the dynamic relocation entries in the file")); - // -symbols + // --symbols // Also -s in llvm-readelf mode, or -t in llvm-readobj mode. - cl::opt<bool> Symbols("symbols", - cl::desc("Display the symbol table")); + 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. + // --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)); - // -unwind, -u + // --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 + // --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"), @@ -152,11 +165,11 @@ namespace opts { cl::alias DynamicTableAlias("dynamic", cl::desc("Alias for --dynamic-table"), cl::aliasopt(DynamicTable)); - // -needed-libs + // --needed-libs cl::opt<bool> NeededLibraries("needed-libs", cl::desc("Display the needed libraries")); - // -program-headers, -segments, -l + // --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"), @@ -164,149 +177,161 @@ namespace opts { cl::alias SegmentsAlias("segments", cl::desc("Alias for --program-headers"), cl::aliasopt(ProgramHeaders)); - // -string-dump, -p + // --string-dump, -p cl::list<std::string> StringDump("string-dump", cl::desc("<number|name>"), cl::ZeroOrMore); cl::alias StringDumpShort("p", cl::desc("Alias for --string-dump"), - cl::aliasopt(StringDump)); + cl::aliasopt(StringDump), cl::Prefix); - // -hex-dump, -x + // --hex-dump, -x cl::list<std::string> HexDump("hex-dump", cl::desc("<number|name>"), cl::ZeroOrMore); cl::alias HexDumpShort("x", cl::desc("Alias for --hex-dump"), - cl::aliasopt(HexDump)); + cl::aliasopt(HexDump), cl::Prefix); - // -hash-table + // --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 + // --gnu-hash-table cl::opt<bool> GnuHashTable("gnu-hash-table", cl::desc("Display ELF .gnu.hash section")); - // -expand-relocs + // --expand-relocs cl::opt<bool> ExpandRelocs("expand-relocs", cl::desc("Expand each shown relocation to multiple lines")); - // -raw-relr + // --raw-relr cl::opt<bool> RawRelr("raw-relr", cl::desc("Do not decode relocations in SHT_RELR section, display raw contents")); - // -codeview + // --codeview cl::opt<bool> CodeView("codeview", cl::desc("Display CodeView debug information")); - // -codeview-merged-types + // --codeview-merged-types cl::opt<bool> CodeViewMergedTypes("codeview-merged-types", cl::desc("Display the merged CodeView type stream")); - // -codeview-subsection-bytes + // --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")); - // -arm-attributes + // --arm-attributes cl::opt<bool> ARMAttributes("arm-attributes", cl::desc("Display the ARM attributes section")); - // -mips-plt-got + // --mips-plt-got cl::opt<bool> MipsPLTGOT("mips-plt-got", cl::desc("Display the MIPS GOT and PLT GOT sections")); - // -mips-abi-flags + // --mips-abi-flags cl::opt<bool> MipsABIFlags("mips-abi-flags", cl::desc("Display the MIPS.abiflags section")); - // -mips-reginfo + // --mips-reginfo cl::opt<bool> MipsReginfo("mips-reginfo", cl::desc("Display the MIPS .reginfo section")); - // -mips-options + // --mips-options cl::opt<bool> MipsOptions("mips-options", cl::desc("Display the MIPS .MIPS.options section")); - // -coff-imports + // --coff-imports cl::opt<bool> COFFImports("coff-imports", cl::desc("Display the PE/COFF import table")); - // -coff-exports + // --coff-exports cl::opt<bool> COFFExports("coff-exports", cl::desc("Display the PE/COFF export table")); - // -coff-directives + // --coff-directives cl::opt<bool> COFFDirectives("coff-directives", cl::desc("Display the PE/COFF .drectve section")); - // -coff-basereloc + // --coff-basereloc cl::opt<bool> COFFBaseRelocs("coff-basereloc", cl::desc("Display the PE/COFF .reloc section")); - // -coff-debug-directory + // --coff-debug-directory cl::opt<bool> COFFDebugDirectory("coff-debug-directory", cl::desc("Display the PE/COFF debug directory")); - // -coff-resources + // --coff-resources cl::opt<bool> COFFResources("coff-resources", cl::desc("Display the PE/COFF .rsrc section")); - // -coff-load-config + // --coff-load-config cl::opt<bool> COFFLoadConfig("coff-load-config", cl::desc("Display the PE/COFF load config")); - // -elf-linker-options + // --elf-linker-options cl::opt<bool> ELFLinkerOptions("elf-linker-options", cl::desc("Display the ELF .linker-options section")); - // -macho-data-in-code + // --macho-data-in-code cl::opt<bool> MachODataInCode("macho-data-in-code", cl::desc("Display MachO Data in Code command")); - // -macho-indirect-symbols + // --macho-indirect-symbols cl::opt<bool> MachOIndirectSymbols("macho-indirect-symbols", cl::desc("Display MachO indirect symbols")); - // -macho-linker-options + // --macho-linker-options cl::opt<bool> MachOLinkerOptions("macho-linker-options", cl::desc("Display MachO linker options")); - // -macho-segment + // --macho-segment cl::opt<bool> MachOSegment("macho-segment", cl::desc("Display MachO Segment command")); - // -macho-version-min + // --macho-version-min cl::opt<bool> MachOVersionMin("macho-version-min", cl::desc("Display MachO version min command")); - // -macho-dysymtab + // --macho-dysymtab cl::opt<bool> MachODysymtab("macho-dysymtab", cl::desc("Display MachO Dysymtab command")); - // -stackmap + // --stackmap cl::opt<bool> PrintStackMap("stackmap", cl::desc("Display contents of stackmap section")); - // -version-info, -V + // --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 + // --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", @@ -315,7 +340,7 @@ namespace opts { cl::alias SectionGroupsShort("g", cl::desc("Alias for -elf-sections-groups"), cl::aliasopt(SectionGroups)); - // -elf-hash-histogram, -histogram, -I + // --elf-hash-histogram, --histogram, -I cl::opt<bool> HashHistogram( "elf-hash-histogram", cl::desc("Display bucket list histogram for hash sections")); @@ -325,7 +350,7 @@ namespace opts { cl::desc("Alias for --elf-hash-histogram"), cl::aliasopt(HashHistogram)); - // -elf-cg-profile + // --elf-cg-profile cl::opt<bool> CGProfile("elf-cg-profile", cl::desc("Display callgraph profile section")); // -addrsig @@ -338,16 +363,38 @@ namespace opts { 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"); } // namespace opts namespace llvm { LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) { - errs() << "\nError reading file: " << Msg << ".\n"; - errs().flush(); + fouts().flush(); + errs() << "\n"; + WithColor::error(errs()) << Msg << "\n"; exit(1); } +void reportError(StringRef Input, Error Err) { + if (Input == "-") + Input = "<stdin>"; + error(createFileError(Input, std::move(Err))); +} + +void reportWarning(Twine Msg) { + fouts().flush(); + errs() << "\n"; + WithColor::warning(errs()) << Msg << "\n"; +} + +void warn(Error Err) { + handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { + reportWarning(EI.message()); + }); +} + void error(Error EC) { if (!EC) return; @@ -361,28 +408,10 @@ void error(std::error_code EC) { reportError(EC.message()); } -bool relocAddressLess(RelocationRef a, RelocationRef b) { - return a.getOffset() < b.getOffset(); -} - } // namespace llvm static void reportError(StringRef Input, std::error_code EC) { - if (Input == "-") - Input = "<stdin>"; - - reportError(Twine(Input) + ": " + EC.message()); -} - -static void reportError(StringRef Input, Error Err) { - if (Input == "-") - Input = "<stdin>"; - std::string ErrMsg; - { - raw_string_ostream ErrStream(ErrMsg); - logAllUnhandledErrors(std::move(Err), ErrStream, Input + ": "); - } - reportError(ErrMsg); + reportError(Input, errorCodeToError(EC)); } static bool isMipsArch(unsigned Arch) { @@ -399,13 +428,17 @@ static bool isMipsArch(unsigned Arch) { namespace { struct ReadObjTypeTableBuilder { ReadObjTypeTableBuilder() - : Allocator(), IDTable(Allocator), TypeTable(Allocator) {} + : Allocator(), IDTable(Allocator), TypeTable(Allocator), + GlobalIDTable(Allocator), GlobalTypeTable(Allocator) {} llvm::BumpPtrAllocator Allocator; llvm::codeview::MergingTypeTableBuilder IDTable; llvm::codeview::MergingTypeTableBuilder TypeTable; + llvm::codeview::GlobalTypeTableBuilder GlobalIDTable; + llvm::codeview::GlobalTypeTableBuilder GlobalTypeTable; + std::vector<OwningBinary<Binary>> Binaries; }; -} +} // namespace static ReadObjTypeTableBuilder CVTypes; /// Creates an format-specific object file dumper. @@ -423,25 +456,34 @@ static std::error_code createDumper(const ObjectFile *Obj, return createMachODumper(Obj, Writer, Result); if (Obj->isWasm()) return createWasmDumper(Obj, Writer, Result); + if (Obj->isXCOFF()) + return createXCOFFDumper(Obj, Writer, Result); return readobj_error::unsupported_obj_file_format; } /// Dumps the specified object file. -static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer) { +static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, + const Archive *A = nullptr) { + std::string FileStr = + A ? Twine(A->getFileName() + "(" + Obj->getFileName() + ")").str() + : Obj->getFileName().str(); + std::unique_ptr<ObjDumper> Dumper; if (std::error_code EC = createDumper(Obj, Writer, Dumper)) - reportError(Obj->getFileName(), EC); + reportError(FileStr, EC); + Writer.startLine() << "\n"; if (opts::Output == opts::LLVM) { - Writer.startLine() << "\n"; - Writer.printString("File", Obj->getFileName()); + Writer.printString("File", FileStr); Writer.printString("Format", Obj->getFileFormatName()); Writer.printString("Arch", Triple::getArchTypeName( (llvm::Triple::ArchType)Obj->getArch())); Writer.printString("AddressSize", formatv("{0}bit", 8 * Obj->getBytesInAddress())); Dumper->printLoadName(); + } else if (opts::Output == opts::GNU && A) { + Writer.printString("File", FileStr); } if (opts::FileHeaders) @@ -452,26 +494,22 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer) { Dumper->printRelocations(); if (opts::DynRelocs) Dumper->printDynamicRelocations(); - if (opts::Symbols) - Dumper->printSymbols(); - if (opts::DynamicSymbols) - Dumper->printDynamicSymbols(); + if (opts::Symbols || opts::DynamicSymbols) + Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols); + if (opts::HashSymbols) + Dumper->printHashSymbols(); if (opts::UnwindInfo) Dumper->printUnwindInfo(); if (opts::DynamicTable) Dumper->printDynamicTable(); if (opts::NeededLibraries) Dumper->printNeededLibraries(); - if (opts::ProgramHeaders) - Dumper->printProgramHeaders(); + if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE) + Dumper->printProgramHeaders(opts::ProgramHeaders, opts::SectionMapping); if (!opts::StringDump.empty()) - llvm::for_each(opts::StringDump, [&Dumper, Obj](StringRef SectionName) { - Dumper->printSectionAsString(Obj, SectionName); - }); + Dumper->printSectionsAsString(Obj, opts::StringDump); if (!opts::HexDump.empty()) - llvm::for_each(opts::HexDump, [&Dumper, Obj](StringRef SectionName) { - Dumper->printSectionAsHex(Obj, SectionName); - }); + Dumper->printSectionsAsHex(Obj, opts::HexDump); if (opts::HashTable) Dumper->printHashTable(); if (opts::GnuHashTable) @@ -525,7 +563,9 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer) { if (opts::CodeView) Dumper->printCodeViewDebugInfo(); if (opts::CodeViewMergedTypes) - Dumper->mergeCodeViewTypes(CVTypes.IDTable, CVTypes.TypeTable); + Dumper->mergeCodeViewTypes(CVTypes.IDTable, CVTypes.TypeTable, + CVTypes.GlobalIDTable, CVTypes.GlobalTypeTable, + opts::CodeViewEnableGHash); } if (Obj->isMachO()) { if (opts::MachODataInCode) @@ -552,12 +592,12 @@ static void dumpArchive(const Archive *Arc, ScopedPrinter &Writer) { Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) { - reportError(Arc->getFileName(), ChildOrErr.takeError()); + reportError(Arc->getFileName(), std::move(E)); } continue; } if (ObjectFile *Obj = dyn_cast<ObjectFile>(&*ChildOrErr.get())) - dumpObject(Obj, Writer); + dumpObject(Obj, Writer, Arc); else if (COFFImportFile *Imp = dyn_cast<COFFImportFile>(&*ChildOrErr.get())) dumpCOFFImportFile(Imp, Writer); else @@ -583,8 +623,8 @@ static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary, } /// Dumps \a WinRes, Windows Resource (.res) file; -static void dumpWindowsResourceFile(WindowsResource *WinRes) { - ScopedPrinter Printer{outs()}; +static void dumpWindowsResourceFile(WindowsResource *WinRes, + ScopedPrinter &Printer) { WindowsRes::Dumper Dumper(WinRes, Printer); if (auto Err = Dumper.printData()) reportError(WinRes->getFileName(), std::move(Err)); @@ -592,9 +632,7 @@ static void dumpWindowsResourceFile(WindowsResource *WinRes) { /// Opens \a File and dumps it. -static void dumpInput(StringRef File) { - ScopedPrinter Writer(outs()); - +static void dumpInput(StringRef File, ScopedPrinter &Writer) { // Attempt to open the binary. Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File); if (!BinaryOrErr) @@ -611,9 +649,11 @@ static void dumpInput(StringRef File) { else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(&Binary)) dumpCOFFImportFile(Import, Writer); else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(&Binary)) - dumpWindowsResourceFile(WinRes); + dumpWindowsResourceFile(WinRes, Writer); else reportError(File, readobj_error::unrecognized_file_format); + + CVTypes.Binaries.push_back(std::move(*BinaryOrErr)); } /// Registers aliases that should only be allowed by readobj. @@ -656,7 +696,7 @@ static void registerReadelfAliases() { StringRef ArgName = OptEntry.getKey(); cl::Option *Option = OptEntry.getValue(); if (ArgName.size() == 1) - Option->setFormattingFlag(cl::Grouping); + apply(Option, cl::Grouping); } } @@ -699,11 +739,17 @@ int main(int argc, const char *argv[]) { if (opts::InputFilenames.empty()) opts::InputFilenames.push_back("-"); - llvm::for_each(opts::InputFilenames, dumpInput); + ScopedPrinter Writer(fouts()); + for (const std::string &I : opts::InputFilenames) + dumpInput(I, Writer); if (opts::CodeViewMergedTypes) { - ScopedPrinter W(outs()); - dumpCodeViewMergedTypes(W, CVTypes.IDTable, CVTypes.TypeTable); + if (opts::CodeViewEnableGHash) + dumpCodeViewMergedTypes(Writer, CVTypes.GlobalIDTable.records(), + CVTypes.GlobalTypeTable.records()); + else + dumpCodeViewMergedTypes(Writer, CVTypes.IDTable.records(), + CVTypes.TypeTable.records()); } return 0; diff --git a/contrib/llvm/tools/llvm-readobj/llvm-readobj.h b/contrib/llvm/tools/llvm-readobj/llvm-readobj.h index 92ed098dc642..0e02da4cb847 100644 --- a/contrib/llvm/tools/llvm-readobj/llvm-readobj.h +++ b/contrib/llvm/tools/llvm-readobj/llvm-readobj.h @@ -1,9 +1,8 @@ //===-- llvm-readobj.h ----------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -23,6 +22,9 @@ namespace llvm { // Various helper functions. LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg); + void reportError(StringRef Input, Error Err); + void reportWarning(Twine Msg); + void warn(llvm::Error Err); void error(std::error_code EC); void error(llvm::Error EC); template <typename T> T error(llvm::Expected<T> &&E) { @@ -44,18 +46,16 @@ namespace llvm { OS.flush(); reportError(Buf); } - bool relocAddressLess(object::RelocationRef A, - object::RelocationRef B); } // 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> DynamicSymbols; 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; } // namespace opts diff --git a/contrib/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/contrib/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp index 975638ed82d1..a7cc1deb8cf6 100644 --- a/contrib/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/contrib/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -1,9 +1,8 @@ //===-- llvm-rtdyld.cpp - MCJIT Testing Tool ------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -30,9 +29,13 @@ #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Memory.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/MSVCErrorWorkarounds.h" +#include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" + +#include <future> #include <list> using namespace llvm; @@ -74,6 +77,10 @@ Dylibs("dylib", cl::desc("Add library."), cl::ZeroOrMore); +static cl::list<std::string> InputArgv("args", cl::Positional, + cl::desc("<program arguments>..."), + cl::ZeroOrMore, cl::PositionalEatsArgs); + static cl::opt<std::string> TripleName("triple", cl::desc("Target triple for disassembler")); @@ -88,35 +95,28 @@ CheckFiles("check", cl::desc("File containing RuntimeDyld verifier checks."), cl::ZeroOrMore); -// Tracking BUG: 19665 -// http://llvm.org/bugs/show_bug.cgi?id=19665 -// -// Do not change these options to cl::opt<uint64_t> since this silently breaks -// argument parsing. -static cl::opt<unsigned long long> -PreallocMemory("preallocate", - cl::desc("Allocate memory upfront rather than on-demand"), - cl::init(0)); - -static cl::opt<unsigned long long> -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); - -static cl::opt<unsigned long long> -TargetAddrEnd("target-addr-end", - cl::desc("For -verify only: end of phony target address range."), - cl::init(~0ULL), - cl::Hidden); - -static cl::opt<unsigned long long> -TargetSectionSep("target-section-sep", - cl::desc("For -verify only: Separation between sections in " - "phony target address space."), - cl::init(0), - cl::Hidden); +static cl::opt<uint64_t> + PreallocMemory("preallocate", + cl::desc("Allocate memory upfront rather than on-demand"), + cl::init(0)); + +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); + +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); + +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", @@ -138,14 +138,50 @@ PrintAllocationRequests("print-alloc-requests", "manager by RuntimeDyld"), cl::Hidden); +ExitOnError ExitOnErr; + /* *** */ +using SectionIDMap = StringMap<unsigned>; +using FileToSectionIDMap = StringMap<SectionIDMap>; + +void dumpFileToSectionIDMap(const FileToSectionIDMap &FileToSecIDMap) { + for (const auto &KV : FileToSecIDMap) { + llvm::dbgs() << "In " << KV.first() << "\n"; + for (auto &KV2 : KV.second) + llvm::dbgs() << " \"" << KV2.first() << "\" -> " << KV2.second << "\n"; + } +} + +Expected<unsigned> getSectionId(const FileToSectionIDMap &FileToSecIDMap, + StringRef FileName, StringRef SectionName) { + auto I = FileToSecIDMap.find(FileName); + if (I == FileToSecIDMap.end()) + return make_error<StringError>("No file named " + FileName, + inconvertibleErrorCode()); + auto &SectionIDs = I->second; + auto J = SectionIDs.find(SectionName); + if (J == SectionIDs.end()) + return make_error<StringError>("No section named \"" + SectionName + + "\" in file " + FileName, + inconvertibleErrorCode()); + return J->second; +} + // A trivial memory manager that doesn't do anything fancy, just uses the // support library allocation routines directly. class TrivialMemoryManager : public RTDyldMemoryManager { public: - SmallVector<sys::MemoryBlock, 16> FunctionMemory; - SmallVector<sys::MemoryBlock, 16> DataMemory; + struct SectionInfo { + SectionInfo(StringRef Name, sys::MemoryBlock MB, unsigned SectionID) + : Name(Name), MB(std::move(MB)), SectionID(SectionID) {} + std::string Name; + sys::MemoryBlock MB; + unsigned SectionID = ~0U; + }; + + SmallVector<SectionInfo, 16> FunctionMemory; + SmallVector<SectionInfo, 16> DataMemory; uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, @@ -154,6 +190,11 @@ public: unsigned SectionID, StringRef SectionName, bool IsReadOnly) override; + /// If non null, records subsequent Name -> SectionID mappings. + void setSectionIDsMap(SectionIDMap *SecIDMap) { + this->SecIDMap = SecIDMap; + } + void *getPointerToNamedFunction(const std::string &Name, bool AbortOnFailure = true) override { return nullptr; @@ -171,7 +212,15 @@ public: if (I != DummyExterns.end()) return JITSymbol(I->second, JITSymbolFlags::Exported); - return RTDyldMemoryManager::findSymbol(Name); + if (auto Sym = RTDyldMemoryManager::findSymbol(Name)) + return Sym; + else if (auto Err = Sym.takeError()) + ExitOnErr(std::move(Err)); + else + ExitOnErr(make_error<StringError>("Could not find definition for \"" + + Name + "\"", + inconvertibleErrorCode())); + llvm_unreachable("Should have returned or exited by now"); } void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, @@ -193,7 +242,8 @@ public: SlabSize = Size; } - uint8_t *allocateFromSlab(uintptr_t Size, unsigned Alignment, bool isCode) { + uint8_t *allocateFromSlab(uintptr_t Size, unsigned Alignment, bool isCode, + StringRef SectionName, unsigned SectionID) { Size = alignTo(Size, Alignment); if (CurrentSlabOffset + Size > SlabSize) report_fatal_error("Can't allocate enough memory. Tune --preallocate"); @@ -201,9 +251,9 @@ public: uintptr_t OldSlabOffset = CurrentSlabOffset; sys::MemoryBlock MB((void *)OldSlabOffset, Size); if (isCode) - FunctionMemory.push_back(MB); + FunctionMemory.push_back(SectionInfo(SectionName, MB, SectionID)); else - DataMemory.push_back(MB); + DataMemory.push_back(SectionInfo(SectionName, MB, SectionID)); CurrentSlabOffset += Size; return (uint8_t*)OldSlabOffset; } @@ -214,6 +264,7 @@ private: bool UsePreallocation = false; uintptr_t SlabSize = 0; uintptr_t CurrentSlabOffset = 0; + SectionIDMap *SecIDMap = nullptr; }; uint8_t *TrivialMemoryManager::allocateCodeSection(uintptr_t Size, @@ -224,8 +275,12 @@ uint8_t *TrivialMemoryManager::allocateCodeSection(uintptr_t Size, outs() << "allocateCodeSection(Size = " << Size << ", Alignment = " << Alignment << ", SectionName = " << SectionName << ")\n"; + if (SecIDMap) + (*SecIDMap)[SectionName] = SectionID; + if (UsePreallocation) - return allocateFromSlab(Size, Alignment, true /* isCode */); + return allocateFromSlab(Size, Alignment, true /* isCode */, + SectionName, SectionID); std::error_code EC; sys::MemoryBlock MB = @@ -235,7 +290,7 @@ uint8_t *TrivialMemoryManager::allocateCodeSection(uintptr_t Size, EC); if (!MB.base()) report_fatal_error("MemoryManager allocation failed: " + EC.message()); - FunctionMemory.push_back(MB); + FunctionMemory.push_back(SectionInfo(SectionName, MB, SectionID)); return (uint8_t*)MB.base(); } @@ -248,8 +303,12 @@ uint8_t *TrivialMemoryManager::allocateDataSection(uintptr_t Size, outs() << "allocateDataSection(Size = " << Size << ", Alignment = " << Alignment << ", SectionName = " << SectionName << ")\n"; + if (SecIDMap) + (*SecIDMap)[SectionName] = SectionID; + if (UsePreallocation) - return allocateFromSlab(Size, Alignment, false /* isCode */); + return allocateFromSlab(Size, Alignment, false /* isCode */, SectionName, + SectionID); std::error_code EC; sys::MemoryBlock MB = @@ -259,7 +318,7 @@ uint8_t *TrivialMemoryManager::allocateDataSection(uintptr_t Size, EC); if (!MB.base()) report_fatal_error("MemoryManager allocation failed: " + EC.message()); - DataMemory.push_back(MB); + DataMemory.push_back(SectionInfo(SectionName, MB, SectionID)); return (uint8_t*)MB.base(); } @@ -368,6 +427,8 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { } uint64_t Addr = *AddrOrErr; + object::SectionedAddress Address; + uint64_t Size = P.second; // If we're not using the debug object, compute the address of the // symbol in memory (rather than that in the unrelocated object file) @@ -382,16 +443,20 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { object::section_iterator Sec = *SecOrErr; StringRef SecName; Sec->getName(SecName); + Address.SectionIndex = Sec->getIndex(); uint64_t SectionLoadAddress = LoadedObjInfo->getSectionLoadAddress(*Sec); if (SectionLoadAddress != 0) Addr += SectionLoadAddress - Sec->getAddress(); - } + } else if (auto SecOrErr = Sym.getSection()) + Address.SectionIndex = SecOrErr.get()->getIndex(); outs() << "Function: " << *Name << ", Size = " << Size << ", Addr = " << Addr << "\n"; - DILineInfoTable Lines = Context->getLineInfoForAddressRange(Addr, Size); + Address.Address = Addr; + DILineInfoTable Lines = + Context->getLineInfoForAddressRange(Address, Size); for (auto &D : Lines) { outs() << " Line info @ " << D.first - Addr << ": " << D.second.FileName << ", line:" << D.second.Line << "\n"; @@ -464,9 +529,11 @@ static int executeInput() { // Invalidate the instruction cache for each loaded function. for (auto &FM : MemMgr.FunctionMemory) { + auto &FM_MB = FM.MB; + // Make sure the memory is executable. // setExecutable will call InvalidateInstructionCache. - if (auto EC = sys::Memory::protectMappedMemory(FM, + if (auto EC = sys::Memory::protectMappedMemory(FM_MB, sys::Memory::MF_READ | sys::Memory::MF_EXEC)) ErrorAndExit("unable to mark function executable: '" + EC.message() + @@ -478,11 +545,13 @@ static int executeInput() { int (*Main)(int, const char**) = (int(*)(int,const char**)) uintptr_t(MainAddress); - const char **Argv = new const char*[2]; + std::vector<const char *> Argv; // Use the name of the first input object module as argv[0] for the target. - Argv[0] = InputFileList[0].c_str(); - Argv[1] = nullptr; - return Main(1, Argv); + Argv.push_back(InputFileList[0].data()); + for (auto &Arg : InputArgv) + Argv.push_back(Arg.data()); + Argv.push_back(nullptr); + return Main(Argv.size() - 1, Argv.data()); } static int checkAllExpressions(RuntimeDyldChecker &Checker) { @@ -500,10 +569,10 @@ static int checkAllExpressions(RuntimeDyldChecker &Checker) { return 0; } -void applySpecificSectionMappings(RuntimeDyldChecker &Checker) { +void applySpecificSectionMappings(RuntimeDyld &Dyld, + const FileToSectionIDMap &FileToSecIDMap) { for (StringRef Mapping : SpecificSectionMappings) { - size_t EqualsIdx = Mapping.find_first_of("="); std::string SectionIDStr = Mapping.substr(0, EqualsIdx); size_t ComaIdx = Mapping.find_first_of(","); @@ -514,17 +583,10 @@ void applySpecificSectionMappings(RuntimeDyldChecker &Checker) { std::string FileName = SectionIDStr.substr(0, ComaIdx); std::string SectionName = SectionIDStr.substr(ComaIdx + 1); + unsigned SectionID = + ExitOnErr(getSectionId(FileToSecIDMap, FileName, SectionName)); - uint64_t OldAddrInt; - std::string ErrorMsg; - std::tie(OldAddrInt, ErrorMsg) = - Checker.getSectionAddr(FileName, SectionName, true); - - if (ErrorMsg != "") - report_fatal_error(ErrorMsg); - - void* OldAddr = reinterpret_cast<void*>(static_cast<uintptr_t>(OldAddrInt)); - + auto* OldAddr = Dyld.getSectionContent(SectionID).data(); std::string NewAddrStr = Mapping.substr(EqualsIdx + 1); uint64_t NewAddr; @@ -532,7 +594,7 @@ void applySpecificSectionMappings(RuntimeDyldChecker &Checker) { report_fatal_error("Invalid section address in mapping '" + Mapping + "'."); - Checker.getRTDyld().mapSectionAddress(OldAddr, NewAddr); + Dyld.mapSectionAddress(OldAddr, NewAddr); } } @@ -548,21 +610,17 @@ void applySpecificSectionMappings(RuntimeDyldChecker &Checker) { // (e.g. 1 << 32) to stress-test stubs, GOTs, etc. // static void remapSectionsAndSymbols(const llvm::Triple &TargetTriple, - TrivialMemoryManager &MemMgr, - RuntimeDyldChecker &Checker) { + RuntimeDyld &Dyld, + TrivialMemoryManager &MemMgr) { // Set up a work list (section addr/size pairs). - typedef std::list<std::pair<void*, uint64_t>> WorklistT; + typedef std::list<const TrivialMemoryManager::SectionInfo*> WorklistT; WorklistT Worklist; for (const auto& CodeSection : MemMgr.FunctionMemory) - Worklist.push_back(std::make_pair(CodeSection.base(), CodeSection.size())); + Worklist.push_back(&CodeSection); for (const auto& DataSection : MemMgr.DataMemory) - Worklist.push_back(std::make_pair(DataSection.base(), DataSection.size())); - - // Apply any section-specific mappings that were requested on the command - // line. - applySpecificSectionMappings(Checker); + Worklist.push_back(&DataSection); // Keep an "already allocated" mapping of section target addresses to sizes. // Sections whose address mappings aren't specified on the command line will @@ -577,16 +635,16 @@ static void remapSectionsAndSymbols(const llvm::Triple &TargetTriple, I != E;) { WorklistT::iterator Tmp = I; ++I; - auto LoadAddr = Checker.getSectionLoadAddress(Tmp->first); - if (LoadAddr && - *LoadAddr != static_cast<uint64_t>( - reinterpret_cast<uintptr_t>(Tmp->first))) { + auto LoadAddr = Dyld.getSectionLoadAddress((*Tmp)->SectionID); + + if (LoadAddr != static_cast<uint64_t>( + reinterpret_cast<uintptr_t>((*Tmp)->MB.base()))) { // A section will have a LoadAddr of 0 if it wasn't loaded for whatever // reason (e.g. zero byte COFF sections). Don't include those sections in // the allocation map. - if (*LoadAddr != 0) - AlreadyAllocated[*LoadAddr] = Tmp->second; + if (LoadAddr != 0) + AlreadyAllocated[LoadAddr] = (*Tmp)->MB.allocatedSize(); Worklist.erase(Tmp); } } @@ -604,19 +662,20 @@ static void remapSectionsAndSymbols(const llvm::Triple &TargetTriple, // Process any elements remaining in the worklist. while (!Worklist.empty()) { - std::pair<void*, uint64_t> CurEntry = Worklist.front(); + auto *CurEntry = Worklist.front(); Worklist.pop_front(); uint64_t NextSectionAddr = TargetAddrStart; for (const auto &Alloc : AlreadyAllocated) - if (NextSectionAddr + CurEntry.second + TargetSectionSep <= Alloc.first) + if (NextSectionAddr + CurEntry->MB.allocatedSize() + TargetSectionSep <= + Alloc.first) break; else NextSectionAddr = Alloc.first + Alloc.second + TargetSectionSep; - AlreadyAllocated[NextSectionAddr] = CurEntry.second; - Checker.getRTDyld().mapSectionAddress(CurEntry.first, NextSectionAddr); + Dyld.mapSectionAddress(CurEntry->MB.base(), NextSectionAddr); + AlreadyAllocated[NextSectionAddr] = CurEntry->MB.allocatedSize(); } // Add dummy symbols to the memory manager. @@ -688,18 +747,132 @@ static int linkAndVerify() { // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; doPreallocation(MemMgr); + + struct StubID { + unsigned SectionID; + uint32_t Offset; + }; + using StubInfos = StringMap<StubID>; + using StubContainers = StringMap<StubInfos>; + + StubContainers StubMap; RuntimeDyld Dyld(MemMgr, MemMgr); Dyld.setProcessAllSections(true); - RuntimeDyldChecker Checker(Dyld, Disassembler.get(), InstPrinter.get(), - llvm::dbgs()); + + Dyld.setNotifyStubEmitted([&StubMap](StringRef FilePath, + StringRef SectionName, + StringRef SymbolName, unsigned SectionID, + uint32_t StubOffset) { + std::string ContainerName = + (sys::path::filename(FilePath) + "/" + SectionName).str(); + StubMap[ContainerName][SymbolName] = {SectionID, StubOffset}; + }); + + auto GetSymbolInfo = + [&Dyld, &MemMgr]( + StringRef Symbol) -> Expected<RuntimeDyldChecker::MemoryRegionInfo> { + RuntimeDyldChecker::MemoryRegionInfo SymInfo; + + // First get the target address. + if (auto InternalSymbol = Dyld.getSymbol(Symbol)) + SymInfo.setTargetAddress(InternalSymbol.getAddress()); + else { + // Symbol not found in RuntimeDyld. Fall back to external lookup. +#ifdef _MSC_VER + using ExpectedLookupResult = + MSVCPExpected<JITSymbolResolver::LookupResult>; +#else + using ExpectedLookupResult = Expected<JITSymbolResolver::LookupResult>; +#endif + + auto ResultP = std::make_shared<std::promise<ExpectedLookupResult>>(); + auto ResultF = ResultP->get_future(); + + MemMgr.lookup(JITSymbolResolver::LookupSet({Symbol}), + [=](Expected<JITSymbolResolver::LookupResult> Result) { + ResultP->set_value(std::move(Result)); + }); + + auto Result = ResultF.get(); + if (!Result) + return Result.takeError(); + + auto I = Result->find(Symbol); + assert(I != Result->end() && + "Expected symbol address if no error occurred"); + SymInfo.setTargetAddress(I->second.getAddress()); + } + + // Now find the symbol content if possible (otherwise leave content as a + // default-constructed StringRef). + if (auto *SymAddr = Dyld.getSymbolLocalAddress(Symbol)) { + unsigned SectionID = Dyld.getSymbolSectionID(Symbol); + if (SectionID != ~0U) { + char *CSymAddr = static_cast<char *>(SymAddr); + StringRef SecContent = Dyld.getSectionContent(SectionID); + uint64_t SymSize = SecContent.size() - (CSymAddr - SecContent.data()); + SymInfo.setContent(StringRef(CSymAddr, SymSize)); + } + } + return SymInfo; + }; + + auto IsSymbolValid = [&Dyld, GetSymbolInfo](StringRef Symbol) { + if (Dyld.getSymbol(Symbol)) + return true; + auto SymInfo = GetSymbolInfo(Symbol); + if (!SymInfo) { + logAllUnhandledErrors(SymInfo.takeError(), errs(), "RTDyldChecker: "); + return false; + } + return SymInfo->getTargetAddress() != 0; + }; + + FileToSectionIDMap FileToSecIDMap; + + auto GetSectionInfo = [&Dyld, &FileToSecIDMap](StringRef FileName, + StringRef SectionName) + -> Expected<RuntimeDyldChecker::MemoryRegionInfo> { + auto SectionID = getSectionId(FileToSecIDMap, FileName, SectionName); + if (!SectionID) + return SectionID.takeError(); + RuntimeDyldChecker::MemoryRegionInfo SecInfo; + SecInfo.setTargetAddress(Dyld.getSectionLoadAddress(*SectionID)); + SecInfo.setContent(Dyld.getSectionContent(*SectionID)); + return SecInfo; + }; + + auto GetStubInfo = [&Dyld, &StubMap](StringRef StubContainer, + StringRef SymbolName) + -> Expected<RuntimeDyldChecker::MemoryRegionInfo> { + if (!StubMap.count(StubContainer)) + return make_error<StringError>("Stub container not found: " + + StubContainer, + inconvertibleErrorCode()); + if (!StubMap[StubContainer].count(SymbolName)) + return make_error<StringError>("Symbol name " + SymbolName + + " in stub container " + StubContainer, + inconvertibleErrorCode()); + auto &SI = StubMap[StubContainer][SymbolName]; + RuntimeDyldChecker::MemoryRegionInfo StubMemInfo; + StubMemInfo.setTargetAddress(Dyld.getSectionLoadAddress(SI.SectionID) + + SI.Offset); + StubMemInfo.setContent( + Dyld.getSectionContent(SI.SectionID).substr(SI.Offset)); + return StubMemInfo; + }; + + // We will initialize this below once we have the first object file and can + // know the endianness. + std::unique_ptr<RuntimeDyldChecker> Checker; // If we don't have any input files, read from stdin. if (!InputFileList.size()) InputFileList.push_back("-"); - for (auto &Filename : InputFileList) { + for (auto &InputFile : InputFileList) { // Load the input memory buffer. ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer = - MemoryBuffer::getFileOrSTDIN(Filename); + MemoryBuffer::getFileOrSTDIN(InputFile); if (std::error_code EC = InputBuffer.getError()) ErrorAndExit("unable to read input: '" + EC.message() + "'"); @@ -717,6 +890,15 @@ static int linkAndVerify() { ObjectFile &Obj = **MaybeObj; + if (!Checker) + Checker = llvm::make_unique<RuntimeDyldChecker>( + IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, + GetStubInfo, Obj.isLittleEndian() ? support::little : support::big, + Disassembler.get(), InstPrinter.get(), dbgs()); + + auto FileName = sys::path::filename(InputFile); + MemMgr.setSectionIDsMap(&FileToSecIDMap[FileName]); + // Load the object file Dyld.loadObject(Obj); if (Dyld.hasError()) { @@ -726,7 +908,8 @@ static int linkAndVerify() { // Re-map the section addresses into the phony target address space and add // dummy symbols. - remapSectionsAndSymbols(TheTriple, MemMgr, Checker); + applySpecificSectionMappings(Dyld, FileToSecIDMap); + remapSectionsAndSymbols(TheTriple, Dyld, MemMgr); // Resolve all the relocations we can. Dyld.resolveRelocations(); @@ -734,7 +917,7 @@ static int linkAndVerify() { // Register EH frames. Dyld.registerEHFrames(); - int ErrorCode = checkAllExpressions(Checker); + int ErrorCode = checkAllExpressions(*Checker); if (Dyld.hasError()) ErrorAndExit("RTDyld reported an error applying relocations:\n " + Dyld.getErrorString()); @@ -752,6 +935,8 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm MC-JIT tool\n"); + ExitOnErr.setBanner(std::string(argv[0]) + ": "); + switch (Action) { case AC_Execute: return executeInput(); diff --git a/contrib/llvm/tools/llvm-stress/llvm-stress.cpp b/contrib/llvm/tools/llvm-stress/llvm-stress.cpp index c29b7a7f7e46..a455bf13fe7b 100644 --- a/contrib/llvm/tools/llvm-stress/llvm-stress.cpp +++ b/contrib/llvm/tools/llvm-stress/llvm-stress.cpp @@ -1,9 +1,8 @@ //===- llvm-stress.cpp - Generate random LL files to stress-test LLVM -----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -277,7 +276,7 @@ protected: /// Pick a random type. Type *pickType() { - return (getRandom() & 1 ? pickVectorType() : pickScalarType()); + return (getRandom() & 1) ? pickVectorType() : pickScalarType(); } /// Pick a random pointer type. diff --git a/contrib/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/contrib/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp index 9d19f994b739..ea94cf9b69a1 100644 --- a/contrib/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/contrib/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -1,9 +1,8 @@ //===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -39,12 +38,17 @@ ClUseSymbolTable("use-symbol-table", cl::init(true), static cl::opt<FunctionNameKind> ClPrintFunctions( "functions", cl::init(FunctionNameKind::LinkageName), - cl::desc("Print function name for a given address:"), + cl::desc("Print function name for a given address"), cl::ValueOptional, cl::values(clEnumValN(FunctionNameKind::None, "none", "omit function name"), clEnumValN(FunctionNameKind::ShortName, "short", "print short function name"), clEnumValN(FunctionNameKind::LinkageName, "linkage", - "print function linkage name"))); + "print function linkage name"), + // Sentinel value for unspecified value. + clEnumValN(FunctionNameKind::LinkageName, "", ""))); +static cl::alias ClPrintFunctionsShort("f", cl::desc("Alias for -functions"), + cl::NotHidden, cl::Grouping, + cl::aliasopt(ClPrintFunctions)); static cl::opt<bool> ClUseRelativeAddress("relative-address", cl::init(false), @@ -54,13 +58,29 @@ static cl::opt<bool> static cl::opt<bool> ClPrintInlining("inlining", cl::init(true), cl::desc("Print all inlined frames for a given address")); +static cl::alias + ClPrintInliningAliasI("i", cl::desc("Alias for -inlining"), + cl::NotHidden, cl::aliasopt(ClPrintInlining), + cl::Grouping); +static cl::alias + ClPrintInliningAliasInlines("inlines", cl::desc("Alias for -inlining"), + cl::NotHidden, cl::aliasopt(ClPrintInlining)); -// -demangle, -C +// -basenames, -s +static cl::opt<bool> ClBasenames("basenames", cl::init(false), + cl::desc("Strip directory names from paths")); +static cl::alias ClBasenamesShort("s", cl::desc("Alias for -basenames"), + cl::NotHidden, cl::aliasopt(ClBasenames)); + +// -demangle, -C, -no-demangle static cl::opt<bool> ClDemangle("demangle", cl::init(true), cl::desc("Demangle function names")); static cl::alias ClDemangleShort("C", cl::desc("Alias for -demangle"), - cl::NotHidden, cl::aliasopt(ClDemangle)); + cl::NotHidden, cl::aliasopt(ClDemangle), cl::Grouping); +static cl::opt<bool> +ClNoDemangle("no-demangle", cl::init(false), + cl::desc("Don't demangle function names")); static cl::opt<std::string> ClDefaultArch("default-arch", cl::init(""), cl::desc("Default architecture " @@ -74,10 +94,9 @@ ClBinaryName("obj", cl::init(""), static cl::alias ClBinaryNameAliasExe("exe", cl::desc("Alias for -obj"), cl::NotHidden, cl::aliasopt(ClBinaryName)); -static cl::alias -ClBinaryNameAliasE("e", cl::desc("Alias for -obj"), - cl::NotHidden, cl::aliasopt(ClBinaryName)); - +static cl::alias ClBinaryNameAliasE("e", cl::desc("Alias for -obj"), + cl::NotHidden, cl::Grouping, cl::Prefix, + cl::aliasopt(ClBinaryName)); static cl::opt<std::string> ClDwpName("dwp", cl::init(""), @@ -97,7 +116,7 @@ ClPrintAddressAliasAddresses("addresses", cl::desc("Alias for -print-address"), cl::NotHidden, cl::aliasopt(ClPrintAddress)); static cl::alias ClPrintAddressAliasA("a", cl::desc("Alias for -print-address"), - cl::NotHidden, cl::aliasopt(ClPrintAddress)); + cl::NotHidden, cl::aliasopt(ClPrintAddress), cl::Grouping); // -pretty-print, -p static cl::opt<bool> @@ -105,7 +124,7 @@ static cl::opt<bool> cl::desc("Make the output more human friendly")); static cl::alias ClPrettyPrintShort("p", cl::desc("Alias for -pretty-print"), cl::NotHidden, - cl::aliasopt(ClPrettyPrint)); + cl::aliasopt(ClPrettyPrint), cl::Grouping); static cl::opt<int> ClPrintSourceContextLines( "print-source-context-lines", cl::init(0), @@ -114,10 +133,30 @@ static cl::opt<int> ClPrintSourceContextLines( static cl::opt<bool> ClVerbose("verbose", cl::init(false), cl::desc("Print verbose line info")); +// -adjust-vma +static cl::opt<uint64_t> + ClAdjustVMA("adjust-vma", cl::init(0), cl::value_desc("offset"), + cl::desc("Add specified offset to object file addresses")); + static cl::list<std::string> ClInputAddresses(cl::Positional, cl::desc("<input addresses>..."), cl::ZeroOrMore); +static cl::opt<std::string> + ClFallbackDebugPath("fallback-debug-path", cl::init(""), + cl::desc("Fallback path for debug binaries.")); + +static cl::opt<DIPrinter::OutputStyle> + ClOutputStyle("output-style", cl::init(DIPrinter::OutputStyle::LLVM), + cl::desc("Specify print style"), + cl::values(clEnumValN(DIPrinter::OutputStyle::LLVM, "LLVM", + "LLVM default style"), + clEnumValN(DIPrinter::OutputStyle::GNU, "GNU", + "GNU addr2line style"))); + +static cl::extrahelp + HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); + template<typename T> static bool error(Expected<T> &ResOrErr) { if (ResOrErr) @@ -127,17 +166,25 @@ static bool error(Expected<T> &ResOrErr) { return true; } -static bool parseCommand(StringRef InputString, bool &IsData, +enum class Command { + Code, + Data, + Frame, +}; + +static bool parseCommand(StringRef InputString, Command &Cmd, std::string &ModuleName, uint64_t &ModuleOffset) { const char kDelimiters[] = " \n\r"; ModuleName = ""; if (InputString.consume_front("CODE ")) { - IsData = false; + Cmd = Command::Code; } else if (InputString.consume_front("DATA ")) { - IsData = true; + Cmd = Command::Data; + } else if (InputString.consume_front("FRAME ")) { + Cmd = Command::Frame; } else { // If no cmd, assume it's CODE. - IsData = false; + Cmd = Command::Code; } const char *pos = InputString.data(); // Skip delimiters and parse input filename (if needed). @@ -167,44 +214,85 @@ static bool parseCommand(StringRef InputString, bool &IsData, static void symbolizeInput(StringRef InputString, LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { - bool IsData = false; + Command Cmd; std::string ModuleName; - uint64_t ModuleOffset = 0; - if (!parseCommand(StringRef(InputString), IsData, ModuleName, ModuleOffset)) { + uint64_t Offset = 0; + if (!parseCommand(StringRef(InputString), Cmd, ModuleName, Offset)) { outs() << InputString; return; } if (ClPrintAddress) { outs() << "0x"; - outs().write_hex(ModuleOffset); + outs().write_hex(Offset); StringRef Delimiter = ClPrettyPrint ? ": " : "\n"; outs() << Delimiter; } - if (IsData) { - auto ResOrErr = Symbolizer.symbolizeData(ModuleName, ModuleOffset); + Offset -= ClAdjustVMA; + if (Cmd == Command::Data) { + auto ResOrErr = Symbolizer.symbolizeData( + ModuleName, {Offset, object::SectionedAddress::UndefSection}); Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get()); + } 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"; + } } else if (ClPrintInlining) { - auto ResOrErr = - Symbolizer.symbolizeInlinedCode(ModuleName, ModuleOffset, ClDwpName); + auto ResOrErr = Symbolizer.symbolizeInlinedCode( + ModuleName, {Offset, object::SectionedAddress::UndefSection}); Printer << (error(ResOrErr) ? DIInliningInfo() : ResOrErr.get()); + } else if (ClOutputStyle == DIPrinter::OutputStyle::GNU) { + // With ClPrintFunctions == FunctionNameKind::LinkageName (default) + // and ClUseSymbolTable == 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)); } else { - auto ResOrErr = - Symbolizer.symbolizeCode(ModuleName, ModuleOffset, ClDwpName); + auto ResOrErr = Symbolizer.symbolizeCode( + ModuleName, {Offset, object::SectionedAddress::UndefSection}); Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get()); } - outs() << "\n"; - outs().flush(); + if (ClOutputStyle == DIPrinter::OutputStyle::LLVM) + outs() << "\n"; } int main(int argc, char **argv) { InitLLVM X(argc, argv); + bool IsAddr2Line = sys::path::stem(argv[0]).contains("addr2line"); + + if (IsAddr2Line) { + ClDemangle.setInitialValue(false); + ClPrintFunctions.setInitialValue(FunctionNameKind::None); + ClPrintInlining.setInitialValue(false); + ClOutputStyle.setInitialValue(DIPrinter::OutputStyle::GNU); + } + llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); + cl::ParseCommandLineOptions(argc, argv, IsAddr2Line ? "llvm-addr2line\n" + : "llvm-symbolizer\n"); - cl::ParseCommandLineOptions(argc, argv, "llvm-symbolizer\n"); - LLVMSymbolizer::Options Opts(ClPrintFunctions, ClUseSymbolTable, ClDemangle, - ClUseRelativeAddress, ClDefaultArch); + // If both --demangle and --no-demangle are specified then pick the last one. + if (ClNoDemangle.getPosition() > ClDemangle.getPosition()) + ClDemangle = !ClNoDemangle; + + LLVMSymbolizer::Options Opts; + Opts.PrintFunctions = ClPrintFunctions; + Opts.UseSymbolTable = ClUseSymbolTable; + Opts.Demangle = ClDemangle; + Opts.RelativeAddresses = ClUseRelativeAddress; + Opts.DefaultArch = ClDefaultArch; + Opts.FallbackDebugPath = ClFallbackDebugPath; + Opts.DWPName = ClDwpName; for (const auto &hint : ClDsymHint) { if (sys::path::extension(hint) == ".dSYM") { @@ -217,14 +305,17 @@ int main(int argc, char **argv) { LLVMSymbolizer Symbolizer(Opts); DIPrinter Printer(outs(), ClPrintFunctions != FunctionNameKind::None, - ClPrettyPrint, ClPrintSourceContextLines, ClVerbose); + ClPrettyPrint, ClPrintSourceContextLines, ClVerbose, + ClBasenames, ClOutputStyle); if (ClInputAddresses.empty()) { const int kMaxInputStringLength = 1024; char InputString[kMaxInputStringLength]; - while (fgets(InputString, sizeof(InputString), stdin)) + while (fgets(InputString, sizeof(InputString), stdin)) { symbolizeInput(InputString, Symbolizer, Printer); + outs().flush(); + } } else { for (StringRef Address : ClInputAddresses) symbolizeInput(Address, Symbolizer, Printer); diff --git a/contrib/llvm/tools/llvm-xray/func-id-helper.cpp b/contrib/llvm/tools/llvm-xray/func-id-helper.cpp index c2bef6ddfb39..dc821a420c67 100644 --- a/contrib/llvm/tools/llvm-xray/func-id-helper.cpp +++ b/contrib/llvm/tools/llvm-xray/func-id-helper.cpp @@ -1,9 +1,8 @@ //===- xray-fc-account.cpp: XRay Function Call Accounting Tool ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -30,7 +29,12 @@ std::string FuncIdConversionHelper::SymbolOrNumber(int32_t FuncId) const { return F.str(); } - if (auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, It->second)) { + object::SectionedAddress ModuleAddress; + ModuleAddress.Address = It->second; + // TODO: set proper section index here. + // object::SectionedAddress::UndefSection works for only absolute addresses. + ModuleAddress.SectionIndex = object::SectionedAddress::UndefSection; + if (auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, ModuleAddress)) { auto &DI = *ResOrErr; if (DI.FunctionName == "<invalid>") F << "@(" << std::hex << It->second << ")"; @@ -52,7 +56,12 @@ std::string FuncIdConversionHelper::FileLineAndColumn(int32_t FuncId) const { return "(unknown)"; std::ostringstream F; - auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, It->second); + object::SectionedAddress ModuleAddress; + ModuleAddress.Address = It->second; + // TODO: set proper section index here. + // object::SectionedAddress::UndefSection works for only absolute addresses. + ModuleAddress.SectionIndex = object::SectionedAddress::UndefSection; + auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, ModuleAddress); if (!ResOrErr) { consumeError(ResOrErr.takeError()); return "(unknown)"; diff --git a/contrib/llvm/tools/llvm-xray/func-id-helper.h b/contrib/llvm/tools/llvm-xray/func-id-helper.h index 3e0780d54f90..c6ce198170d5 100644 --- a/contrib/llvm/tools/llvm-xray/func-id-helper.h +++ b/contrib/llvm/tools/llvm-xray/func-id-helper.h @@ -1,9 +1,8 @@ //===- func-id-helper.h - XRay Function ID Conversion Helpers -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-xray/llvm-xray.cpp b/contrib/llvm/tools/llvm-xray/llvm-xray.cpp index e74628f5025f..9ee653e97b2d 100644 --- a/contrib/llvm/tools/llvm-xray/llvm-xray.cpp +++ b/contrib/llvm/tools/llvm-xray/llvm-xray.cpp @@ -1,9 +1,8 @@ //===- llvm-xray.cpp: XRay Tool Main Program ------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-xray/trie-node.h b/contrib/llvm/tools/llvm-xray/trie-node.h index e6ba4e215b91..47d4b8f1e78c 100644 --- a/contrib/llvm/tools/llvm-xray/trie-node.h +++ b/contrib/llvm/tools/llvm-xray/trie-node.h @@ -1,9 +1,8 @@ //===- trie-node.h - XRay Call Stack Data Structure -----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-xray/xray-account.cpp b/contrib/llvm/tools/llvm-xray/xray-account.cpp index 9985c9adcf6c..2b49a311d7e3 100644 --- a/contrib/llvm/tools/llvm-xray/xray-account.cpp +++ b/contrib/llvm/tools/llvm-xray/xray-account.cpp @@ -1,9 +1,8 @@ //===- xray-account.h - XRay Function Call Accounting ---------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -428,9 +427,7 @@ static CommandRegistration Unused(&Account, []() -> Error { Twine("Cannot open file '") + AccountOutput + "' for writing.", EC); const auto &FunctionAddresses = Map.getFunctionAddresses(); - symbolize::LLVMSymbolizer::Options Opts( - symbolize::FunctionNameKind::LinkageName, true, true, false, ""); - symbolize::LLVMSymbolizer Symbolizer(Opts); + symbolize::LLVMSymbolizer Symbolizer; llvm::xray::FuncIdConversionHelper FuncIdHelper(AccountInstrMap, Symbolizer, FunctionAddresses); xray::LatencyAccountant FCA(FuncIdHelper, AccountDeduceSiblingCalls); diff --git a/contrib/llvm/tools/llvm-xray/xray-account.h b/contrib/llvm/tools/llvm-xray/xray-account.h index 5c457f178166..b63ecc59b71a 100644 --- a/contrib/llvm/tools/llvm-xray/xray-account.h +++ b/contrib/llvm/tools/llvm-xray/xray-account.h @@ -1,9 +1,8 @@ //===- xray-account.h - XRay Function Call Accounting ---------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -29,12 +28,11 @@ namespace xray { class LatencyAccountant { public: typedef std::map<int32_t, std::vector<uint64_t>> FunctionLatencyMap; - typedef std::map<llvm::sys::procid_t, std::pair<uint64_t, uint64_t>> + typedef std::map<uint32_t, std::pair<uint64_t, uint64_t>> PerThreadMinMaxTSCMap; typedef std::map<uint8_t, std::pair<uint64_t, uint64_t>> PerCPUMinMaxTSCMap; typedef std::vector<std::pair<int32_t, uint64_t>> FunctionStack; - typedef std::map<llvm::sys::procid_t, FunctionStack> - PerThreadFunctionStackMap; + typedef std::map<uint32_t, FunctionStack> PerThreadFunctionStackMap; private: PerThreadFunctionStackMap PerThreadFunctionStack; @@ -78,13 +76,6 @@ public: /// bool accountRecord(const XRayRecord &Record); - const FunctionStack *getThreadFunctionStack(llvm::sys::procid_t TId) const { - auto I = PerThreadFunctionStack.find(TId); - if (I == PerThreadFunctionStack.end()) - return nullptr; - return &I->second; - } - const PerThreadFunctionStackMap &getPerThreadFunctionStack() const { return PerThreadFunctionStack; } diff --git a/contrib/llvm/tools/llvm-xray/xray-color-helper.cpp b/contrib/llvm/tools/llvm-xray/xray-color-helper.cpp index 78a264b73d8f..c09cad3ba7d2 100644 --- a/contrib/llvm/tools/llvm-xray/xray-color-helper.cpp +++ b/contrib/llvm/tools/llvm-xray/xray-color-helper.cpp @@ -1,9 +1,8 @@ //===-- xray-graph.cpp: XRay Function Call Graph Renderer -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-xray/xray-color-helper.h b/contrib/llvm/tools/llvm-xray/xray-color-helper.h index b2dcf626a65f..0940fc211343 100644 --- a/contrib/llvm/tools/llvm-xray/xray-color-helper.h +++ b/contrib/llvm/tools/llvm-xray/xray-color-helper.h @@ -1,9 +1,8 @@ //===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-xray/xray-converter.cpp b/contrib/llvm/tools/llvm-xray/xray-converter.cpp index a682dbe53e3b..dfc757e0f276 100644 --- a/contrib/llvm/tools/llvm-xray/xray-converter.cpp +++ b/contrib/llvm/tools/llvm-xray/xray-converter.cpp @@ -1,9 +1,8 @@ //===- xray-converter.cpp: XRay Trace Conversion --------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -381,9 +380,7 @@ static CommandRegistration Unused(&Convert, []() -> Error { } const auto &FunctionAddresses = Map.getFunctionAddresses(); - symbolize::LLVMSymbolizer::Options Opts( - symbolize::FunctionNameKind::LinkageName, true, true, false, ""); - symbolize::LLVMSymbolizer Symbolizer(Opts); + symbolize::LLVMSymbolizer Symbolizer; llvm::xray::FuncIdConversionHelper FuncIdHelper(ConvertInstrMap, Symbolizer, FunctionAddresses); llvm::xray::TraceConverter TC(FuncIdHelper, ConvertSymbolize); diff --git a/contrib/llvm/tools/llvm-xray/xray-converter.h b/contrib/llvm/tools/llvm-xray/xray-converter.h index 5f0a3ee298eb..db6d2b1614ee 100644 --- a/contrib/llvm/tools/llvm-xray/xray-converter.h +++ b/contrib/llvm/tools/llvm-xray/xray-converter.h @@ -1,9 +1,8 @@ //===- xray-converter.h - XRay Trace Conversion ---------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-xray/xray-extract.cpp b/contrib/llvm/tools/llvm-xray/xray-extract.cpp index 10fe7d8d6209..7c7d26b5a389 100644 --- a/contrib/llvm/tools/llvm-xray/xray-extract.cpp +++ b/contrib/llvm/tools/llvm-xray/xray-extract.cpp @@ -1,9 +1,8 @@ //===- xray-extract.cpp: XRay Instrumentation Map Extraction --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -87,9 +86,7 @@ static CommandRegistration Unused(&Extract, []() -> Error { Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC); const auto &FunctionAddresses = InstrumentationMapOrError->getFunctionAddresses(); - symbolize::LLVMSymbolizer::Options Opts( - symbolize::FunctionNameKind::LinkageName, true, true, false, ""); - symbolize::LLVMSymbolizer Symbolizer(Opts); + symbolize::LLVMSymbolizer Symbolizer; llvm::xray::FuncIdConversionHelper FuncIdHelper(ExtractInput, Symbolizer, FunctionAddresses); exportAsYAML(*InstrumentationMapOrError, OS, FuncIdHelper); diff --git a/contrib/llvm/tools/llvm-xray/xray-fdr-dump.cpp b/contrib/llvm/tools/llvm-xray/xray-fdr-dump.cpp index 389825605b62..81a93cac57c4 100644 --- a/contrib/llvm/tools/llvm-xray/xray-fdr-dump.cpp +++ b/contrib/llvm/tools/llvm-xray/xray-fdr-dump.cpp @@ -1,9 +1,8 @@ //===- xray-fdr-dump.cpp: XRay FDR Trace Dump Tool ------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -36,10 +35,9 @@ static cl::opt<bool> DumpVerify("verify", static CommandRegistration Unused(&Dump, []() -> Error { // Open the file provided. - int Fd; - if (auto EC = sys::fs::openFileForRead(DumpInput, Fd)) - return createStringError(EC, "Cannot open file '%s' for read.", - DumpInput.c_str()); + auto FDOrErr = sys::fs::openNativeFileForRead(DumpInput); + if (!FDOrErr) + return FDOrErr.takeError(); uint64_t FileSize; if (auto EC = sys::fs::file_size(DumpInput, FileSize)) @@ -48,7 +46,9 @@ static CommandRegistration Unused(&Dump, []() -> Error { std::error_code EC; sys::fs::mapped_file_region MappedFile( - Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); + *FDOrErr, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, + EC); + sys::fs::closeFile(*FDOrErr); DataExtractor DE(StringRef(MappedFile.data(), MappedFile.size()), true, 8); uint32_t OffsetPtr = 0; diff --git a/contrib/llvm/tools/llvm-xray/xray-graph-diff.cpp b/contrib/llvm/tools/llvm-xray/xray-graph-diff.cpp index a22f2a99811d..a514be97f40b 100644 --- a/contrib/llvm/tools/llvm-xray/xray-graph-diff.cpp +++ b/contrib/llvm/tools/llvm-xray/xray-graph-diff.cpp @@ -1,9 +1,8 @@ //===-- xray-graph-diff.cpp: XRay Function Call Graph Renderer ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-xray/xray-graph-diff.h b/contrib/llvm/tools/llvm-xray/xray-graph-diff.h index 5abec91d8582..5d12c563f47c 100644 --- a/contrib/llvm/tools/llvm-xray/xray-graph-diff.h +++ b/contrib/llvm/tools/llvm-xray/xray-graph-diff.h @@ -1,9 +1,8 @@ //===-- xray-graph-diff.h - XRay Graph Diff Renderer ------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-xray/xray-graph.cpp b/contrib/llvm/tools/llvm-xray/xray-graph.cpp index fe49cca20d57..c09357fcb502 100644 --- a/contrib/llvm/tools/llvm-xray/xray-graph.cpp +++ b/contrib/llvm/tools/llvm-xray/xray-graph.cpp @@ -1,9 +1,8 @@ //===-- xray-graph.cpp: XRay Function Call Graph Renderer -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -437,9 +436,7 @@ Expected<GraphRenderer> GraphRenderer::Factory::getGraphRenderer() { const auto &FunctionAddresses = Map.getFunctionAddresses(); - symbolize::LLVMSymbolizer::Options Opts( - symbolize::FunctionNameKind::LinkageName, true, true, false, ""); - symbolize::LLVMSymbolizer Symbolizer(Opts); + symbolize::LLVMSymbolizer Symbolizer; const auto &Header = Trace.getFileHeader(); llvm::xray::FuncIdConversionHelper FuncIdHelper(InstrMap, Symbolizer, diff --git a/contrib/llvm/tools/llvm-xray/xray-graph.h b/contrib/llvm/tools/llvm-xray/xray-graph.h index fc7f8bb470f2..23372d40f05e 100644 --- a/contrib/llvm/tools/llvm-xray/xray-graph.h +++ b/contrib/llvm/tools/llvm-xray/xray-graph.h @@ -1,9 +1,8 @@ //===-- xray-graph.h - XRay Function Call Graph Renderer --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -79,8 +78,7 @@ public: using FunctionStack = SmallVector<FunctionAttr, 4>; - using PerThreadFunctionStackMap = - DenseMap<llvm::sys::procid_t, FunctionStack>; + using PerThreadFunctionStackMap = DenseMap<uint32_t, FunctionStack>; class GraphT : public Graph<FunctionStats, CallStats, int32_t> { public: diff --git a/contrib/llvm/tools/llvm-xray/xray-registry.cpp b/contrib/llvm/tools/llvm-xray/xray-registry.cpp index fe58e4deaa1e..e5c253d2e8f1 100644 --- a/contrib/llvm/tools/llvm-xray/xray-registry.cpp +++ b/contrib/llvm/tools/llvm-xray/xray-registry.cpp @@ -1,9 +1,8 @@ //===- xray-registry.cpp: Implement a command registry. -------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-xray/xray-registry.h b/contrib/llvm/tools/llvm-xray/xray-registry.h index 6eab016273f5..d6fae78ea53c 100644 --- a/contrib/llvm/tools/llvm-xray/xray-registry.h +++ b/contrib/llvm/tools/llvm-xray/xray-registry.h @@ -1,9 +1,8 @@ //===- xray-registry.h - Define registry mechanism for commands. ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/llvm-xray/xray-stacks.cpp b/contrib/llvm/tools/llvm-xray/xray-stacks.cpp index d3af9e25e6f2..bcfc5cb1f1be 100644 --- a/contrib/llvm/tools/llvm-xray/xray-stacks.cpp +++ b/contrib/llvm/tools/llvm-xray/xray-stacks.cpp @@ -1,9 +1,8 @@ //===- xray-stacks.cpp: XRay Function Call Stack Accounting ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -634,10 +633,8 @@ public: Top->ExtraData.TerminalDurations.end(), 0uLL); { auto E = std::make_pair(Top, TopSum); - TopStacksBySum.insert(std::lower_bound(TopStacksBySum.begin(), - TopStacksBySum.end(), E, - greater_second), - E); + TopStacksBySum.insert( + llvm::lower_bound(TopStacksBySum, E, greater_second), E); if (TopStacksBySum.size() == 11) TopStacksBySum.pop_back(); } @@ -721,9 +718,7 @@ static CommandRegistration Unused(&Stack, []() -> Error { "-all-stacks."), std::make_error_code(std::errc::invalid_argument)); - symbolize::LLVMSymbolizer::Options Opts( - symbolize::FunctionNameKind::LinkageName, true, true, false, ""); - symbolize::LLVMSymbolizer Symbolizer(Opts); + symbolize::LLVMSymbolizer Symbolizer; FuncIdConversionHelper FuncIdHelper(StacksInstrMap, Symbolizer, Map.getFunctionAddresses()); // TODO: Someday, support output to files instead of just directly to diff --git a/contrib/llvm/tools/opt/AnalysisWrappers.cpp b/contrib/llvm/tools/opt/AnalysisWrappers.cpp index cfdd2cf1582b..b888605a516c 100644 --- a/contrib/llvm/tools/opt/AnalysisWrappers.cpp +++ b/contrib/llvm/tools/opt/AnalysisWrappers.cpp @@ -1,9 +1,8 @@ //===- AnalysisWrappers.cpp - Wrappers around non-pass analyses -----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/opt/BreakpointPrinter.cpp b/contrib/llvm/tools/opt/BreakpointPrinter.cpp index d3f54c034f55..a57a8c43c264 100644 --- a/contrib/llvm/tools/opt/BreakpointPrinter.cpp +++ b/contrib/llvm/tools/opt/BreakpointPrinter.cpp @@ -1,9 +1,8 @@ //===- BreakpointPrinter.cpp - Breakpoint location printer ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -36,7 +35,7 @@ struct BreakpointPrinter : public ModulePass { } } else if (auto *TY = dyn_cast<DIType>(Context)) { if (!TY->getName().empty()) { - getContextName(TY->getScope().resolve(), N); + getContextName(TY->getScope(), N); N = N + TY->getName().str() + "::"; } } @@ -50,7 +49,7 @@ struct BreakpointPrinter : public ModulePass { auto *SP = cast_or_null<DISubprogram>(NMD->getOperand(i)); if (!SP) continue; - getContextName(SP->getScope().resolve(), Name); + getContextName(SP->getScope(), Name); Name = Name + SP->getName().str(); if (!Name.empty() && Processed.insert(Name).second) { Out << Name << "\n"; diff --git a/contrib/llvm/tools/opt/BreakpointPrinter.h b/contrib/llvm/tools/opt/BreakpointPrinter.h index 57670e5ee8d8..2877555f852c 100644 --- a/contrib/llvm/tools/opt/BreakpointPrinter.h +++ b/contrib/llvm/tools/opt/BreakpointPrinter.h @@ -1,9 +1,8 @@ //===- BreakpointPrinter.h - Breakpoint location printer ------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/opt/Debugify.cpp b/contrib/llvm/tools/opt/Debugify.cpp index 3b1effba1592..222cc702bc1f 100644 --- a/contrib/llvm/tools/opt/Debugify.cpp +++ b/contrib/llvm/tools/opt/Debugify.cpp @@ -1,9 +1,8 @@ //===- Debugify.cpp - Attach synthetic debug info to everything -----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/opt/Debugify.h b/contrib/llvm/tools/opt/Debugify.h index d1a60c73e723..266f577951ae 100644 --- a/contrib/llvm/tools/opt/Debugify.h +++ b/contrib/llvm/tools/opt/Debugify.h @@ -1,9 +1,8 @@ //===- Debugify.h - Attach synthetic debug info to everything -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/opt/GraphPrinters.cpp b/contrib/llvm/tools/opt/GraphPrinters.cpp index a8bb12f3e018..611fb20513c9 100644 --- a/contrib/llvm/tools/opt/GraphPrinters.cpp +++ b/contrib/llvm/tools/opt/GraphPrinters.cpp @@ -1,9 +1,8 @@ //===- GraphPrinters.cpp - DOT printers for various graph types -----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/opt/NewPMDriver.cpp b/contrib/llvm/tools/opt/NewPMDriver.cpp index 211a3b151fe1..efe0bec35d72 100644 --- a/contrib/llvm/tools/opt/NewPMDriver.cpp +++ b/contrib/llvm/tools/opt/NewPMDriver.cpp @@ -1,9 +1,8 @@ //===- NewPMDriver.cpp - Driver for opt with new PM -----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -101,19 +100,11 @@ static cl::opt<std::string> OptimizerLastEPPipeline( "the OptimizerLast extension point into default pipelines"), cl::Hidden); -enum PGOKind { NoPGO, InstrGen, InstrUse, SampleUse }; -static cl::opt<PGOKind> PGOKindFlag( - "pgo-kind", cl::init(NoPGO), cl::Hidden, - cl::desc("The kind of profile guided optimization"), - cl::values(clEnumValN(NoPGO, "nopgo", "Do not use PGO."), - clEnumValN(InstrGen, "new-pm-pgo-instr-gen-pipeline", - "Instrument the IR to generate profile."), - clEnumValN(InstrUse, "new-pm-pgo-instr-use-pipeline", - "Use instrumented profile to guide PGO."), - clEnumValN(SampleUse, "new-pm-pgo-sample-use-pipeline", - "Use sampled profile to guide PGO."))); -static cl::opt<std::string> ProfileFile( - "profile-file", cl::desc("Path to the profile."), cl::Hidden); +extern cl::opt<PGOKind> PGOKindFlag; +extern cl::opt<std::string> ProfileFile; +extern cl::opt<CSPGOKind> CSPGOKindFlag; +extern cl::opt<std::string> CSProfileGenFile; + static cl::opt<std::string> ProfileRemappingFile("profile-remapping-file", cl::desc("Path to the profile remapping file."), @@ -231,25 +222,46 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, Optional<PGOOptions> P; switch (PGOKindFlag) { case InstrGen: - P = PGOOptions(ProfileFile, "", "", "", true); + P = PGOOptions(ProfileFile, "", "", PGOOptions::IRInstr); break; case InstrUse: - P = PGOOptions("", ProfileFile, "", ProfileRemappingFile, false); + P = PGOOptions(ProfileFile, "", ProfileRemappingFile, PGOOptions::IRUse); break; case SampleUse: - P = PGOOptions("", "", ProfileFile, ProfileRemappingFile, false); + P = PGOOptions(ProfileFile, "", ProfileRemappingFile, + PGOOptions::SampleUse); break; case NoPGO: if (DebugInfoForProfiling) - P = PGOOptions("", "", "", "", false, true); + P = PGOOptions("", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction, + true); else P = None; - } + } + if (CSPGOKindFlag != NoCSPGO) { + if (P && (P->Action == PGOOptions::IRInstr || + P->Action == PGOOptions::SampleUse)) + errs() << "CSPGOKind cannot be used with IRInstr or SampleUse"; + if (CSPGOKindFlag == CSInstrGen) { + if (CSProfileGenFile.empty()) + errs() << "CSInstrGen needs to specify CSProfileGenFile"; + if (P) { + P->CSAction = PGOOptions::CSIRInstr; + P->CSProfileGenFile = CSProfileGenFile; + } else + P = PGOOptions("", CSProfileGenFile, ProfileRemappingFile, + PGOOptions::NoAction, PGOOptions::CSIRInstr); + } else /* CSPGOKindFlag == CSInstrUse */ { + if (!P) + errs() << "CSInstrUse needs to be together with InstrUse"; + P->CSAction = PGOOptions::CSIRUse; + } + } PassInstrumentationCallbacks PIC; StandardInstrumentations SI; SI.registerCallbacks(PIC); - PassBuilder PB(TM, P, &PIC); + PassBuilder PB(TM, PipelineTuningOptions(), P, &PIC); registerEPCallbacks(PB, VerifyEachPass, DebugPM); // Load requested pass plugins and let them register pass builder callbacks diff --git a/contrib/llvm/tools/opt/NewPMDriver.h b/contrib/llvm/tools/opt/NewPMDriver.h index 7d74a5777d11..b672c97c9aa3 100644 --- a/contrib/llvm/tools/opt/NewPMDriver.h +++ b/contrib/llvm/tools/opt/NewPMDriver.h @@ -1,9 +1,8 @@ //===- NewPMDriver.h - Function to drive opt with the new PM ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -40,6 +39,13 @@ enum VerifierKind { VK_VerifyInAndOut, VK_VerifyEachPass }; +enum PGOKind { + NoPGO, + InstrGen, + InstrUse, + SampleUse +}; +enum CSPGOKind { NoCSPGO, CSInstrGen, CSInstrUse }; } /// Driver function to run the new pass manager over a module. diff --git a/contrib/llvm/tools/opt/PassPrinters.cpp b/contrib/llvm/tools/opt/PassPrinters.cpp index 310d491c06a5..70da6a43f8d9 100644 --- a/contrib/llvm/tools/opt/PassPrinters.cpp +++ b/contrib/llvm/tools/opt/PassPrinters.cpp @@ -1,9 +1,8 @@ //===- PassPrinters.cpp - Utilities to print analysis info for passes -----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/opt/PassPrinters.h b/contrib/llvm/tools/opt/PassPrinters.h index e66f3f457b7a..d4e7a4a97f31 100644 --- a/contrib/llvm/tools/opt/PassPrinters.h +++ b/contrib/llvm/tools/opt/PassPrinters.h @@ -1,9 +1,8 @@ //=- PassPrinters.h - Utilities to print analysis info for passes -*- C++ -*-=// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/contrib/llvm/tools/opt/PrintSCC.cpp b/contrib/llvm/tools/opt/PrintSCC.cpp index 78ede2b72f84..419886d6cc60 100644 --- a/contrib/llvm/tools/opt/PrintSCC.cpp +++ b/contrib/llvm/tools/opt/PrintSCC.cpp @@ -1,9 +1,8 @@ //===- PrintSCC.cpp - Enumerate SCCs in some key graphs -------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/contrib/llvm/tools/opt/opt.cpp b/contrib/llvm/tools/opt/opt.cpp index a4967a234d9c..ccf8b073b82b 100644 --- a/contrib/llvm/tools/opt/opt.cpp +++ b/contrib/llvm/tools/opt/opt.cpp @@ -1,9 +1,8 @@ //===- opt.cpp - The LLVM Modular Optimizer -------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -34,6 +33,7 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/LegacyPassNameParser.h" #include "llvm/IR/Module.h" +#include "llvm/IR/RemarkStreamer.h" #include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" #include "llvm/InitializePasses.h" @@ -173,18 +173,9 @@ static cl::opt<std::string> TargetTriple("mtriple", cl::desc("Override target triple for module")); static cl::opt<bool> -UnitAtATime("funit-at-a-time", - cl::desc("Enable IPO. This corresponds to gcc's -funit-at-a-time"), - cl::init(true)); - -static cl::opt<bool> DisableLoopUnrolling("disable-loop-unrolling", cl::desc("Disable loop unrolling in all relevant passes"), cl::init(false)); -static cl::opt<bool> -DisableLoopVectorization("disable-loop-vectorization", - cl::desc("Disable the loop vectorization pass"), - cl::init(false)); static cl::opt<bool> DisableSLPVectorization("disable-slp-vectorization", @@ -260,21 +251,62 @@ static cl::opt<bool> Coroutines( cl::desc("Enable coroutine passes."), cl::init(false), cl::Hidden); -static cl::opt<bool> PassRemarksWithHotness( +static cl::opt<bool> RemarksWithHotness( "pass-remarks-with-hotness", cl::desc("With PGO, include profile count in optimization remarks"), cl::Hidden); -static cl::opt<unsigned> PassRemarksHotnessThreshold( - "pass-remarks-hotness-threshold", - cl::desc("Minimum profile count required for an optimization remark to be output"), - cl::Hidden); +static cl::opt<unsigned> + RemarksHotnessThreshold("pass-remarks-hotness-threshold", + cl::desc("Minimum profile count required for " + "an optimization remark to be output"), + cl::Hidden); static cl::opt<std::string> RemarksFilename("pass-remarks-output", - cl::desc("YAML output filename for pass remarks"), + cl::desc("Output filename for pass remarks"), cl::value_desc("filename")); +static cl::opt<std::string> + RemarksPasses("pass-remarks-filter", + cl::desc("Only record optimization remarks from passes whose " + "names match the given regular expression"), + cl::value_desc("regex")); + +static cl::opt<std::string> RemarksFormat( + "pass-remarks-format", + cl::desc("The format used for serializing remarks (default: YAML)"), + cl::value_desc("format"), cl::init("yaml")); + +cl::opt<PGOKind> + PGOKindFlag("pgo-kind", cl::init(NoPGO), cl::Hidden, + cl::desc("The kind of profile guided optimization"), + cl::values(clEnumValN(NoPGO, "nopgo", "Do not use PGO."), + clEnumValN(InstrGen, "pgo-instr-gen-pipeline", + "Instrument the IR to generate profile."), + clEnumValN(InstrUse, "pgo-instr-use-pipeline", + "Use instrumented profile to guide PGO."), + clEnumValN(SampleUse, "pgo-sample-use-pipeline", + "Use sampled profile to guide PGO."))); +cl::opt<std::string> ProfileFile("profile-file", + cl::desc("Path to the profile."), cl::Hidden); + +cl::opt<CSPGOKind> CSPGOKindFlag( + "cspgo-kind", cl::init(NoCSPGO), cl::Hidden, + cl::desc("The kind of context sensitive profile guided optimization"), + cl::values( + clEnumValN(NoCSPGO, "nocspgo", "Do not use CSPGO."), + clEnumValN( + CSInstrGen, "cspgo-instr-gen-pipeline", + "Instrument (context sensitive) the IR to generate profile."), + clEnumValN( + CSInstrUse, "cspgo-instr-use-pipeline", + "Use instrumented (context sensitive) profile to guide PGO."))); +cl::opt<std::string> CSProfileGenFile( + "cs-profilegen-file", + cl::desc("Path to the instrumented context sensitive profile."), + cl::Hidden); + class OptCustomPassManager : public legacy::PassManager { DebugifyStatsMap DIStatsMap; @@ -348,15 +380,16 @@ static void AddOptimizationPasses(legacy::PassManagerBase &MPM, } else { Builder.Inliner = createAlwaysInlinerLegacyPass(); } - Builder.DisableUnitAtATime = !UnitAtATime; Builder.DisableUnrollLoops = (DisableLoopUnrolling.getNumOccurrences() > 0) ? DisableLoopUnrolling : OptLevel == 0; - // This is final, unless there is a #pragma vectorize enable - if (DisableLoopVectorization) - Builder.LoopVectorize = false; - // If option wasn't forced via cmd line (-vectorize-loops, -loop-vectorize) - else if (!Builder.LoopVectorize) + // Check if vectorization is explicitly disabled via -vectorize-loops=false. + // The flag enables vectorization in the LoopVectorize pass, it is on by + // default, and if it was disabled, leave it disabled here. + // Another flag that exists: -loop-vectorize, controls adding the pass to the + // pass manager. If set, the pass is added, and there is no additional check + // here for it. + if (Builder.LoopVectorize) Builder.LoopVectorize = OptLevel > 1 && SizeLevel < 2; // When #pragma vectorize is on for SLP, do the same as above @@ -369,6 +402,32 @@ static void AddOptimizationPasses(legacy::PassManagerBase &MPM, if (Coroutines) addCoroutinePassesToExtensionPoints(Builder); + switch (PGOKindFlag) { + case InstrGen: + Builder.EnablePGOInstrGen = true; + Builder.PGOInstrGen = ProfileFile; + break; + case InstrUse: + Builder.PGOInstrUse = ProfileFile; + break; + case SampleUse: + Builder.PGOSampleUse = ProfileFile; + break; + default: + break; + } + + switch (CSPGOKindFlag) { + case CSInstrGen: + Builder.EnablePGOCSInstrGen = true; + break; + case CSInstrUse: + Builder.EnablePGOCSInstrUse = true; + break; + default: + break; + } + Builder.populateFunctionPassManager(FPM); Builder.populateModulePassManager(MPM); } @@ -464,6 +523,7 @@ int main(int argc, char **argv) { initializeDwarfEHPreparePass(Registry); initializeSafeStackLegacyPassPass(Registry); initializeSjLjEHPreparePass(Registry); + initializeStackProtectorPass(Registry); initializePreISelIntrinsicLoweringLegacyPassPass(Registry); initializeGlobalMergePass(Registry); initializeIndirectBrExpandPassPass(Registry); @@ -475,6 +535,7 @@ int main(int argc, char **argv) { initializeExpandReductionsPass(Registry); initializeWasmEHPreparePass(Registry); initializeWriteBitcodePassPass(Registry); + initializeHardwareLoopsPass(Registry); #ifdef LINK_POLLY_INTO_TOOLS polly::initializePollyPasses(Registry); @@ -494,24 +555,15 @@ int main(int argc, char **argv) { if (!DisableDITypeMap) Context.enableDebugTypeODRUniquing(); - if (PassRemarksWithHotness) - Context.setDiagnosticsHotnessRequested(true); - - if (PassRemarksHotnessThreshold) - Context.setDiagnosticsHotnessThreshold(PassRemarksHotnessThreshold); - - std::unique_ptr<ToolOutputFile> OptRemarkFile; - if (RemarksFilename != "") { - std::error_code EC; - OptRemarkFile = - llvm::make_unique<ToolOutputFile>(RemarksFilename, EC, sys::fs::F_None); - if (EC) { - errs() << EC.message() << '\n'; - return 1; - } - Context.setDiagnosticsOutputFile( - llvm::make_unique<yaml::Output>(OptRemarkFile->os())); + Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr = + setupOptimizationRemarks(Context, RemarksFilename, RemarksPasses, + RemarksFormat, RemarksWithHotness, + RemarksHotnessThreshold); + if (Error E = RemarksFileOrErr.takeError()) { + errs() << toString(std::move(E)) << '\n'; + return 1; } + std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr); // Load the input module... std::unique_ptr<Module> M = @@ -585,6 +637,11 @@ int main(int argc, char **argv) { CPUStr = getCPUStr(); FeaturesStr = getFeaturesStr(); Machine = GetTargetMachine(ModuleTriple, CPUStr, FeaturesStr, Options); + } else if (ModuleTriple.getArchName() != "unknown" && + ModuleTriple.getArchName() != "") { + errs() << argv[0] << ": unrecognized architecture '" + << ModuleTriple.getArchName() << "' provided.\n"; + return 1; } std::unique_ptr<TargetMachine> TM(Machine); @@ -620,7 +677,7 @@ int main(int argc, char **argv) { // string. Hand off the rest of the functionality to the new code for that // layer. return runPassPipeline(argv[0], *M, TM.get(), Out.get(), ThinLinkOut.get(), - OptRemarkFile.get(), PassPipeline, OK, VK, + RemarksFile.get(), PassPipeline, OK, VK, PreserveAssemblyUseListOrder, PreserveBitcodeUseListOrder, EmitSummaryIndex, EmitModuleHash, EnableDebugify) @@ -856,8 +913,8 @@ int main(int argc, char **argv) { "the compile-twice option\n"; Out->os() << BOS->str(); Out->keep(); - if (OptRemarkFile) - OptRemarkFile->keep(); + if (RemarksFile) + RemarksFile->keep(); return 1; } Out->os() << BOS->str(); @@ -870,8 +927,8 @@ int main(int argc, char **argv) { if (!NoOutput || PrintBreakpoints) Out->keep(); - if (OptRemarkFile) - OptRemarkFile->keep(); + if (RemarksFile) + RemarksFile->keep(); if (ThinLinkOut) ThinLinkOut->keep(); |