diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 |
commit | cfca06d7963fa0909f90483b42a6d7d194d01e08 (patch) | |
tree | 209fb2a2d68f8f277793fc8df46c753d31bc853b /llvm/tools | |
parent | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff) |
Notes
Diffstat (limited to 'llvm/tools')
117 files changed, 8503 insertions, 2874 deletions
diff --git a/llvm/tools/bugpoint/CrashDebugger.cpp b/llvm/tools/bugpoint/CrashDebugger.cpp index aa88a06a6df06..1a39ff654f056 100644 --- a/llvm/tools/bugpoint/CrashDebugger.cpp +++ b/llvm/tools/bugpoint/CrashDebugger.cpp @@ -499,7 +499,8 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock *> &BBs) { std::vector<std::pair<std::string, std::string>> BlockInfo; for (BasicBlock *BB : Blocks) - BlockInfo.emplace_back(BB->getParent()->getName(), BB->getName()); + BlockInfo.emplace_back(std::string(BB->getParent()->getName()), + std::string(BB->getName())); SmallVector<BasicBlock *, 16> ToProcess; for (auto &F : *M) { @@ -606,7 +607,8 @@ bool ReduceCrashingConditionals::TestBlocks( std::vector<std::pair<std::string, std::string>> BlockInfo; for (const BasicBlock *BB : Blocks) - BlockInfo.emplace_back(BB->getParent()->getName(), BB->getName()); + BlockInfo.emplace_back(std::string(BB->getParent()->getName()), + std::string(BB->getName())); SmallVector<BasicBlock *, 16> ToProcess; for (auto &F : *M) { @@ -696,7 +698,8 @@ bool ReduceSimplifyCFG::TestBlocks(std::vector<const BasicBlock *> &BBs) { std::vector<std::pair<std::string, std::string>> BlockInfo; for (const BasicBlock *BB : Blocks) - BlockInfo.emplace_back(BB->getParent()->getName(), BB->getName()); + BlockInfo.emplace_back(std::string(BB->getParent()->getName()), + std::string(BB->getName())); // Loop over and delete any hack up any blocks that are not listed... for (auto &F : *M) @@ -861,7 +864,7 @@ bool ReduceCrashingMetadata::TestInsts(std::vector<Instruction *> &Insts) { // selected in Instructions. for (Function &F : *M) for (Instruction &Inst : instructions(F)) { - if (Instructions.find(&Inst) == Instructions.end()) { + if (!Instructions.count(&Inst)) { Inst.dropUnknownNonDebugMetadata(); Inst.setDebugLoc({}); } @@ -1216,7 +1219,7 @@ static Error DebugACrash(BugDriver &BD, BugTester TestFn) { // For each remaining function, try to reduce that function's attributes. std::vector<std::string> FunctionNames; for (Function &F : BD.getProgram()) - FunctionNames.push_back(F.getName()); + FunctionNames.push_back(std::string(F.getName())); if (!FunctionNames.empty() && !BugpointIsInterrupted) { outs() << "\n*** Attempting to reduce the number of function attributes" diff --git a/llvm/tools/bugpoint/ExecutionDriver.cpp b/llvm/tools/bugpoint/ExecutionDriver.cpp index 40f198b88d1a0..4c83a95989766 100644 --- a/llvm/tools/bugpoint/ExecutionDriver.cpp +++ b/llvm/tools/bugpoint/ExecutionDriver.cpp @@ -311,7 +311,7 @@ Expected<std::string> BugDriver::executeProgram(const Module &Program, << "!\n"; exit(1); } - BitcodeFile = UniqueFilename.str(); + BitcodeFile = std::string(UniqueFilename.str()); if (writeProgramToFile(BitcodeFile, UniqueFD, Program)) { errs() << ToolName << ": Error emitting bitcode to file '" << BitcodeFile @@ -336,7 +336,7 @@ Expected<std::string> BugDriver::executeProgram(const Module &Program, << "\n"; exit(1); } - OutputFile = UniqueFile.str(); + OutputFile = std::string(UniqueFile.str()); // Figure out which shared objects to run, if any. std::vector<std::string> SharedObjs(AdditionalSOs); diff --git a/llvm/tools/bugpoint/Miscompilation.cpp b/llvm/tools/bugpoint/Miscompilation.cpp index 1621a51c91d6d..e69fe9ff6c15a 100644 --- a/llvm/tools/bugpoint/Miscompilation.cpp +++ b/llvm/tools/bugpoint/Miscompilation.cpp @@ -389,7 +389,8 @@ ExtractLoops(BugDriver &BD, std::vector<std::pair<std::string, FunctionType *>> MisCompFunctions; for (Function *F : MiscompiledFunctions) { - MisCompFunctions.emplace_back(F->getName(), F->getFunctionType()); + MisCompFunctions.emplace_back(std::string(F->getName()), + F->getFunctionType()); } if (Linker::linkModules(*ToNotOptimize, @@ -415,7 +416,8 @@ ExtractLoops(BugDriver &BD, E = ToOptimizeLoopExtracted->end(); I != E; ++I) if (!I->isDeclaration()) - MisCompFunctions.emplace_back(I->getName(), I->getFunctionType()); + MisCompFunctions.emplace_back(std::string(I->getName()), + I->getFunctionType()); // Okay, great! Now we know that we extracted a loop and that loop // extraction both didn't break the program, and didn't mask the problem. @@ -586,7 +588,8 @@ ExtractBlocks(BugDriver &BD, for (Module::iterator I = Extracted->begin(), E = Extracted->end(); I != E; ++I) if (!I->isDeclaration()) - MisCompFunctions.emplace_back(I->getName(), I->getFunctionType()); + MisCompFunctions.emplace_back(std::string(I->getName()), + I->getFunctionType()); if (Linker::linkModules(*ProgClone, std::move(Extracted))) exit(1); @@ -953,7 +956,8 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD, << "Error making unique filename: " << EC.message() << "\n"; exit(1); } - if (BD.writeProgramToFile(TestModuleBC.str(), TestModuleFD, *Test)) { + if (BD.writeProgramToFile(std::string(TestModuleBC.str()), TestModuleFD, + *Test)) { errs() << "Error writing bitcode to `" << TestModuleBC.str() << "'\nExiting."; exit(1); @@ -972,7 +976,8 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD, exit(1); } - if (BD.writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, *Safe)) { + if (BD.writeProgramToFile(std::string(SafeModuleBC.str()), SafeModuleFD, + *Safe)) { errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; exit(1); } @@ -980,7 +985,7 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD, FileRemover SafeModuleBCRemover(SafeModuleBC.str(), !SaveTemps); Expected<std::string> SharedObject = - BD.compileSharedObject(SafeModuleBC.str()); + BD.compileSharedObject(std::string(SafeModuleBC.str())); if (Error E = SharedObject.takeError()) return std::move(E); @@ -988,8 +993,8 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD, // Run the code generator on the `Test' code, loading the shared library. // The function returns whether or not the new output differs from reference. - Expected<bool> Result = - BD.diffProgram(BD.getProgram(), TestModuleBC.str(), *SharedObject, false); + Expected<bool> Result = BD.diffProgram( + BD.getProgram(), std::string(TestModuleBC.str()), *SharedObject, false); if (Error E = Result.takeError()) return std::move(E); @@ -1046,7 +1051,8 @@ Error BugDriver::debugCodeGenerator() { exit(1); } - if (writeProgramToFile(TestModuleBC.str(), TestModuleFD, *ToCodeGen)) { + if (writeProgramToFile(std::string(TestModuleBC.str()), TestModuleFD, + *ToCodeGen)) { errs() << "Error writing bitcode to `" << TestModuleBC << "'\nExiting."; exit(1); } @@ -1062,11 +1068,13 @@ Error BugDriver::debugCodeGenerator() { exit(1); } - if (writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, *ToNotCodeGen)) { + if (writeProgramToFile(std::string(SafeModuleBC.str()), SafeModuleFD, + *ToNotCodeGen)) { errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; exit(1); } - Expected<std::string> SharedObject = compileSharedObject(SafeModuleBC.str()); + Expected<std::string> SharedObject = + compileSharedObject(std::string(SafeModuleBC.str())); if (Error E = SharedObject.takeError()) return E; diff --git a/llvm/tools/bugpoint/OptimizerDriver.cpp b/llvm/tools/bugpoint/OptimizerDriver.cpp index 64af81fcc8a1a..25a970bd68785 100644 --- a/llvm/tools/bugpoint/OptimizerDriver.cpp +++ b/llvm/tools/bugpoint/OptimizerDriver.cpp @@ -141,7 +141,7 @@ bool BugDriver::runPasses(Module &Program, << ": Error making unique filename: " << EC.message() << "\n"; return 1; } - OutputFilename = UniqueFilename.str(); + OutputFilename = std::string(UniqueFilename.str()); // set up the input file name Expected<sys::fs::TempFile> Temp = diff --git a/llvm/tools/bugpoint/ToolRunner.cpp b/llvm/tools/bugpoint/ToolRunner.cpp index 19b2ea2c0181e..d880aca044d1b 100644 --- a/llvm/tools/bugpoint/ToolRunner.cpp +++ b/llvm/tools/bugpoint/ToolRunner.cpp @@ -442,7 +442,7 @@ Expected<CC::FileType> LLC::OutputCode(const std::string &Bitcode, errs() << "Error making unique filename: " << EC.message() << "\n"; exit(1); } - OutputAsmFile = UniqueFile.str(); + OutputAsmFile = std::string(UniqueFile.str()); std::vector<StringRef> LLCArgs; LLCArgs.push_back(LLCPath); @@ -772,7 +772,7 @@ Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType, errs() << "Error making unique filename: " << EC.message() << "\n"; exit(1); } - OutputFile = UniqueFilename.str(); + OutputFile = std::string(UniqueFilename.str()); std::vector<StringRef> CCArgs; diff --git a/llvm/tools/bugpoint/ToolRunner.h b/llvm/tools/bugpoint/ToolRunner.h index dde4ec539cfb0..f6b5f26c7a668 100644 --- a/llvm/tools/bugpoint/ToolRunner.h +++ b/llvm/tools/bugpoint/ToolRunner.h @@ -40,7 +40,7 @@ class CC { std::vector<std::string> ccArgs; // CC-specific arguments. CC(StringRef ccPath, StringRef RemotePath, const std::vector<std::string> *CCArgs) - : CCPath(ccPath), RemoteClientPath(RemotePath) { + : CCPath(std::string(ccPath)), RemoteClientPath(std::string(RemotePath)) { if (CCArgs) ccArgs = *CCArgs; } diff --git a/llvm/tools/bugpoint/bugpoint.cpp b/llvm/tools/bugpoint/bugpoint.cpp index d29a79ee3e13b..937ec23231b07 100644 --- a/llvm/tools/bugpoint/bugpoint.cpp +++ b/llvm/tools/bugpoint/bugpoint.cpp @@ -110,7 +110,7 @@ public: void add(Pass *P) override { const void *ID = P->getPassID(); const PassInfo *PI = PassRegistry::getPassRegistry()->getPassInfo(ID); - D.addPass(PI->getPassArgument()); + D.addPass(std::string(PI->getPassArgument())); } }; } @@ -221,7 +221,7 @@ int main(int argc, char **argv) { AddOptimizationPasses(PM, 2, 2); for (const PassInfo *PI : PassList) - D.addPass(PI->getPassArgument()); + D.addPass(std::string(PI->getPassArgument())); // Bugpoint has the ability of generating a plethora of core files, so to // avoid filling up the disk, we prevent it diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp index b35f8e853c307..95f2963ecbd61 100644 --- a/llvm/tools/llc/llc.cpp +++ b/llvm/tools/llc/llc.cpp @@ -15,7 +15,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/CodeGen/CommandFlags.inc" +#include "llvm/CodeGen/CommandFlags.h" #include "llvm/CodeGen/LinkAllAsmWriterComponents.h" #include "llvm/CodeGen/LinkAllCodegenComponents.h" #include "llvm/CodeGen/MIRParser/MIRParser.h" @@ -29,9 +29,9 @@ #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LLVMRemarkStreamer.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/InitializePasses.h" @@ -50,11 +50,14 @@ #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/WithColor.h" +#include "llvm/Target/TargetLoweringObjectFile.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/Utils/Cloning.h" #include <memory> using namespace llvm; +static codegen::RegisterCodeGenFlags CGF; + // General options for llc. Other pass-specific options are specified // within the corresponding llc passes, and target-specific options // and back-end code generation options are specified with the target machine. @@ -171,7 +174,7 @@ struct RunPassOption { SmallVector<StringRef, 8> PassNames; StringRef(Val).split(PassNames, ',', -1, false); for (auto PassName : PassNames) - RunPassNames->push_back(PassName); + RunPassNames->push_back(std::string(PassName)); } }; } @@ -196,13 +199,13 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName, // If InputFilename ends in .bc or .ll, remove it. StringRef IFN = InputFilename; if (IFN.endswith(".bc") || IFN.endswith(".ll")) - OutputFilename = IFN.drop_back(3); + OutputFilename = std::string(IFN.drop_back(3)); else if (IFN.endswith(".mir")) - OutputFilename = IFN.drop_back(4); + OutputFilename = std::string(IFN.drop_back(4)); else - OutputFilename = IFN; + OutputFilename = std::string(IFN); - switch (FileType) { + switch (codegen::getFileType()) { case CGFT_AssemblyFile: if (TargetName[0] == 'c') { if (TargetName[1] == 0) @@ -229,7 +232,7 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName, // Decide if we need "binary" output. bool Binary = false; - switch (FileType) { + switch (codegen::getFileType()) { case CGFT_AssemblyFile: break; case CGFT_ObjectFile: @@ -316,6 +319,7 @@ int main(int argc, char **argv) { initializeScalarizeMaskedMemIntrinPass(*Registry); initializeExpandReductionsPass(*Registry); initializeHardwareLoopsPass(*Registry); + initializeTransformUtils(*Registry); // Initialize debugging passes. initializeScavengerTestPass(*Registry); @@ -334,9 +338,9 @@ int main(int argc, char **argv) { Context.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, &HasError); Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr = - setupOptimizationRemarks(Context, RemarksFilename, RemarksPasses, - RemarksFormat, RemarksWithHotness, - RemarksHotnessThreshold); + setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses, + RemarksFormat, RemarksWithHotness, + RemarksHotnessThreshold); if (Error E = RemarksFileOrErr.takeError()) { WithColor::error(errs(), argv[0]) << toString(std::move(E)) << '\n'; return 1; @@ -383,8 +387,9 @@ static bool addPass(PassManagerBase &PM, const char *argv0, return true; } std::string Banner = std::string("After ") + std::string(P->getPassName()); + TPC.addMachinePrePasses(); PM.add(P); - TPC.printAndVerify(Banner); + TPC.addMachinePostPasses(Banner); return false; } @@ -395,51 +400,18 @@ static int compileModule(char **argv, LLVMContext &Context) { std::unique_ptr<Module> M; std::unique_ptr<MIRParser> MIR; Triple TheTriple; - std::string CPUStr = getCPUStr(), FeaturesStr = getFeaturesStr(); + std::string CPUStr = codegen::getCPUStr(), + FeaturesStr = codegen::getFeaturesStr(); // Set attributes on functions as loaded from MIR from command line arguments. auto setMIRFunctionAttributes = [&CPUStr, &FeaturesStr](Function &F) { - setFunctionAttributes(CPUStr, FeaturesStr, F); + codegen::setFunctionAttributes(CPUStr, FeaturesStr, F); }; - bool SkipModule = MCPU == "help" || + auto MAttrs = codegen::getMAttrs(); + bool SkipModule = codegen::getMCPU() == "help" || (!MAttrs.empty() && MAttrs.front() == "help"); - // If user just wants to list available options, skip module loading - if (!SkipModule) { - if (InputLanguage == "mir" || - (InputLanguage == "" && StringRef(InputFilename).endswith(".mir"))) { - MIR = createMIRParserFromFile(InputFilename, Err, Context, - setMIRFunctionAttributes); - if (MIR) - M = MIR->parseIRModule(); - } else - M = parseIRFile(InputFilename, Err, Context, false); - if (!M) { - Err.print(argv[0], WithColor::error(errs(), argv[0])); - return 1; - } - - // If we are supposed to override the target triple, do so now. - if (!TargetTriple.empty()) - M->setTargetTriple(Triple::normalize(TargetTriple)); - TheTriple = Triple(M->getTargetTriple()); - } else { - TheTriple = Triple(Triple::normalize(TargetTriple)); - } - - if (TheTriple.getTriple().empty()) - TheTriple.setTriple(sys::getDefaultTargetTriple()); - - // Get the target specific parser. - std::string Error; - const Target *TheTarget = TargetRegistry::lookupTarget(MArch, TheTriple, - Error); - if (!TheTarget) { - WithColor::error(errs(), argv[0]) << Error; - return 1; - } - CodeGenOpt::Level OLvl = CodeGenOpt::Default; switch (OptLevel) { default: @@ -452,7 +424,7 @@ static int compileModule(char **argv, LLVMContext &Context) { case '3': OLvl = CodeGenOpt::Aggressive; break; } - TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(); Options.DisableIntegratedAS = NoIntegratedAssembler; Options.MCOptions.ShowMCEncoding = ShowMCEncoding; Options.MCOptions.MCUseDwarfDirectory = EnableDwarfDirectory; @@ -461,21 +433,97 @@ static int compileModule(char **argv, LLVMContext &Context) { Options.MCOptions.IASSearchPaths = IncludeDirs; Options.MCOptions.SplitDwarfFile = SplitDwarfFile; - std::unique_ptr<TargetMachine> Target(TheTarget->createTargetMachine( - TheTriple.getTriple(), CPUStr, FeaturesStr, Options, getRelocModel(), - getCodeModel(), OLvl)); + Optional<Reloc::Model> RM = codegen::getExplicitRelocModel(); + + const Target *TheTarget = nullptr; + std::unique_ptr<TargetMachine> Target; + + // If user just wants to list available options, skip module loading + if (!SkipModule) { + auto SetDataLayout = + [&](StringRef DataLayoutTargetTriple) -> Optional<std::string> { + // If we are supposed to override the target triple, do so now. + std::string IRTargetTriple = DataLayoutTargetTriple.str(); + if (!TargetTriple.empty()) + IRTargetTriple = Triple::normalize(TargetTriple); + TheTriple = Triple(IRTargetTriple); + if (TheTriple.getTriple().empty()) + TheTriple.setTriple(sys::getDefaultTargetTriple()); + + std::string Error; + TheTarget = + TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error); + if (!TheTarget) { + WithColor::error(errs(), argv[0]) << Error; + exit(1); + } + + // On AIX, setting the relocation model to anything other than PIC is + // considered a user error. + if (TheTriple.isOSAIX() && RM.hasValue() && *RM != Reloc::PIC_) { + WithColor::error(errs(), argv[0]) + << "invalid relocation model, AIX only supports PIC.\n"; + exit(1); + } - assert(Target && "Could not allocate target machine!"); + Target = std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine( + TheTriple.getTriple(), CPUStr, FeaturesStr, Options, RM, + codegen::getExplicitCodeModel(), OLvl)); + assert(Target && "Could not allocate target machine!"); - // If we don't have a module then just exit now. We do this down - // here since the CPU/Feature help is underneath the target machine - // creation. - if (SkipModule) + return Target->createDataLayout().getStringRepresentation(); + }; + if (InputLanguage == "mir" || + (InputLanguage == "" && StringRef(InputFilename).endswith(".mir"))) { + MIR = createMIRParserFromFile(InputFilename, Err, Context, + setMIRFunctionAttributes); + if (MIR) + M = MIR->parseIRModule(SetDataLayout); + } else { + M = parseIRFile(InputFilename, Err, Context, SetDataLayout); + } + if (!M) { + Err.print(argv[0], WithColor::error(errs(), argv[0])); + return 1; + } + if (!TargetTriple.empty()) + M->setTargetTriple(Triple::normalize(TargetTriple)); + } else { + TheTriple = Triple(Triple::normalize(TargetTriple)); + if (TheTriple.getTriple().empty()) + TheTriple.setTriple(sys::getDefaultTargetTriple()); + + // Get the target specific parser. + std::string Error; + TheTarget = + TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error); + if (!TheTarget) { + WithColor::error(errs(), argv[0]) << Error; + return 1; + } + + // On AIX, setting the relocation model to anything other than PIC is + // considered a user error. + if (TheTriple.isOSAIX() && RM.hasValue() && *RM != Reloc::PIC_) { + WithColor::error(errs(), argv[0]) + << "invalid relocation model, AIX only supports PIC.\n"; + return 1; + } + + Target = std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine( + TheTriple.getTriple(), CPUStr, FeaturesStr, Options, RM, + codegen::getExplicitCodeModel(), OLvl)); + assert(Target && "Could not allocate target machine!"); + + // If we don't have a module then just exit now. We do this down + // here since the CPU/Feature help is underneath the target machine + // creation. return 0; + } assert(M && "Should have exited if we didn't have a module!"); - if (FloatABIForCalls != FloatABI::Default) - Options.FloatABIType = FloatABIForCalls; + if (codegen::getFloatABIForCalls() != FloatABI::Default) + Options.FloatABIType = codegen::getFloatABIForCalls(); // Figure out where we are going to send the output. std::unique_ptr<ToolOutputFile> Out = @@ -504,13 +552,6 @@ static int compileModule(char **argv, LLVMContext &Context) { TLII.disableAllFunctions(); PM.add(new TargetLibraryInfoWrapperPass(TLII)); - // Add the target data from the target machine, if it exists, or the module. - M->setDataLayout(Target->createDataLayout()); - - // This needs to be done after setting datalayout since it calls verifier - // to check debug info whereas verifier relies on correct datalayout. - UpgradeDebugInfo(*M); - // Verify module immediately to catch problems before doInitialization() is // called on any passes. if (!NoVerify && verifyModule(*M, &errs())) { @@ -522,10 +563,9 @@ static int compileModule(char **argv, LLVMContext &Context) { // Override function attributes based on CPUStr, FeaturesStr, and command line // flags. - setFunctionAttributes(CPUStr, FeaturesStr, *M); + codegen::setFunctionAttributes(CPUStr, FeaturesStr, *M); - if (RelaxAll.getNumOccurrences() > 0 && - FileType != CGFT_ObjectFile) + if (mc::getExplicitRelaxAll() && codegen::getFileType() != CGFT_ObjectFile) WithColor::warning(errs(), argv[0]) << ": warning: ignoring -mc-relax-all because filetype != obj"; @@ -536,7 +576,7 @@ static int compileModule(char **argv, LLVMContext &Context) { // so we can memcmp the contents in CompileTwice mode SmallVector<char, 0> Buffer; std::unique_ptr<raw_svector_ostream> BOS; - if ((FileType != CGFT_AssemblyFile && + if ((codegen::getFileType() != CGFT_AssemblyFile && !Out->os().supportsSeeking()) || CompileTwice) { BOS = std::make_unique<raw_svector_ostream>(Buffer); @@ -575,15 +615,17 @@ static int compileModule(char **argv, LLVMContext &Context) { TPC.setInitialized(); PM.add(createPrintMIRPass(*OS)); PM.add(createFreeMachineFunctionPass()); - } else if (Target->addPassesToEmitFile(PM, *OS, - DwoOut ? &DwoOut->os() : nullptr, - FileType, NoVerify, MMIWP)) { + } else if (Target->addPassesToEmitFile( + PM, *OS, DwoOut ? &DwoOut->os() : nullptr, + codegen::getFileType(), NoVerify, MMIWP)) { WithColor::warning(errs(), argv[0]) << "target does not support generation of this" << " file type!\n"; return 1; } + const_cast<TargetLoweringObjectFile *>(LLVMTM.getObjFileLowering()) + ->Initialize(MMIWP->getMMI().getContext(), *Target); if (MIR) { assert(MMIWP && "Forgot to create MMIWP?"); if (MIR->parseMachineFunctions(*M, MMIWP->getMMI())) diff --git a/llvm/tools/lli/lli.cpp b/llvm/tools/lli/lli.cpp index bfe7e8f043034..981e0812d45ee 100644 --- a/llvm/tools/lli/lli.cpp +++ b/llvm/tools/lli/lli.cpp @@ -16,7 +16,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Bitcode/BitcodeReader.h" -#include "llvm/CodeGen/CommandFlags.inc" +#include "llvm/CodeGen/CommandFlags.h" #include "llvm/CodeGen/LinkAllCodegenComponents.h" #include "llvm/Config/llvm-config.h" #include "llvm/ExecutionEngine/GenericValue.h" @@ -24,10 +24,13 @@ #include "llvm/ExecutionEngine/JITEventListener.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/ObjectCache.h" +#include "llvm/ExecutionEngine/Orc/DebugUtils.h" #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/ExecutionEngine/Orc/MachOPlatform.h" #include "llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h" +#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" #include "llvm/ExecutionEngine/OrcMCJITReplacement.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/IRBuilder.h" @@ -67,6 +70,8 @@ using namespace llvm; +static codegen::RegisterCodeGenFlags CGF; + #define DEBUG_TYPE "lli" namespace { @@ -115,6 +120,10 @@ namespace { cl::desc("Specifies the JITDylib to be used for any subsequent " "-extra-module arguments.")); + cl::list<std::string> + Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"), + cl::ZeroOrMore); + // The MCJIT supports building for a target address space separate from // the JIT compilation process. Use a forked process and a copying // memory manager with IPC to execute using this functionality. @@ -197,6 +206,24 @@ namespace { cl::desc("Generate software floating point library calls"), cl::init(false)); + cl::opt<bool> NoProcessSymbols( + "no-process-syms", + cl::desc("Do not resolve lli process symbols in JIT'd code"), + cl::init(false)); + + enum class LLJITPlatform { DetectHost, GenericIR, MachO }; + + cl::opt<LLJITPlatform> + Platform("lljit-platform", cl::desc("Platform to use with LLJIT"), + cl::init(LLJITPlatform::DetectHost), + cl::values(clEnumValN(LLJITPlatform::DetectHost, "DetectHost", + "Select based on JIT target triple"), + clEnumValN(LLJITPlatform::GenericIR, "GenericIR", + "Use LLJITGenericIRPlatform"), + clEnumValN(LLJITPlatform::MachO, "MachO", + "Use LLJITMachOPlatform")), + cl::Hidden); + enum class DumpKind { NoDump, DumpFuncsToStdOut, @@ -250,6 +277,7 @@ public: SmallString<128> dir(sys::path::parent_path(CacheName)); sys::fs::create_directories(Twine(dir)); } + std::error_code EC; raw_fd_ostream outfile(CacheName, EC, sys::fs::OF_None); outfile.write(Obj.getBufferStart(), Obj.getBufferSize()); @@ -282,14 +310,16 @@ private: size_t PrefixLength = Prefix.length(); if (ModID.substr(0, PrefixLength) != Prefix) return false; - std::string CacheSubdir = ModID.substr(PrefixLength); + + std::string CacheSubdir = ModID.substr(PrefixLength); #if defined(_WIN32) - // Transform "X:\foo" => "/X\foo" for convenience. - if (isalpha(CacheSubdir[0]) && CacheSubdir[1] == ':') { - CacheSubdir[1] = CacheSubdir[0]; - CacheSubdir[0] = '/'; - } + // Transform "X:\foo" => "/X\foo" for convenience. + if (isalpha(CacheSubdir[0]) && CacheSubdir[1] == ':') { + CacheSubdir[1] = CacheSubdir[0]; + CacheSubdir[0] = '/'; + } #endif + CacheName = CacheDir + CacheSubdir; size_t pos = CacheName.rfind('.'); CacheName.replace(pos, CacheName.length() - pos, ".o"); @@ -350,6 +380,7 @@ static void reportError(SMDiagnostic Err, const char *ProgName) { exit(1); } +Error loadDylibs(); int runOrcLazyJIT(const char *ProgName); void disallowOrcOptions(); @@ -375,6 +406,8 @@ int main(int argc, char **argv, char * const *envp) { if (DisableCoreFiles) sys::Process::PreventCoreFiles(); + ExitOnErr(loadDylibs()); + if (UseJITKind == JITKind::OrcLazy) return runOrcLazyJIT(argv[0]); else @@ -405,13 +438,13 @@ int main(int argc, char **argv, char * const *envp) { std::string ErrorMsg; EngineBuilder builder(std::move(Owner)); - builder.setMArch(MArch); - builder.setMCPU(getCPUStr()); - builder.setMAttrs(getFeatureList()); - if (RelocModel.getNumOccurrences()) - builder.setRelocationModel(RelocModel); - if (CMModel.getNumOccurrences()) - builder.setCodeModel(CMModel); + builder.setMArch(codegen::getMArch()); + builder.setMCPU(codegen::getCPUStr()); + builder.setMAttrs(codegen::getFeatureList()); + if (auto RM = codegen::getExplicitRelocModel()) + builder.setRelocationModel(RM.getValue()); + if (auto CM = codegen::getExplicitCodeModel()) + builder.setCodeModel(CM.getValue()); builder.setErrorStr(&ErrorMsg); builder.setEngineKind(ForceInterpreter ? EngineKind::Interpreter @@ -443,9 +476,9 @@ int main(int argc, char **argv, char * const *envp) { builder.setOptLevel(getOptLevel()); - TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); - if (FloatABIForCalls != FloatABI::Default) - Options.FloatABIType = FloatABIForCalls; + TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(); + if (codegen::getFloatABIForCalls() != FloatABI::Default) + Options.FloatABIType = codegen::getFloatABIForCalls(); builder.setTargetOptions(Options); @@ -709,7 +742,7 @@ static std::function<void(Module &)> createDebugDumper() { continue; if (F.hasName()) { - std::string Name(F.getName()); + std::string Name(std::string(F.getName())); printf("%s ", Name.c_str()); } else printf("<anon> "); @@ -738,75 +771,164 @@ static std::function<void(Module &)> createDebugDumper() { llvm_unreachable("Unknown DumpKind"); } +Error loadDylibs() { + for (const auto &Dylib : Dylibs) { + std::string ErrMsg; + if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg)) + return make_error<StringError>(ErrMsg, inconvertibleErrorCode()); + } + + return Error::success(); +} + static void exitOnLazyCallThroughFailure() { exit(1); } +Expected<orc::ThreadSafeModule> +loadModule(StringRef Path, orc::ThreadSafeContext TSCtx) { + SMDiagnostic Err; + auto M = parseIRFile(Path, Err, *TSCtx.getContext()); + if (!M) { + std::string ErrMsg; + { + raw_string_ostream ErrMsgStream(ErrMsg); + Err.print("lli", ErrMsgStream); + } + return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode()); + } + + if (EnableCacheManager) + M->setModuleIdentifier("file:" + M->getModuleIdentifier()); + + return orc::ThreadSafeModule(std::move(M), std::move(TSCtx)); +} + int runOrcLazyJIT(const char *ProgName) { // Start setting up the JIT environment. // Parse the main module. orc::ThreadSafeContext TSCtx(std::make_unique<LLVMContext>()); - SMDiagnostic Err; - auto MainModule = parseIRFile(InputFile, Err, *TSCtx.getContext()); - if (!MainModule) - reportError(Err, ProgName); + auto MainModule = ExitOnErr(loadModule(InputFile, TSCtx)); + + // Get TargetTriple and DataLayout from the main module if they're explicitly + // set. + Optional<Triple> TT; + Optional<DataLayout> DL; + MainModule.withModuleDo([&](Module &M) { + if (!M.getTargetTriple().empty()) + TT = Triple(M.getTargetTriple()); + if (!M.getDataLayout().isDefault()) + DL = M.getDataLayout(); + }); - const auto &TT = MainModule->getTargetTriple(); orc::LLLazyJITBuilder Builder; Builder.setJITTargetMachineBuilder( - TT.empty() ? ExitOnErr(orc::JITTargetMachineBuilder::detectHost()) - : orc::JITTargetMachineBuilder(Triple(TT))); + TT ? orc::JITTargetMachineBuilder(*TT) + : ExitOnErr(orc::JITTargetMachineBuilder::detectHost())); - if (!MArch.empty()) - Builder.getJITTargetMachineBuilder()->getTargetTriple().setArchName(MArch); + TT = Builder.getJITTargetMachineBuilder()->getTargetTriple(); + if (DL) + Builder.setDataLayout(DL); + + if (!codegen::getMArch().empty()) + Builder.getJITTargetMachineBuilder()->getTargetTriple().setArchName( + codegen::getMArch()); Builder.getJITTargetMachineBuilder() - ->setCPU(getCPUStr()) - .addFeatures(getFeatureList()) - .setRelocationModel(RelocModel.getNumOccurrences() - ? Optional<Reloc::Model>(RelocModel) - : None) - .setCodeModel(CMModel.getNumOccurrences() - ? Optional<CodeModel::Model>(CMModel) - : None); + ->setCPU(codegen::getCPUStr()) + .addFeatures(codegen::getFeatureList()) + .setRelocationModel(codegen::getExplicitRelocModel()) + .setCodeModel(codegen::getExplicitCodeModel()); Builder.setLazyCompileFailureAddr( pointerToJITTargetAddress(exitOnLazyCallThroughFailure)); Builder.setNumCompileThreads(LazyJITCompileThreads); + // If the object cache is enabled then set a custom compile function + // creator to use the cache. + std::unique_ptr<LLIObjectCache> CacheManager; + if (EnableCacheManager) { + + CacheManager = std::make_unique<LLIObjectCache>(ObjectCacheDir); + + Builder.setCompileFunctionCreator( + [&](orc::JITTargetMachineBuilder JTMB) + -> Expected<std::unique_ptr<orc::IRCompileLayer::IRCompiler>> { + if (LazyJITCompileThreads > 0) + return std::make_unique<orc::ConcurrentIRCompiler>(std::move(JTMB), + CacheManager.get()); + + auto TM = JTMB.createTargetMachine(); + if (!TM) + return TM.takeError(); + + return std::make_unique<orc::TMOwningSimpleCompiler>(std::move(*TM), + CacheManager.get()); + }); + } + + // Set up LLJIT platform. + { + LLJITPlatform P = Platform; + if (P == LLJITPlatform::DetectHost) { + if (TT->isOSBinFormatMachO()) + P = LLJITPlatform::MachO; + else + P = LLJITPlatform::GenericIR; + } + + switch (P) { + case LLJITPlatform::GenericIR: + // Nothing to do: LLJITBuilder will use this by default. + break; + case LLJITPlatform::MachO: + Builder.setPlatformSetUp(orc::setUpMachOPlatform); + ExitOnErr(orc::enableObjCRegistration("libobjc.dylib")); + break; + default: + llvm_unreachable("Unrecognized platform value"); + } + } + auto J = ExitOnErr(Builder.create()); + if (TT->isOSBinFormatELF()) + static_cast<llvm::orc::RTDyldObjectLinkingLayer &>(J->getObjLinkingLayer()) + .registerJITEventListener( + *JITEventListener::createGDBRegistrationListener()); + if (PerModuleLazy) J->setPartitionFunction(orc::CompileOnDemandLayer::compileWholeModule); auto Dump = createDebugDumper(); - J->setLazyCompileTransform([&](orc::ThreadSafeModule TSM, - const orc::MaterializationResponsibility &R) { - TSM.withModuleDo([&](Module &M) { - if (verifyModule(M, &dbgs())) { - dbgs() << "Bad module: " << &M << "\n"; - exit(1); - } - Dump(M); - }); - return TSM; - }); + J->getIRTransformLayer().setTransform( + [&](orc::ThreadSafeModule TSM, + const orc::MaterializationResponsibility &R) { + TSM.withModuleDo([&](Module &M) { + if (verifyModule(M, &dbgs())) { + dbgs() << "Bad module: " << &M << "\n"; + exit(1); + } + Dump(M); + }); + return TSM; + }); orc::MangleAndInterner Mangle(J->getExecutionSession(), J->getDataLayout()); - J->getMainJITDylib().addGenerator( - ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( - J->getDataLayout().getGlobalPrefix(), - [MainName = Mangle("main")](const orc::SymbolStringPtr &Name) { - return Name != MainName; - }))); - orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides; - ExitOnErr(CXXRuntimeOverrides.enable(J->getMainJITDylib(), Mangle)); + // Unless they've been explicitly disabled, make process symbols available to + // JIT'd code. + if (!NoProcessSymbols) + J->getMainJITDylib().addGenerator( + ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( + J->getDataLayout().getGlobalPrefix(), + [MainName = Mangle("main")](const orc::SymbolStringPtr &Name) { + return Name != MainName; + }))); // Add the main module. - ExitOnErr( - J->addLazyIRModule(orc::ThreadSafeModule(std::move(MainModule), TSCtx))); + ExitOnErr(J->addLazyIRModule(std::move(MainModule))); // Create JITDylibs and add any extra modules. { @@ -818,23 +940,23 @@ int runOrcLazyJIT(const char *ProgName) { for (auto JDItr = JITDylibs.begin(), JDEnd = JITDylibs.end(); JDItr != JDEnd; ++JDItr) { orc::JITDylib *JD = J->getJITDylibByName(*JDItr); - if (!JD) - JD = &J->createJITDylib(*JDItr); + if (!JD) { + JD = &ExitOnErr(J->createJITDylib(*JDItr)); + J->getMainJITDylib().addToLinkOrder(*JD); + JD->addToLinkOrder(J->getMainJITDylib()); + } IdxToDylib[JITDylibs.getPosition(JDItr - JITDylibs.begin())] = JD; } for (auto EMItr = ExtraModules.begin(), EMEnd = ExtraModules.end(); EMItr != EMEnd; ++EMItr) { - auto M = parseIRFile(*EMItr, Err, *TSCtx.getContext()); - if (!M) - reportError(Err, ProgName); + auto M = ExitOnErr(loadModule(*EMItr, TSCtx)); auto EMIdx = ExtraModules.getPosition(EMItr - ExtraModules.begin()); assert(EMIdx != 0 && "ExtraModule should have index > 0"); auto JDItr = std::prev(IdxToDylib.lower_bound(EMIdx)); auto &JD = *JDItr->second; - ExitOnErr( - J->addLazyIRModule(JD, orc::ThreadSafeModule(std::move(M), TSCtx))); + ExitOnErr(J->addLazyIRModule(JD, std::move(M))); } for (auto EAItr = ExtraArchives.begin(), EAEnd = ExtraArchives.end(); @@ -844,7 +966,7 @@ int runOrcLazyJIT(const char *ProgName) { auto JDItr = std::prev(IdxToDylib.lower_bound(EAIdx)); auto &JD = *JDItr->second; JD.addGenerator(ExitOnErr(orc::StaticLibraryDefinitionGenerator::Load( - J->getObjLinkingLayer(), EAItr->c_str()))); + J->getObjLinkingLayer(), EAItr->c_str(), *TT))); } } @@ -855,7 +977,7 @@ int runOrcLazyJIT(const char *ProgName) { } // Run any static constructors. - ExitOnErr(J->runConstructors()); + ExitOnErr(J->initialize(J->getMainJITDylib())); // Run any -thread-entry points. std::vector<std::thread> AltEntryThreads; @@ -880,8 +1002,7 @@ int runOrcLazyJIT(const char *ProgName) { AltEntryThread.join(); // Run destructors. - ExitOnErr(J->runDestructors()); - CXXRuntimeOverrides.runDestructors(); + ExitOnErr(J->deinitialize(J->getMainJITDylib())); return Result; } diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp index c339dfe1f33e3..d699d4323f0a1 100644 --- a/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/llvm/tools/llvm-ar/llvm-ar.cpp @@ -14,17 +14,22 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" +#include "llvm/BinaryFormat/Magic.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/IRObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/SymbolicFile.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" @@ -45,8 +50,7 @@ #endif #ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include <windows.h> +#include "llvm/Support/Windows/WindowsSupport.h" #endif using namespace llvm; @@ -83,6 +87,9 @@ OPTIONS: =bsd - bsd --plugin=<string> - ignored for compatibility -h --help - display this help and exit + --rsp-quoting - quoting style for response files + =posix - posix + =windows - windows --version - print the version and exit @<file> - read options from <file> @@ -513,13 +520,13 @@ static std::string normalizePath(StringRef Path) { static bool comparePaths(StringRef Path1, StringRef Path2) { // When on Windows this function calls CompareStringOrdinal -// as Windows file paths are case-insensitive. +// as Windows file paths are case-insensitive. // CompareStringOrdinal compares two Unicode strings for // binary equivalence and allows for case insensitivity. #ifdef _WIN32 SmallVector<wchar_t, 128> WPath1, WPath2; - failIfError(sys::path::widenPath(normalizePath(Path1), WPath1)); - failIfError(sys::path::widenPath(normalizePath(Path2), WPath2)); + failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path1), WPath1)); + failIfError(sys::windows::UTF8ToUTF16(normalizePath(Path2), WPath2)); return CompareStringOrdinal(WPath1.data(), WPath1.size(), WPath2.data(), WPath2.size(), true) == CSTR_EQUAL; @@ -650,7 +657,7 @@ static void addChildMember(std::vector<NewArchiveMember> &Members, // the archive it's in, so the file resolves correctly. if (Thin && FlattenArchive) { StringSaver Saver(Alloc); - Expected<std::string> FileNameOrErr = M.getName(); + 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)); @@ -792,7 +799,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, int Pos = Ret.size(); Expected<StringRef> NameOrErr = Child.getName(); failIfError(NameOrErr.takeError()); - std::string Name = NameOrErr.get(); + std::string Name = std::string(NameOrErr.get()); if (comparePaths(Name, RelPos)) { assert(AddAfter || AddBefore); if (AddBefore) @@ -871,8 +878,9 @@ static object::Archive::Kind getDefaultForHost() { } static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) { + auto MemBufferRef = Member.Buf->getMemBufferRef(); Expected<std::unique_ptr<object::ObjectFile>> OptionalObject = - object::ObjectFile::createObjectFile(Member.Buf->getMemBufferRef()); + object::ObjectFile::createObjectFile(MemBufferRef); if (OptionalObject) return isa<object::MachOObjectFile>(**OptionalObject) @@ -881,6 +889,23 @@ static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) { // squelch the error in case we had a non-object file consumeError(OptionalObject.takeError()); + + // If we're adding a bitcode file to the archive, detect the Archive kind + // based on the target triple. + LLVMContext Context; + if (identify_magic(MemBufferRef.getBuffer()) == file_magic::bitcode) { + if (auto ObjOrErr = object::SymbolicFile::createSymbolicFile( + MemBufferRef, file_magic::bitcode, &Context)) { + auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr); + return Triple(IRObject.getTargetTriple()).isOSDarwin() + ? object::Archive::K_DARWIN + : object::Archive::K_GNU; + } else { + // Squelch the error in case this was not a SymbolicFile. + consumeError(ObjOrErr.takeError()); + } + } + return getDefaultForHost(); } @@ -974,7 +999,7 @@ static int performOperation(ArchiveOperation Operation, MemoryBuffer::getFile(ArchiveName, -1, false); std::error_code EC = Buf.getError(); if (EC && EC != errc::no_such_file_or_directory) - fail("error opening '" + ArchiveName + "': " + EC.message()); + fail("unable to open '" + ArchiveName + "': " + EC.message()); if (!EC) { Error Err = Error::success(); @@ -989,7 +1014,7 @@ static int performOperation(ArchiveOperation Operation, assert(EC == errc::no_such_file_or_directory); if (!shouldCreateArchive(Operation)) { - failIfError(EC, Twine("error loading '") + ArchiveName + "'"); + failIfError(EC, Twine("unable to load '") + ArchiveName + "'"); } else { if (!Create) { // Produce a warning if we should and we're creating the archive @@ -1058,7 +1083,7 @@ static void runMRIScript() { fail("editing multiple archives not supported"); if (Saved) fail("file already saved"); - ArchiveName = Rest; + ArchiveName = std::string(Rest); break; case MRICommand::Delete: { llvm::erase_if(NewMembers, [=](NewArchiveMember &M) { @@ -1075,9 +1100,9 @@ static void runMRIScript() { fail("unknown command: " + CommandStr); } } - + ParsingMRIScript = false; - + // Nothing to do if not saved. if (Saved) performOperation(ReplaceOrInsert, &NewMembers); @@ -1096,61 +1121,103 @@ static bool handleGenericOption(StringRef arg) { return false; } +static const char *matchFlagWithArg(StringRef Expected, + ArrayRef<const char *>::iterator &ArgIt, + ArrayRef<const char *> Args) { + StringRef Arg = *ArgIt; + + if (Arg.startswith("--")) + Arg = Arg.substr(2); + else if (Arg.startswith("-")) + Arg = Arg.substr(1); + + size_t len = Expected.size(); + if (Arg == Expected) { + if (++ArgIt == Args.end()) + fail(std::string(Expected) + " requires an argument"); + + return *ArgIt; + } + if (Arg.startswith(Expected) && Arg.size() > len && Arg[len] == '=') + return Arg.data() + len + 1; + + return nullptr; +} + +static cl::TokenizerCallback getRspQuoting(ArrayRef<const char *> ArgsArr) { + cl::TokenizerCallback Ret = + Triple(sys::getProcessTriple()).getOS() == Triple::Win32 + ? cl::TokenizeWindowsCommandLine + : cl::TokenizeGNUCommandLine; + + for (ArrayRef<const char *>::iterator ArgIt = ArgsArr.begin(); + ArgIt != ArgsArr.end(); ++ArgIt) { + if (const char *Match = matchFlagWithArg("rsp-quoting", ArgIt, ArgsArr)) { + StringRef MatchRef = Match; + if (MatchRef == "posix") + Ret = cl::TokenizeGNUCommandLine; + else if (MatchRef == "windows") + Ret = cl::TokenizeWindowsCommandLine; + else + fail(std::string("Invalid response file quoting style ") + Match); + } + } + + return Ret; +} + static int ar_main(int argc, char **argv) { - SmallVector<const char *, 0> Argv(argv, argv + argc); + SmallVector<const char *, 0> Argv(argv + 1, argv + argc); StringSaver Saver(Alloc); - cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv); - for (size_t i = 1; i < Argv.size(); ++i) { - StringRef Arg = Argv[i]; - const char *match = nullptr; - auto MatchFlagWithArg = [&](const char *expected) { - size_t len = strlen(expected); - if (Arg == expected) { - if (++i >= Argv.size()) - fail(std::string(expected) + " requires an argument"); - match = Argv[i]; - return true; - } - if (Arg.startswith(expected) && Arg.size() > len && Arg[len] == '=') { - match = Arg.data() + len + 1; - return true; - } - return false; - }; - if (handleGenericOption(Argv[i])) + + cl::ExpandResponseFiles(Saver, getRspQuoting(makeArrayRef(argv, argc)), Argv); + + for (ArrayRef<const char *>::iterator ArgIt = Argv.begin(); + ArgIt != Argv.end(); ++ArgIt) { + const char *Match = nullptr; + + if (handleGenericOption(*ArgIt)) return 0; - if (Arg == "--") { - for (; i < Argv.size(); ++i) - PositionalArgs.push_back(Argv[i]); + if (strcmp(*ArgIt, "--") == 0) { + ++ArgIt; + for (; ArgIt != Argv.end(); ++ArgIt) + PositionalArgs.push_back(*ArgIt); break; } - if (Arg[0] == '-') { - if (Arg.startswith("--")) - Arg = Argv[i] + 2; + + if (*ArgIt[0] != '-') { + if (Options.empty()) + Options += *ArgIt; else - Arg = Argv[i] + 1; - if (Arg == "M") { - MRI = true; - } else if (MatchFlagWithArg("format")) { - FormatType = StringSwitch<Format>(match) - .Case("default", Default) - .Case("gnu", GNU) - .Case("darwin", DARWIN) - .Case("bsd", BSD) - .Default(Unknown); - if (FormatType == Unknown) - fail(std::string("Invalid format ") + match); - } else if (MatchFlagWithArg("plugin")) { - // Ignored. - } else { - Options += Argv[i] + 1; - } - } else if (Options.empty()) { - Options += Argv[i]; - } else { - PositionalArgs.push_back(Argv[i]); + PositionalArgs.push_back(*ArgIt); + continue; } + + if (strcmp(*ArgIt, "-M") == 0) { + MRI = true; + continue; + } + + Match = matchFlagWithArg("format", ArgIt, Argv); + if (Match) { + FormatType = StringSwitch<Format>(Match) + .Case("default", Default) + .Case("gnu", GNU) + .Case("darwin", DARWIN) + .Case("bsd", BSD) + .Default(Unknown); + if (FormatType == Unknown) + fail(std::string("Invalid format ") + Match); + continue; + } + + if (matchFlagWithArg("plugin", ArgIt, Argv) || + matchFlagWithArg("rsp-quoting", ArgIt, Argv)) + continue; + + Options += *ArgIt + 1; } + ArchiveOperation Operation = parseCommandLine(); return performOperation(Operation, nullptr); } diff --git a/llvm/tools/llvm-as/llvm-as.cpp b/llvm/tools/llvm-as/llvm-as.cpp index c9f50e38fc618..f2b52890a7f5c 100644 --- a/llvm/tools/llvm-as/llvm-as.cpp +++ b/llvm/tools/llvm-as/llvm-as.cpp @@ -88,11 +88,13 @@ static void WriteOutputFile(const Module *M, const ModuleSummaryIndex *Index) { exit(1); } - if (Force || !CheckBitcodeOutputToConsole(Out->os(), true)) { + if (Force || !CheckBitcodeOutputToConsole(Out->os())) { const ModuleSummaryIndex *IndexToWrite = nullptr; - // Don't attempt to write a summary index unless it contains any entries. - // Otherwise we get an empty summary section. - if (Index && Index->begin() != Index->end()) + // Don't attempt to write a summary index unless it contains any entries or + // has non-zero flags. The latter is used to assemble dummy index files for + // skipping modules by distributed ThinLTO backends. Otherwise we get an empty + // summary section. + if (Index && (Index->begin() != Index->end() || Index->getFlags())) IndexToWrite = Index; if (!IndexToWrite || (M && (!M->empty() || !M->global_empty()))) // If we have a non-empty Module, then we write the Module plus @@ -119,8 +121,19 @@ int main(int argc, char **argv) { // Parse the file now... SMDiagnostic Err; - auto ModuleAndIndex = parseAssemblyFileWithIndex( - InputFilename, Err, Context, nullptr, !DisableVerify, ClDataLayout); + auto SetDataLayout = [](StringRef) -> Optional<std::string> { + if (ClDataLayout.empty()) + return None; + return ClDataLayout; + }; + ParsedModuleAndIndex ModuleAndIndex; + if (DisableVerify) { + ModuleAndIndex = parseAssemblyFileWithIndexNoUpgradeDebugInfo( + InputFilename, Err, Context, nullptr, SetDataLayout); + } else { + ModuleAndIndex = parseAssemblyFileWithIndex(InputFilename, Err, Context, + nullptr, SetDataLayout); + } std::unique_ptr<Module> M = std::move(ModuleAndIndex.Mod); if (!M.get()) { Err.print(argv[0], errs()); diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp index 5f1e23f20d772..b3c895b44a6d6 100644 --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -33,6 +33,7 @@ #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/SpecialCaseList.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/Threading.h" #include "llvm/Support/ToolOutputFile.h" @@ -245,7 +246,8 @@ CodeCoverageTool::getSourceFile(StringRef SourceFile) { error(EC.message(), SourceFile); return EC; } - LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get())); + LoadedSourceFiles.emplace_back(std::string(SourceFile), + std::move(Buffer.get())); return *LoadedSourceFiles.back().second; } @@ -413,7 +415,8 @@ void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { // Convert input files from local paths to coverage data file paths. StringMap<std::string> InvRemappedFilenames; for (const auto &RemappedFilename : RemappedFilenames) - InvRemappedFilenames[RemappedFilename.getValue()] = RemappedFilename.getKey(); + InvRemappedFilenames[RemappedFilename.getValue()] = + std::string(RemappedFilename.getKey()); for (std::string &Filename : SourceFiles) { SmallString<128> NativeFilename; @@ -510,7 +513,7 @@ void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { for (const auto &Function : Coverage.getCoveredFunctions()) // On Windows, lines in the demangler's output file end with "\r\n". // Splitting by '\n' keeps '\r's, so cut them now. - DC.DemangledNames[Function.Name] = Symbols[I++].rtrim(); + DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim()); } void CodeCoverageTool::writeSourceFileView(StringRef SourceFile, @@ -688,7 +691,8 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { // PathRemapping. auto EquivPair = StringRef(PathRemap).split(','); if (!(EquivPair.first.empty() && EquivPair.second.empty())) - PathRemapping = EquivPair; + PathRemapping = {std::string(EquivPair.first), + std::string(EquivPair.second)}; // If a demangler is supplied, check if it exists and register it. if (!DemanglerOpts.empty()) { @@ -864,8 +868,8 @@ int CodeCoverageTool::doShow(int argc, const char **argv, } sys::fs::file_status Status; - if (sys::fs::status(PGOFilename, Status)) { - error("profdata file error: can not get the file status. \n"); + if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { + error("Could not read profile data!", EC.message()); return 1; } @@ -886,7 +890,7 @@ int CodeCoverageTool::doShow(int argc, const char **argv, // Get the source files from the function coverage mapping. for (StringRef Filename : Coverage->getUniqueSourceFiles()) { if (!IgnoreFilenameFilters.matchesFilename(Filename)) - SourceFiles.push_back(Filename); + SourceFiles.push_back(std::string(Filename)); } // Create an index out of the source files. @@ -940,21 +944,21 @@ int CodeCoverageTool::doShow(int argc, const char **argv, (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); - auto NumThreads = ViewOpts.NumThreads; - - // If NumThreads is not specified, auto-detect a good default. - if (NumThreads == 0) - NumThreads = - std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), - unsigned(SourceFiles.size()))); + ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads); + if (ViewOpts.NumThreads == 0) { + // If NumThreads is not specified, create one thread for each input, up to + // the number of hardware cores. + S = heavyweight_hardware_concurrency(SourceFiles.size()); + S.Limit = true; + } - if (!ViewOpts.hasOutputDirectory() || NumThreads == 1) { + if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) { for (const std::string &SourceFile : SourceFiles) writeSourceFileView(SourceFile, Coverage.get(), Printer.get(), ShowFilenames); } else { // In -output-dir mode, it's safe to use multiple threads to print files. - ThreadPool Pool(NumThreads); + ThreadPool Pool(S); for (const std::string &SourceFile : SourceFiles) Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile, Coverage.get(), Printer.get(), ShowFilenames); diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp index 216b5e3fd2263..c8bb1aa5b6ea6 100644 --- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -53,7 +53,7 @@ #include <utility> /// The semantic version combined as a string. -#define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.0" +#define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.1" /// Unique type identifier for JSON coverage export. #define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export" @@ -72,8 +72,9 @@ int64_t clamp_uint64_to_int64(uint64_t u) { } json::Array renderSegment(const coverage::CoverageSegment &Segment) { - return json::Array({Segment.Line, Segment.Col, clamp_uint64_to_int64(Segment.Count), - Segment.HasCount, Segment.IsRegionEntry}); + return json::Array({Segment.Line, Segment.Col, + clamp_uint64_to_int64(Segment.Count), Segment.HasCount, + Segment.IsRegionEntry, Segment.IsGapRegion}); } json::Array renderRegion(const coverage::CountedRegion &Region) { @@ -162,12 +163,14 @@ json::Array renderFiles(const coverage::CoverageMapping &Coverage, ArrayRef<std::string> SourceFiles, ArrayRef<FileCoverageSummary> FileReports, const CoverageViewOptions &Options) { - auto NumThreads = Options.NumThreads; - if (NumThreads == 0) { - NumThreads = std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), - unsigned(SourceFiles.size()))); + ThreadPoolStrategy S = hardware_concurrency(Options.NumThreads); + if (Options.NumThreads == 0) { + // If NumThreads is not specified, create one thread for each input, up to + // the number of hardware cores. + S = heavyweight_hardware_concurrency(SourceFiles.size()); + S.Limit = true; } - ThreadPool Pool(NumThreads); + ThreadPool Pool(S); json::Array FileArray; std::mutex FileArrayMutex; diff --git a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp index d9b0c3b0d7a88..a6b3c66070304 100644 --- a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp @@ -78,10 +78,11 @@ void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, const std::string &Filename, - const FileCoverageSummary &FileReport, bool ExportSummaryOnly) { + const FileCoverageSummary &FileReport, bool ExportSummaryOnly, + bool SkipFunctions) { OS << "SF:" << Filename << '\n'; - if (!ExportSummaryOnly) { + if (!ExportSummaryOnly && !SkipFunctions) { renderFunctions(OS, Coverage.getCoveredFunctions(Filename)); } renderFunctionSummary(OS, FileReport); @@ -99,9 +100,10 @@ void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage, ArrayRef<std::string> SourceFiles, ArrayRef<FileCoverageSummary> FileReports, - bool ExportSummaryOnly) { + bool ExportSummaryOnly, bool SkipFunctions) { for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) - renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly); + renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly, + SkipFunctions); } } // end anonymous namespace @@ -119,6 +121,6 @@ void CoverageExporterLcov::renderRoot(ArrayRef<std::string> SourceFiles) { FileCoverageSummary Totals = FileCoverageSummary("Totals"); auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals, SourceFiles, Options); - renderFiles(OS, Coverage, SourceFiles, FileReports, - Options.ExportSummaryOnly); + renderFiles(OS, Coverage, SourceFiles, FileReports, Options.ExportSummaryOnly, + Options.SkipFunctions); } diff --git a/llvm/tools/llvm-cov/CoverageFilters.cpp b/llvm/tools/llvm-cov/CoverageFilters.cpp index ca241e386e87e..da3b5214eec4c 100644 --- a/llvm/tools/llvm-cov/CoverageFilters.cpp +++ b/llvm/tools/llvm-cov/CoverageFilters.cpp @@ -13,6 +13,7 @@ #include "CoverageFilters.h" #include "CoverageSummaryInfo.h" #include "llvm/Support/Regex.h" +#include "llvm/Support/SpecialCaseList.h" using namespace llvm; diff --git a/llvm/tools/llvm-cov/CoverageFilters.h b/llvm/tools/llvm-cov/CoverageFilters.h index ce56e16071117..33fd9929c59a2 100644 --- a/llvm/tools/llvm-cov/CoverageFilters.h +++ b/llvm/tools/llvm-cov/CoverageFilters.h @@ -13,13 +13,17 @@ #ifndef LLVM_COV_COVERAGEFILTERS_H #define LLVM_COV_COVERAGEFILTERS_H -#include "CoverageSummaryInfo.h" -#include "llvm/ProfileData/Coverage/CoverageMapping.h" -#include "llvm/Support/SpecialCaseList.h" +#include "llvm/ADT/StringRef.h" #include <memory> #include <vector> namespace llvm { +class SpecialCaseList; + +namespace coverage { +class CoverageMapping; +struct FunctionRecord; +} // namespace coverage /// Matches specific functions that pass the requirement of this filter. class CoverageFilter { diff --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp index 82259542c5970..8509710032d15 100644 --- a/llvm/tools/llvm-cov/CoverageReport.cpp +++ b/llvm/tools/llvm-cov/CoverageReport.cpp @@ -352,15 +352,15 @@ std::vector<FileCoverageSummary> CoverageReport::prepareFileReports( ArrayRef<std::string> Files, const CoverageViewOptions &Options, const CoverageFilter &Filters) { unsigned LCP = getRedundantPrefixLen(Files); - auto NumThreads = Options.NumThreads; - // If NumThreads is not specified, auto-detect a good default. - if (NumThreads == 0) - NumThreads = - std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), - unsigned(Files.size()))); - - ThreadPool Pool(NumThreads); + ThreadPoolStrategy S = hardware_concurrency(Options.NumThreads); + if (Options.NumThreads == 0) { + // If NumThreads is not specified, create one thread for each input, up to + // the number of hardware cores. + S = heavyweight_hardware_concurrency(Files.size()); + S.Limit = true; + } + ThreadPool Pool(S); std::vector<FileCoverageSummary> FileReports; FileReports.reserve(Files.size()); diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp index 1029f7784040d..929529c27b6ea 100644 --- a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp +++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp @@ -51,7 +51,7 @@ FunctionCoverageSummary::get(const InstantiationGroup &Group, ArrayRef<FunctionCoverageSummary> Summaries) { std::string Name; if (Group.hasName()) { - Name = Group.getName(); + Name = std::string(Group.getName()); } else { llvm::raw_string_ostream OS(Name); OS << "Definition at line " << Group.getLine() << ", column " diff --git a/llvm/tools/llvm-cov/SourceCoverageView.cpp b/llvm/tools/llvm-cov/SourceCoverageView.cpp index 0e20ea63cd6ff..cd7395a1a87d3 100644 --- a/llvm/tools/llvm-cov/SourceCoverageView.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageView.cpp @@ -48,7 +48,7 @@ std::string CoveragePrinter::getOutputPath(StringRef Path, StringRef Extension, sys::path::append(FullPath, PathFilename); sys::path::native(FullPath); - return FullPath.str(); + return std::string(FullPath.str()); } Expected<CoveragePrinter::OwnedStream> @@ -158,7 +158,7 @@ std::string SourceCoverageView::getSourceName() const { SmallString<128> SourceText(SourceName); sys::path::remove_dots(SourceText, /*remove_dot_dots=*/true); sys::path::native(SourceText); - return SourceText.str(); + return std::string(SourceText.str()); } void SourceCoverageView::addExpansion( diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp index e3332245f9c8e..9d10def0a211b 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -30,8 +30,7 @@ std::string escape(StringRef Str, const CoverageViewOptions &Opts) { if (C == '\t') { // Replace '\t' with up to TabSize spaces. unsigned NumSpaces = Opts.TabSize - (ColNum % Opts.TabSize); - for (unsigned I = 0; I < NumSpaces; ++I) - TabExpandedResult += ' '; + TabExpandedResult.append(NumSpaces, ' '); ColNum += NumSpaces; } else { TabExpandedResult += C; @@ -250,7 +249,7 @@ const char *CreatedTimeTag = "h4"; std::string getPathToStyle(StringRef ViewPath) { std::string PathToStyle = ""; - std::string PathSep = sys::path::get_separator(); + std::string PathSep = std::string(sys::path::get_separator()); unsigned NumSeps = ViewPath.count(PathSep); for (unsigned I = 0, E = NumSeps; I < E; ++I) PathToStyle += ".." + PathSep; @@ -359,7 +358,7 @@ void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF, // Simplify the display file path, and wrap it in a link if requested. std::string Filename; if (IsTotals) { - Filename = SF; + Filename = std::string(SF); } else { Filename = buildLinkToFile(SF, FCS); } @@ -507,7 +506,7 @@ void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L, unsigned LCol = 1; auto Snip = [&](unsigned Start, unsigned Len) { - Snippets.push_back(Line.substr(Start, Len)); + Snippets.push_back(std::string(Line.substr(Start, Len))); LCol += Len; }; @@ -533,7 +532,7 @@ void SourceCoverageViewHTML::renderLine(raw_ostream &OS, LineRef L, auto Highlight = [&](const std::string &Snippet, unsigned LC, unsigned RC) { if (getOptions().Debug) HighlightedRanges.emplace_back(LC, RC); - return tag("span", Snippet, Color.getValue()); + return tag("span", Snippet, std::string(Color.getValue())); }; auto CheckIfUncovered = [&](const CoverageSegment *S) { diff --git a/llvm/tools/llvm-cov/gcov.cpp b/llvm/tools/llvm-cov/gcov.cpp index 8a00ff64711fb..7a1dbbfe9338b 100644 --- a/llvm/tools/llvm-cov/gcov.cpp +++ b/llvm/tools/llvm-cov/gcov.cpp @@ -65,11 +65,11 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, // Clear the filename to make it clear we didn't read anything. GCDA = "-"; } else { - GCOVBuffer GCDA_GB(GCDA_Buff.get().get()); - if (!GF.readGCDA(GCDA_GB)) { + GCOVBuffer gcda_buf(GCDA_Buff.get().get()); + if (!gcda_buf.readGCDAFormat()) + errs() << GCDA << ":not a gcov data file\n"; + else if (!GF.readGCDA(gcda_buf)) errs() << "Invalid .gcda File!\n"; - return; - } } if (DumpGCOV) @@ -77,7 +77,7 @@ static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, FileInfo FI(Options); GF.collectLineCounts(FI); - FI.print(llvm::outs(), SourceFile, GCNO, GCDA); + FI.print(llvm::outs(), SourceFile, GCNO, GCDA, GF); } int gcovMain(int argc, const char *argv[]) { @@ -105,6 +105,16 @@ int gcovMain(int argc, const char *argv[]) { cl::desc("Show coverage for each function")); cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary)); + // Supported by gcov 4.9~8. gcov 9 (GCC r265587) removed --intermediate-format + // and -i was changed to mean --json-format. We consider this format still + // useful and support -i. + cl::opt<bool> Intermediate( + "intermediate-format", cl::init(false), + cl::desc("Output .gcov in intermediate text format")); + cl::alias IntermediateA("i", cl::desc("Alias for --intermediate-format"), + cl::Grouping, cl::NotHidden, + cl::aliasopt(Intermediate)); + cl::opt<bool> NoOutput("n", cl::Grouping, cl::init(false), cl::desc("Do not output any .gcov files")); cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput)); @@ -119,6 +129,10 @@ int gcovMain(int argc, const char *argv[]) { cl::desc("Preserve path components")); cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths)); + cl::opt<bool> UseStdout("t", cl::Grouping, cl::init(false), + cl::desc("Print to stdout")); + cl::alias UseStdoutA("stdout", cl::aliasopt(UseStdout)); + cl::opt<bool> UncondBranch("u", cl::Grouping, cl::init(false), cl::desc("Display unconditional branch info " "(requires -b)")); @@ -140,8 +154,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, - HashFilenames); + PreservePaths, UncondBranch, Intermediate, LongNames, + NoOutput, UseStdout, HashFilenames); for (const auto &SourceFile : SourceFiles) reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV, diff --git a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp index 6de512fc18dc0..93d6322a167ee 100644 --- a/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp +++ b/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp @@ -145,9 +145,9 @@ static void demangleLine(llvm::raw_ostream &OS, StringRef Mangled, bool Split) { SmallVector<std::pair<StringRef, StringRef>, 16> Words; SplitStringDelims(Mangled, Words, IsLegalItaniumChar); for (const auto &Word : Words) - Result += ::demangle(Word.first) + Word.second.str(); + Result += ::demangle(std::string(Word.first)) + Word.second.str(); } else - Result = ::demangle(Mangled); + Result = ::demangle(std::string(Mangled)); OS << Result << '\n'; OS.flush(); } diff --git a/llvm/tools/llvm-diff/DiffConsumer.cpp b/llvm/tools/llvm-diff/DiffConsumer.cpp index b797143bde1b4..6228ff2bae983 100644 --- a/llvm/tools/llvm-diff/DiffConsumer.cpp +++ b/llvm/tools/llvm-diff/DiffConsumer.cpp @@ -50,15 +50,15 @@ void DiffConsumer::printValue(Value *V, bool isL) { return; } if (V->getType()->isVoidTy()) { - if (isa<StoreInst>(V)) { + if (auto *SI = dyn_cast<StoreInst>(V)) { out << "store to "; - printValue(cast<StoreInst>(V)->getPointerOperand(), isL); - } else if (isa<CallInst>(V)) { + printValue(SI->getPointerOperand(), isL); + } else if (auto *CI = dyn_cast<CallInst>(V)) { out << "call to "; - printValue(cast<CallInst>(V)->getCalledValue(), isL); - } else if (isa<InvokeInst>(V)) { + printValue(CI->getCalledOperand(), isL); + } else if (auto *II = dyn_cast<InvokeInst>(V)) { out << "invoke to "; - printValue(cast<InvokeInst>(V)->getCalledValue(), isL); + printValue(II->getCalledOperand(), isL); } else { out << *V; } diff --git a/llvm/tools/llvm-diff/DifferenceEngine.cpp b/llvm/tools/llvm-diff/DifferenceEngine.cpp index 564ce78705927..2cf1afbc6af57 100644 --- a/llvm/tools/llvm-diff/DifferenceEngine.cpp +++ b/llvm/tools/llvm-diff/DifferenceEngine.cpp @@ -14,10 +14,10 @@ #include "DifferenceEngine.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSet.h" #include "llvm/IR/CFG.h" -#include "llvm/IR/CallSite.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" @@ -222,9 +222,9 @@ class FunctionDifferenceEngine { bool matchForBlockDiff(Instruction *L, Instruction *R); void runBlockDiff(BasicBlock::iterator LI, BasicBlock::iterator RI); - bool diffCallSites(CallSite L, CallSite R, bool Complain) { + bool diffCallSites(CallBase &L, CallBase &R, bool Complain) { // FIXME: call attributes - if (!equivalentAsOperands(L.getCalledValue(), R.getCalledValue())) { + if (!equivalentAsOperands(L.getCalledOperand(), R.getCalledOperand())) { if (Complain) Engine.log("called functions differ"); return true; } @@ -233,10 +233,10 @@ class FunctionDifferenceEngine { return true; } for (unsigned I = 0, E = L.arg_size(); I != E; ++I) - if (!equivalentAsOperands(L.getArgument(I), R.getArgument(I))) { + if (!equivalentAsOperands(L.getArgOperand(I), R.getArgOperand(I))) { if (Complain) Engine.logf("arguments %l and %r differ") - << L.getArgument(I) << R.getArgument(I); + << L.getArgOperand(I) << R.getArgOperand(I); return true; } return false; @@ -258,7 +258,7 @@ class FunctionDifferenceEngine { return true; } } else if (isa<CallInst>(L)) { - return diffCallSites(CallSite(L), CallSite(R), Complain); + return diffCallSites(cast<CallInst>(*L), cast<CallInst>(*R), Complain); } else if (isa<PHINode>(L)) { // FIXME: implement. @@ -273,14 +273,14 @@ class FunctionDifferenceEngine { // Terminators. } else if (isa<InvokeInst>(L)) { - InvokeInst *LI = cast<InvokeInst>(L); - InvokeInst *RI = cast<InvokeInst>(R); - if (diffCallSites(CallSite(LI), CallSite(RI), Complain)) + InvokeInst &LI = cast<InvokeInst>(*L); + InvokeInst &RI = cast<InvokeInst>(*R); + if (diffCallSites(LI, RI, Complain)) return true; if (TryUnify) { - tryUnify(LI->getNormalDest(), RI->getNormalDest()); - tryUnify(LI->getUnwindDest(), RI->getUnwindDest()); + tryUnify(LI.getNormalDest(), RI.getNormalDest()); + tryUnify(LI.getUnwindDest(), RI.getUnwindDest()); } return false; @@ -577,7 +577,7 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, DiffLogBuilder Diff(Engine.getConsumer()); // Drop trailing matches. - while (Path.back() == DC_match) + while (Path.size() && Path.back() == DC_match) Path.pop_back(); // Skip leading matches. @@ -638,7 +638,8 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, if (!isa<CallInst>(*I)) return; CallInst *LCall = cast<CallInst>(&*I); InvokeInst *RInvoke = cast<InvokeInst>(RTerm); - if (!equivalentAsOperands(LCall->getCalledValue(), RInvoke->getCalledValue())) + if (!equivalentAsOperands(LCall->getCalledOperand(), + RInvoke->getCalledOperand())) return; if (!LCall->use_empty()) Values[LCall] = RInvoke; @@ -651,7 +652,8 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, if (!isa<CallInst>(*I)) return; CallInst *RCall = cast<CallInst>(I); InvokeInst *LInvoke = cast<InvokeInst>(LTerm); - if (!equivalentAsOperands(LInvoke->getCalledValue(), RCall->getCalledValue())) + if (!equivalentAsOperands(LInvoke->getCalledOperand(), + RCall->getCalledOperand())) return; if (!LInvoke->use_empty()) Values[LInvoke] = RCall; diff --git a/llvm/tools/llvm-dwarfdump/SectionSizes.cpp b/llvm/tools/llvm-dwarfdump/SectionSizes.cpp new file mode 100644 index 0000000000000..8c456d50baa7c --- /dev/null +++ b/llvm/tools/llvm-dwarfdump/SectionSizes.cpp @@ -0,0 +1,124 @@ +//===-- SectionSizes.cpp - Debug section sizes ----------------------------===// +// +// 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-dwarfdump.h" + +#define DEBUG_TYPE "dwarfdump" + +using namespace llvm; +using namespace llvm::dwarfdump; +using namespace llvm::object; + +static size_t getNameColumnWidth(const SectionSizes &Sizes, + const StringRef SectionNameTitle) { + // The minimum column width should be the size of "SECTION". + size_t Width = SectionNameTitle.size(); + for (const auto &DebugSec : Sizes.DebugSectionSizes) { + StringRef SectionName = DebugSec.getKey(); + Width = std::max(Width, SectionName.size()); + } + return Width; +} + +static size_t getSizeColumnWidth(const SectionSizes &Sizes, + const StringRef SectionSizeTitle) { + // The minimum column width should be the size of the column title. + size_t Width = SectionSizeTitle.size(); + for (const auto &DebugSec : Sizes.DebugSectionSizes) { + size_t NumWidth = std::to_string(DebugSec.getValue()).size(); + Width = std::max(Width, NumWidth); + } + return Width; +} + +static void prettyPrintSectionSizes(const ObjectFile &Obj, + const SectionSizes &Sizes, + raw_ostream &OS) { + const StringRef SectionNameTitle = "SECTION"; + const StringRef SectionSizeTitle = "SIZE (b)"; + + size_t NameColWidth = getNameColumnWidth(Sizes, SectionNameTitle); + size_t SizeColWidth = getSizeColumnWidth(Sizes, SectionSizeTitle); + + OS << "----------------------------------------------------" << '\n'; + OS << SectionNameTitle; + size_t SectionNameTitleWidth = SectionNameTitle.size(); + for (unsigned i = 0; i < (NameColWidth - SectionNameTitleWidth) + 2; i++) + OS << " "; + OS << SectionSizeTitle << '\n'; + for (unsigned i = 0; i < NameColWidth; i++) + OS << "-"; + OS << " "; + + for (unsigned i = 0; i < SizeColWidth; i++) + OS << "-"; + OS << '\n'; + + for (const auto &DebugSec : Sizes.DebugSectionSizes) { + OS << left_justify(DebugSec.getKey(), NameColWidth) << " "; + + auto NumBytes = std::to_string(DebugSec.getValue()); + OS << right_justify(NumBytes, SizeColWidth) << " (" + << format("%0.2f", DebugSec.getValue() / + static_cast<double>(Sizes.TotalObjectSize) * 100) + << "%)\n"; + } + + OS << '\n'; + OS << " Total Size: " << Sizes.TotalDebugSectionsSize << " (" + << format("%0.2f", Sizes.TotalDebugSectionsSize / + static_cast<double>(Sizes.TotalObjectSize) * 100) + << "%)\n"; + OS << " Total File Size: " << Sizes.TotalObjectSize << '\n'; + OS << "----------------------------------------------------" << '\n'; +} + +void dwarfdump::calculateSectionSizes(const ObjectFile &Obj, + SectionSizes &Sizes, + const Twine &Filename) { + // Get total size. + Sizes.TotalObjectSize = Obj.getData().size(); + + for (const SectionRef &Section : Obj.sections()) { + StringRef SectionName; + if (Expected<StringRef> NameOrErr = Section.getName()) + SectionName = *NameOrErr; + else + WithColor::defaultWarningHandler( + createFileError(Filename, NameOrErr.takeError())); + + LLVM_DEBUG(dbgs() << SectionName.str() << ": " << Section.getSize() + << '\n'); + + if (!Section.isDebugSection(SectionName)) + continue; + + Sizes.TotalDebugSectionsSize += Section.getSize(); + Sizes.DebugSectionSizes[SectionName] += Section.getSize(); + } +} + +bool dwarfdump::collectObjectSectionSizes(ObjectFile &Obj, + DWARFContext & /*DICtx*/, + const Twine &Filename, + raw_ostream &OS) { + SectionSizes Sizes; + + // Get the section sizes. + calculateSectionSizes(Obj, Sizes, Filename); + + OS << "----------------------------------------------------\n"; + OS << "file: " << Filename.str() << '\n'; + + prettyPrintSectionSizes(Obj, Sizes, OS); + + // TODO: If the input file is an archive, print the cumulative summary of all + // files from the archive. + + return true; +} diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp index 5bef4d5148ca3..18b4c40c4d751 100644 --- a/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -1,3 +1,12 @@ +//===-- Statistics.cpp - Debug Info quality metrics -----------------------===// +// +// 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-dwarfdump.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" @@ -9,7 +18,8 @@ #define DEBUG_TYPE "dwarfdump" using namespace llvm; -using namespace object; +using namespace llvm::dwarfdump; +using namespace llvm::object; /// This represents the number of categories of debug location coverage being /// calculated. The first category is the number of variables with 0% location @@ -17,11 +27,14 @@ using namespace object; /// location coverage. constexpr int NumOfCoverageCategories = 12; +namespace { /// Holds statistics for one function (or other entity that has a PC range and /// contains variables, such as a compile unit). struct PerFunctionStats { /// Number of inlined instances of this function. unsigned NumFnInlined = 0; + /// Number of out-of-line instances of this function. + unsigned NumFnOutOfLine = 0; /// Number of inlined instances that have abstract origins. unsigned NumAbstractOrigins = 0; /// Number of variables and parameters with location across all inlined @@ -29,13 +42,12 @@ struct PerFunctionStats { unsigned TotalVarWithLoc = 0; /// Number of constants with location across all inlined instances. unsigned ConstantMembers = 0; + /// Number of arificial variables, parameters or members across all instances. + unsigned NumArtificial = 0; /// 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. @@ -46,14 +58,14 @@ struct PerFunctionStats { 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 with type. - unsigned NumVarTypes = 0; - /// Number of variables with DW_AT_location. - unsigned NumVarLocations = 0; + /// Number of local variables. + unsigned NumLocalVars = 0; + /// Number of local variables with source location. + unsigned NumLocalVarSourceLocations = 0; + /// Number of local variables with type. + unsigned NumLocalVarTypes = 0; + /// Number of local variables with DW_AT_location. + unsigned NumLocalVarLocations = 0; }; /// Holds accumulated global statistics about DIEs. @@ -68,21 +80,19 @@ struct GlobalStats { /// Total number of PC range bytes covered by DW_AT_locations of /// formal parameters. unsigned ParamScopeBytesCovered = 0; - /// Total number of PC range bytes in each variable's enclosing scope - /// (only for parameters). + /// Total number of PC range bytes in each parameter's enclosing scope. unsigned ParamScopeBytes = 0; /// Total number of PC range bytes covered by DW_AT_locations with /// the debug entry values (DW_OP_entry_value) (only for parameters). unsigned ParamScopeEntryValueBytesCovered = 0; /// Total number of PC range bytes covered by DW_AT_locations (only for local /// variables). - unsigned VarScopeBytesCovered = 0; - /// Total number of PC range bytes in each variable's enclosing scope - /// (only for local variables). - unsigned VarScopeBytes = 0; + unsigned LocalVarScopeBytesCovered = 0; + /// Total number of PC range bytes in each local variable's enclosing scope. + unsigned LocalVarScopeBytes = 0; /// Total number of PC range bytes covered by DW_AT_locations with /// the debug entry values (DW_OP_entry_value) (only for local variables). - unsigned VarScopeEntryValueBytesCovered = 0; + unsigned LocalVarScopeEntryValueBytesCovered = 0; /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line). unsigned CallSiteEntries = 0; /// Total number of call site DIEs (DW_TAG_call_site). @@ -118,10 +128,10 @@ struct LocationStats { std::vector<unsigned> ParamNonEntryValLocStats{ std::vector<unsigned>(NumOfCoverageCategories, 0)}; /// The debug location statistics for local variables. - std::vector<unsigned> VarLocStats{ + std::vector<unsigned> LocalVarLocStats{ std::vector<unsigned>(NumOfCoverageCategories, 0)}; /// Map non debug entry values coverage for local variables. - std::vector<unsigned> VarNonEntryValLocStats{ + std::vector<unsigned> LocalVarNonEntryValLocStats{ std::vector<unsigned>(NumOfCoverageCategories, 0)}; /// Total number of local variables and function parameters processed. unsigned NumVarParam = 0; @@ -130,13 +140,14 @@ struct LocationStats { /// Total number of local variables processed. unsigned NumVar = 0; }; +} // namespace /// Collect debug location statistics for one DIE. static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope, std::vector<unsigned> &VarParamLocStats, std::vector<unsigned> &ParamLocStats, - std::vector<unsigned> &VarLocStats, bool IsParam, - bool IsLocalVar) { + std::vector<unsigned> &LocalVarLocStats, + bool IsParam, bool IsLocalVar) { auto getCoverageBucket = [BytesCovered, BytesInScope]() -> unsigned { // No debug location at all for the variable. if (BytesCovered == 0) @@ -155,7 +166,36 @@ static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope, if (IsParam) ParamLocStats[CoverageBucket]++; else if (IsLocalVar) - VarLocStats[CoverageBucket]++; + LocalVarLocStats[CoverageBucket]++; +} +/// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName +/// and DeclLine. The identifier aims to be unique for any unique entities, +/// but keeping the same among different instances of the same entity. +static std::string constructDieID(DWARFDie Die, + StringRef Prefix = StringRef()) { + std::string IDStr; + llvm::raw_string_ostream ID(IDStr); + ID << Prefix + << Die.getName(DINameKind::LinkageName); + + // Prefix + Name is enough for local variables and parameters. + if (!Prefix.empty() && !Prefix.equals("g")) + return ID.str(); + + auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file); + std::string File; + if (DeclFile) { + DWARFUnit *U = Die.getDwarfUnit(); + if (const auto *LT = U->getContext().getLineTableForUnit(U)) + if (LT->getFileNameByIndex( + dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) + File = std::string(sys::path::filename(File)); + } + ID << ":" << (File.empty() ? "/" : File); + ID << ":" + << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0); + return ID.str(); } /// Collect debug info quality metrics for one DIE. @@ -168,12 +208,13 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, bool HasLoc = false; bool HasSrcLoc = false; bool HasType = false; - bool IsArtificial = false; uint64_t BytesCovered = 0; uint64_t BytesEntryValuesCovered = 0; auto &FnStats = FnStatMap[FnPrefix]; bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter; bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable; + bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member && + Die.find(dwarf::DW_AT_const_value); if (Die.getTag() == dwarf::DW_TAG_call_site || Die.getTag() == dwarf::DW_TAG_GNU_call_site) { @@ -187,11 +228,15 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, return; } - if (!IsParam && !IsLocalVar && Die.getTag() != dwarf::DW_TAG_member) { + if (!IsParam && !IsLocalVar && !IsConstantMember) { // Not a variable or constant member. return; } + // Ignore declarations of global variables. + if (IsLocalVar && Die.find(dwarf::DW_AT_declaration)) + return; + if (Die.findRecursively(dwarf::DW_AT_decl_file) && Die.findRecursively(dwarf::DW_AT_decl_line)) HasSrcLoc = true; @@ -199,14 +244,12 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, if (Die.findRecursively(dwarf::DW_AT_type)) HasType = true; - if (Die.find(dwarf::DW_AT_artificial)) - IsArtificial = true; - auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool { DWARFUnit *U = Die.getDwarfUnit(); DataExtractor Data(toStringRef(D), Die.getDwarfUnit()->getContext().isLittleEndian(), 0); - DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); + DWARFExpression Expression(Data, U->getAddressByteSize(), + U->getFormParams().Format); // Consider the expression containing the DW_OP_entry_value as // an entry value. return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { @@ -220,10 +263,6 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, HasLoc = true; BytesCovered = BytesInScope; } else { - if (Die.getTag() == dwarf::DW_TAG_member) { - // Non-const member. - return; - } // Handle variables and function arguments. Expected<std::vector<DWARFLocationExpression>> Loc = Die.getLocations(dwarf::DW_AT_location); @@ -257,25 +296,24 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, LocStats.NumVar++; collectLocStats(BytesCovered, BytesInScope, LocStats.VarParamLocStats, - LocStats.ParamLocStats, LocStats.VarLocStats, IsParam, + LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam, IsLocalVar); // Non debug entry values coverage statistics. collectLocStats(BytesCovered - BytesEntryValuesCovered, BytesInScope, LocStats.VarParamNonEntryValLocStats, LocStats.ParamNonEntryValLocStats, - LocStats.VarNonEntryValLocStats, IsParam, IsLocalVar); + LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar); } // Collect PC range coverage data. if (DWARFDie D = Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) Die = D; - // 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); + + std::string VarID = constructDieID(Die, VarPrefix); + FnStats.VarsInFunction.insert(VarID); + if (BytesInScope) { - FnStats.TotalVarWithLoc += (unsigned)HasLoc; // Turns out we have a lot of ranges that extend past the lexical scope. GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered); GlobalStats.ScopeBytes += BytesInScope; @@ -286,34 +324,43 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, GlobalStats.ParamScopeBytes += BytesInScope; GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered; } else if (IsLocalVar) { - GlobalStats.VarScopeBytesCovered += std::min(BytesInScope, BytesCovered); - GlobalStats.VarScopeBytes += BytesInScope; - GlobalStats.VarScopeEntryValueBytesCovered += BytesEntryValuesCovered; + GlobalStats.LocalVarScopeBytesCovered += + std::min(BytesInScope, BytesCovered); + GlobalStats.LocalVarScopeBytes += BytesInScope; + GlobalStats.LocalVarScopeEntryValueBytesCovered += + BytesEntryValuesCovered; } assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes); - } else if (Die.getTag() == dwarf::DW_TAG_member) { + } + + if (IsConstantMember) { FnStats.ConstantMembers++; - } else { - FnStats.TotalVarWithLoc += (unsigned)HasLoc; + return; } - if (!IsArtificial) { - if (IsParam) { - FnStats.NumParams++; - if (HasType) - FnStats.NumParamTypes++; - if (HasSrcLoc) - FnStats.NumParamSourceLocations++; - if (HasLoc) - FnStats.NumParamLocations++; - } else if (IsLocalVar) { - FnStats.NumVars++; - if (HasType) - FnStats.NumVarTypes++; - if (HasSrcLoc) - FnStats.NumVarSourceLocations++; - if (HasLoc) - FnStats.NumVarLocations++; - } + + FnStats.TotalVarWithLoc += (unsigned)HasLoc; + + if (Die.find(dwarf::DW_AT_artificial)) { + FnStats.NumArtificial++; + return; + } + + if (IsParam) { + FnStats.NumParams++; + if (HasType) + FnStats.NumParamTypes++; + if (HasSrcLoc) + FnStats.NumParamSourceLocations++; + if (HasLoc) + FnStats.NumParamLocations++; + } else if (IsLocalVar) { + FnStats.NumLocalVars++; + if (HasType) + FnStats.NumLocalVarTypes++; + if (HasSrcLoc) + FnStats.NumLocalVarSourceLocations++; + if (HasLoc) + FnStats.NumLocalVarLocations++; } } @@ -324,8 +371,12 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats, LocationStats &LocStats) { - // Handle any kind of lexical scope. const dwarf::Tag Tag = Die.getTag(); + // Skip function types. + if (Tag == dwarf::DW_TAG_subroutine_type) + return; + + // Handle any kind of lexical scope. const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; @@ -358,27 +409,25 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, // 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]; + std::string FnID = constructDieID(Die); + // We've seen an instance of this function. + auto &FnStats = FnStatMap[FnID]; + FnStats.IsFunction = true; if (IsInlinedFunction) { FnStats.NumFnInlined++; if (Die.findRecursively(dwarf::DW_AT_abstract_origin)) FnStats.NumAbstractOrigins++; + } else { + FnStats.NumFnOutOfLine++; } - 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; + // Update function prefix. + FnPrefix = FnID; } if (BytesInThisScope) { @@ -402,11 +451,14 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, // Traverse children. unsigned LexicalBlockIndex = 0; + unsigned FormalParameterIndex = 0; DWARFDie Child = Die.getFirstChild(); while (Child) { std::string ChildVarPrefix = VarPrefix; if (Child.getTag() == dwarf::DW_TAG_lexical_block) ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; + if (Child.getTag() == dwarf::DW_TAG_formal_parameter) + ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.'; collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap, GlobalStats, LocStats); @@ -421,29 +473,44 @@ static void printDatum(raw_ostream &OS, const char *Key, json::Value Value) { OS << ",\"" << Key << "\":" << Value; LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); } -static void printLocationStats(raw_ostream &OS, - const char *Key, + +static void printLocationStats(raw_ostream &OS, const char *Key, std::vector<unsigned> &LocationStats) { - OS << ",\"" << Key << " with 0% of its scope covered\":" + OS << ",\"" << Key << " with 0% of parent scope covered by DW_AT_location\":" << LocationStats[0]; - LLVM_DEBUG(llvm::dbgs() << Key << " with 0% of its scope covered: " - << LocationStats[0] << '\n'); - OS << ",\"" << Key << " with (0%,10%) of its scope covered\":" + LLVM_DEBUG( + llvm::dbgs() << Key + << " with 0% of parent scope covered by DW_AT_location: \\" + << LocationStats[0] << '\n'); + OS << ",\"" << Key + << " with (0%,10%) of parent scope covered by DW_AT_location\":" << LocationStats[1]; - LLVM_DEBUG(llvm::dbgs() << Key << " with (0%,10%) of its scope covered: " - << LocationStats[1] << '\n'); + LLVM_DEBUG(llvm::dbgs() + << Key + << " with (0%,10%) of parent scope covered by DW_AT_location: " + << LocationStats[1] << '\n'); for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { OS << ",\"" << Key << " with [" << (i - 1) * 10 << "%," << i * 10 - << "%) of its scope covered\":" << LocationStats[i]; + << "%) of parent scope covered by DW_AT_location\":" << LocationStats[i]; LLVM_DEBUG(llvm::dbgs() << Key << " with [" << (i - 1) * 10 << "%," << i * 10 - << "%) of its scope covered: " << LocationStats[i]); + << "%) of parent scope covered by DW_AT_location: " + << LocationStats[i]); } - OS << ",\"" << Key << " with 100% of its scope covered\":" + OS << ",\"" << Key + << " with 100% of parent scope covered by DW_AT_location\":" << LocationStats[NumOfCoverageCategories - 1]; - LLVM_DEBUG(llvm::dbgs() << Key << " with 100% of its scope covered: " - << LocationStats[NumOfCoverageCategories - 1]); + LLVM_DEBUG( + llvm::dbgs() << Key + << " with 100% of parent scope covered by DW_AT_location: " + << LocationStats[NumOfCoverageCategories - 1]); } + +static void printSectionSizes(raw_ostream &OS, const SectionSizes &Sizes) { + for (const auto &DebugSec : Sizes.DebugSectionSizes) + OS << ",\"#bytes in " << DebugSec.getKey() << "\":" << DebugSec.getValue(); +} + /// \} /// Collect debug info quality metrics for an entire DIContext. @@ -454,8 +521,9 @@ static void printLocationStats(raw_ostream &OS, /// of particular optimizations. The raw numbers themselves are not particularly /// useful, only the delta between compiling the same program with different /// compilers is. -bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, - Twine Filename, raw_ostream &OS) { +bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, + const Twine &Filename, + raw_ostream &OS) { StringRef FormatName = Obj.getFileFormatName(); GlobalStats GlobalStats; LocationStats LocStats; @@ -465,10 +533,14 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats); + /// Collect the sizes of debug sections. + SectionSizes Sizes; + calculateSectionSizes(Obj, Sizes, Filename); + /// 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 = 4; + unsigned Version = 5; unsigned VarParamTotal = 0; unsigned VarParamUnique = 0; unsigned VarParamWithLoc = 0; @@ -480,16 +552,18 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, unsigned ParamWithType = 0; unsigned ParamWithLoc = 0; unsigned ParamWithSrcLoc = 0; - unsigned VarTotal = 0; - unsigned VarWithType = 0; - unsigned VarWithSrcLoc = 0; - unsigned VarWithLoc = 0; + unsigned LocalVarTotal = 0; + unsigned LocalVarWithType = 0; + unsigned LocalVarWithSrcLoc = 0; + unsigned LocalVarWithLoc = 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 TotalVars = Stats.VarsInFunction.size() * + (Stats.NumFnInlined + Stats.NumFnOutOfLine); + // Count variables in global scope. + if (!Stats.IsFunction) + TotalVars = + Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial; unsigned Constants = Stats.ConstantMembers; VarParamWithLoc += Stats.TotalVarWithLoc + Constants; VarParamTotal += TotalVars; @@ -505,10 +579,10 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, ParamWithType += Stats.NumParamTypes; ParamWithLoc += Stats.NumParamLocations; ParamWithSrcLoc += Stats.NumParamSourceLocations; - VarTotal += Stats.NumVars; - VarWithType += Stats.NumVarTypes; - VarWithLoc += Stats.NumVarLocations; - VarWithSrcLoc += Stats.NumVarSourceLocations; + LocalVarTotal += Stats.NumLocalVars; + LocalVarWithType += Stats.NumLocalVarTypes; + LocalVarWithLoc += Stats.NumLocalVarLocations; + LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations; } // Print summary. @@ -516,56 +590,97 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, OS << "{\"version\":" << Version; LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; llvm::dbgs() << "---------------------------------\n"); + 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, "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, "call site DIEs", GlobalStats.CallSiteDIEs); - printDatum(OS, "call site parameter DIEs", GlobalStats.CallSiteParamDIEs); - printDatum(OS, "scope bytes total", GlobalStats.ScopeBytes); - printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); - printDatum(OS, "entry value scope bytes covered", + + printDatum(OS, "#functions", NumFunctions); + printDatum(OS, "#functions with location", NumFuncsWithSrcLoc); + printDatum(OS, "#inlined functions", NumInlinedFunctions); + printDatum(OS, "#inlined functions with abstract origins", + NumAbstractOrigins); + + // This includes local variables and formal parameters. + printDatum(OS, "#unique source variables", VarParamUnique); + printDatum(OS, "#source variables", VarParamTotal); + printDatum(OS, "#source variables with location", VarParamWithLoc); + + printDatum(OS, "#call site entries", GlobalStats.CallSiteEntries); + printDatum(OS, "#call site DIEs", GlobalStats.CallSiteDIEs); + printDatum(OS, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs); + + printDatum(OS, "sum_all_variables(#bytes in parent scope)", + GlobalStats.ScopeBytes); + printDatum(OS, + "sum_all_variables(#bytes in parent scope covered by " + "DW_AT_location)", + GlobalStats.ScopeBytesCovered); + printDatum(OS, + "sum_all_variables(#bytes in parent scope covered by " + "DW_OP_entry_value)", GlobalStats.ScopeEntryValueBytesCovered); - printDatum(OS, "formal params scope bytes total", + + printDatum(OS, "sum_all_params(#bytes in parent scope)", GlobalStats.ParamScopeBytes); - printDatum(OS, "formal params scope bytes covered", - GlobalStats.ParamScopeBytesCovered); - printDatum(OS, "formal params entry value scope bytes covered", + printDatum( + OS, + "sum_all_params(#bytes in parent scope covered by DW_AT_location)", + GlobalStats.ParamScopeBytesCovered); + printDatum(OS, + "sum_all_params(#bytes in parent scope covered by " + "DW_OP_entry_value)", GlobalStats.ParamScopeEntryValueBytesCovered); - printDatum(OS, "vars scope bytes total", GlobalStats.VarScopeBytes); - printDatum(OS, "vars scope bytes covered", GlobalStats.VarScopeBytesCovered); - printDatum(OS, "vars entry value scope bytes covered", - GlobalStats.VarScopeEntryValueBytesCovered); - 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); - printDatum(OS, "total variables procesed by location statistics", + + printDatum(OS, "sum_all_local_vars(#bytes in parent scope)", + GlobalStats.LocalVarScopeBytes); + printDatum(OS, + "sum_all_local_vars(#bytes in parent scope covered by " + "DW_AT_location)", + GlobalStats.LocalVarScopeBytesCovered); + printDatum(OS, + "sum_all_local_vars(#bytes in parent scope covered by " + "DW_OP_entry_value)", + GlobalStats.LocalVarScopeEntryValueBytesCovered); + + printDatum(OS, "#bytes witin functions", GlobalStats.FunctionSize); + printDatum(OS, "#bytes witin inlined functions", + GlobalStats.InlineFunctionSize); + + // Print the summary for formal parameters. + printDatum(OS, "#params", ParamTotal); + printDatum(OS, "#params with source location", ParamWithSrcLoc); + printDatum(OS, "#params with type", ParamWithType); + printDatum(OS, "#params with binary location", ParamWithLoc); + + // Print the summary for local variables. + printDatum(OS, "#local vars", LocalVarTotal); + printDatum(OS, "#local vars with source location", LocalVarWithSrcLoc); + printDatum(OS, "#local vars with type", LocalVarWithType); + printDatum(OS, "#local vars with binary location", LocalVarWithLoc); + + // Print the debug section sizes. + printSectionSizes(OS, Sizes); + + // Print the location statistics for variables (includes local variables + // and formal parameters). + printDatum(OS, "#variables processed by location statistics", LocStats.NumVarParam); - printLocationStats(OS, "variables", LocStats.VarParamLocStats); - printLocationStats(OS, "variables (excluding the debug entry values)", + printLocationStats(OS, "#variables", LocStats.VarParamLocStats); + printLocationStats(OS, "#variables - entry values", LocStats.VarParamNonEntryValLocStats); - printDatum(OS, "total params procesed by location statistics", - LocStats.NumParam); - printLocationStats(OS, "params", LocStats.ParamLocStats); - printLocationStats(OS, "params (excluding the debug entry values)", + + // Print the location statistics for formal parameters. + printDatum(OS, "#params processed by location statistics", LocStats.NumParam); + printLocationStats(OS, "#params", LocStats.ParamLocStats); + printLocationStats(OS, "#params - entry values", LocStats.ParamNonEntryValLocStats); - printDatum(OS, "total vars procesed by location statistics", LocStats.NumVar); - printLocationStats(OS, "vars", LocStats.VarLocStats); - printLocationStats(OS, "vars (excluding the debug entry values)", - LocStats.VarNonEntryValLocStats); + + // Print the location statistics for local variables. + printDatum(OS, "#local vars processed by location statistics", + LocStats.NumVar); + printLocationStats(OS, "#local vars", LocStats.LocalVarLocStats); + printLocationStats(OS, "#local vars - entry values", + LocStats.LocalVarNonEntryValLocStats); OS << "}\n"; LLVM_DEBUG( llvm::dbgs() << "Total Availability: " diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index 374bdd482a8d6..d8fa4f9953dca 100644 --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm-dwarfdump.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" @@ -29,10 +30,13 @@ #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" +#include <cstdlib> using namespace llvm; -using namespace object; +using namespace llvm::dwarfdump; +using namespace llvm::object; +namespace { /// Parser for options that take an optional offest argument. /// @{ struct OffsetOption { @@ -40,6 +44,8 @@ struct OffsetOption { bool HasValue = false; bool IsRequested = false; }; +struct BoolOption : public OffsetOption {}; +} // namespace namespace llvm { namespace cl { @@ -57,7 +63,7 @@ public: return false; } if (Arg.getAsInteger(0, Val.Val)) - return O.error("'" + Arg + "' value invalid for integer argument!"); + return O.error("'" + Arg + "' value invalid for integer argument"); Val.HasValue = true; Val.IsRequested = true; return false; @@ -67,22 +73,42 @@ public: return ValueOptional; } - void printOptionInfo(const Option &O, size_t GlobalWidth) const { - outs() << " -" << O.ArgStr; - Option::printHelpStr(O.HelpStr, GlobalWidth, getOptionWidth(O)); - } + StringRef getValueName() const override { return StringRef("offset"); } void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, size_t GlobalWidth) const { printOptionName(O, GlobalWidth); outs() << "[=offset]"; } +}; + +template <> class parser<BoolOption> final : public basic_parser<BoolOption> { +public: + parser(Option &O) : basic_parser(O) {} + + /// Return true on error. + bool parse(Option &O, StringRef ArgName, StringRef Arg, BoolOption &Val) { + if (Arg != "") + return O.error("this is a flag and does not take a value"); + Val.Val = 0; + Val.HasValue = false; + Val.IsRequested = true; + return false; + } + + enum ValueExpected getValueExpectedFlagDefault() const { + return ValueOptional; + } - // An out-of-line virtual method to provide a 'home' for this class. - void anchor() override {}; + StringRef getValueName() const override { return StringRef(); } + + void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, + size_t GlobalWidth) const { + printOptionName(O, GlobalWidth); + } }; -} // cl -} // llvm +} // namespace cl +} // namespace llvm /// @} /// Command line options. @@ -110,10 +136,10 @@ static alias DumpAllAlias("a", desc("Alias for -all"), aliasopt(DumpAll)); static unsigned DumpType = DIDT_Null; static std::array<llvm::Optional<uint64_t>, (unsigned)DIDT_ID_Count> DumpOffsets; -#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME) \ - static opt<OffsetOption> Dump##ENUM_NAME( \ - CMDLINE_NAME, desc("Dump the " ELF_NAME " section"), \ - cat(SectionCategory)); +#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ + static opt<OPTION> Dump##ENUM_NAME(CMDLINE_NAME, \ + desc("Dump the " ELF_NAME " section"), \ + cat(SectionCategory)); #include "llvm/BinaryFormat/Dwarf.def" #undef HANDLE_DWARF_SECTION @@ -208,6 +234,11 @@ static cl::opt<bool> Statistics("statistics", cl::desc("Emit JSON-formatted debug info quality metrics."), cat(DwarfDumpCategory)); +static cl::opt<bool> + ShowSectionSizes("show-section-sizes", + cl::desc("Show the sizes of all debug sections, " + "expressed in bytes."), + cat(DwarfDumpCategory)); static opt<bool> Verify("verify", desc("Verify the DWARF debug info."), cat(DwarfDumpCategory)); static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."), @@ -233,7 +264,7 @@ static void error(StringRef Prefix, std::error_code EC) { exit(1); } -static DIDumpOptions getDumpOpts() { +static DIDumpOptions getDumpOpts(DWARFContext &C) { DIDumpOptions DumpOpts; DumpOpts.DumpType = DumpType; DumpOpts.ChildRecurseDepth = ChildRecurseDepth; @@ -244,6 +275,7 @@ static DIDumpOptions getDumpOpts() { DumpOpts.ShowForm = ShowForm; DumpOpts.SummarizeTypes = SummarizeTypes; DumpOpts.Verbose = Verbose; + DumpOpts.RecoverableErrorHandler = C.getRecoverableErrorHandler(); // In -verify mode, print DIEs without children in error messages. if (Verify) return DumpOpts.noImplicitRecursion(); @@ -278,12 +310,13 @@ static bool filterArch(ObjectFile &Obj) { return false; } -using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, Twine, - raw_ostream &)>; +using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, + const Twine &, raw_ostream &)>; /// Print only DIEs that have a certain name. static bool filterByName(const StringSet<> &Names, DWARFDie Die, StringRef NameRef, raw_ostream &OS) { + DIDumpOptions DumpOpts = getDumpOpts(Die.getDwarfUnit()->getContext()); std::string Name = (IgnoreCase && !UseRegex) ? NameRef.lower() : NameRef.str(); if (UseRegex) { @@ -296,13 +329,13 @@ static bool filterByName(const StringSet<> &Names, DWARFDie Die, exit(1); } if (RE.match(Name)) { - Die.dump(OS, 0, getDumpOpts()); + Die.dump(OS, 0, DumpOpts); return true; } } } else if (Names.count(Name)) { // Match full text. - Die.dump(OS, 0, getDumpOpts()); + Die.dump(OS, 0, DumpOpts); return true; } return false; @@ -375,8 +408,9 @@ static void filterByAccelName(ArrayRef<std::string> Names, DWARFContext &DICtx, llvm::sort(Dies); Dies.erase(std::unique(Dies.begin(), Dies.end()), Dies.end()); + DIDumpOptions DumpOpts = getDumpOpts(DICtx); for (DWARFDie Die : Dies) - Die.dump(OS, 0, getDumpOpts()); + Die.dump(OS, 0, DumpOpts); } /// Handle the --lookup option and dump the DIEs and line info for the given @@ -392,7 +426,7 @@ static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address, if (!DIEsForAddr) return false; - DIDumpOptions DumpOpts = getDumpOpts(); + DIDumpOptions DumpOpts = getDumpOpts(DICtx); DumpOpts.ChildRecurseDepth = 0; DIEsForAddr.CompileUnit->dump(OS, DumpOpts); if (DIEsForAddr.FunctionDIE) { @@ -410,11 +444,8 @@ static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address, return true; } -bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, - Twine Filename, raw_ostream &OS); - -static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename, - raw_ostream &OS) { +static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, + const Twine &Filename, raw_ostream &OS) { logAllUnhandledErrors(DICtx.loadRegisterInfo(Obj), errs(), Filename.str() + ": "); // The UUID dump already contains all the same information. @@ -443,18 +474,18 @@ static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename, } // Dump the complete DWARF structure. - DICtx.dump(OS, getDumpOpts(), DumpOffsets); + DICtx.dump(OS, getDumpOpts(DICtx), DumpOffsets); return true; } static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx, - Twine Filename, raw_ostream &OS) { + const Twine &Filename, raw_ostream &OS) { // Verify the DWARF and exit with non-zero exit status if verification // fails. raw_ostream &stream = Quiet ? nulls() : OS; stream << "Verifying " << Filename.str() << ":\tfile format " << Obj.getFileFormatName() << "\n"; - bool Result = DICtx.verify(stream, getDumpOpts()); + bool Result = DICtx.verify(stream, getDumpOpts(DICtx)); if (Result) stream << "No errors.\n"; else @@ -488,10 +519,16 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, error(Filename, errorToErrorCode(BinOrErr.takeError())); bool Result = true; + auto RecoverableErrorHandler = [&](Error E) { + Result = false; + WithColor::defaultErrorHandler(std::move(E)); + }; if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) { if (filterArch(*Obj)) { - std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj); - Result = HandleObj(*Obj, *DICtx, Filename, OS); + std::unique_ptr<DWARFContext> DICtx = + DWARFContext::create(*Obj, nullptr, "", RecoverableErrorHandler); + if (!HandleObj(*Obj, *DICtx, Filename, OS)) + Result = false; } } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) @@ -501,15 +538,18 @@ static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, if (auto MachOOrErr = ObjForArch.getAsObjectFile()) { auto &Obj = **MachOOrErr; if (filterArch(Obj)) { - std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(Obj); - Result &= HandleObj(Obj, *DICtx, ObjName, OS); + std::unique_ptr<DWARFContext> DICtx = + DWARFContext::create(Obj, nullptr, "", RecoverableErrorHandler); + if (!HandleObj(Obj, *DICtx, ObjName, OS)) + Result = false; } continue; } else consumeError(MachOOrErr.takeError()); if (auto ArchiveOrErr = ObjForArch.getAsArchive()) { error(ObjName, errorToErrorCode(ArchiveOrErr.takeError())); - Result &= handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS); + if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS)) + Result = false; continue; } else consumeError(ArchiveOrErr.takeError()); @@ -566,6 +606,10 @@ static std::vector<std::string> expandBundle(const std::string &InputPath) { int main(int argc, char **argv) { InitLLVM X(argc, argv); + // Flush outs() when printing to errs(). This avoids interleaving output + // between the two. + errs().tie(&outs()); + llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); @@ -593,7 +637,7 @@ int main(int argc, char **argv) { // Defaults to dumping all sections, unless brief mode is specified in which // case only the .debug_info section in dumped. -#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME) \ +#define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ if (Dump##ENUM_NAME.IsRequested) { \ DumpType |= DIDT_##ENUM_NAME; \ if (Dump##ENUM_NAME.HasValue) { \ @@ -629,18 +673,20 @@ int main(int argc, char **argv) { Objects.insert(Objects.end(), Objs.begin(), Objs.end()); } + bool Success = true; 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, OutputFile.os()); - })) - return 1; - } else if (Statistics) for (auto Object : Objects) - handleFile(Object, collectStatsForObjectFile, OutputFile.os()); - else + Success &= handleFile(Object, verifyObjectFile, OutputFile.os()); + } else if (Statistics) { for (auto Object : Objects) - handleFile(Object, dumpObjectFile, OutputFile.os()); + Success &= handleFile(Object, collectStatsForObjectFile, OutputFile.os()); + } else if (ShowSectionSizes) { + for (auto Object : Objects) + Success &= handleFile(Object, collectObjectSectionSizes, OutputFile.os()); + } else { + for (auto Object : Objects) + Success &= handleFile(Object, dumpObjectFile, OutputFile.os()); + } - return EXIT_SUCCESS; + return Success ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h new file mode 100644 index 0000000000000..dc41298265d2a --- /dev/null +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.h @@ -0,0 +1,43 @@ +//===-- llvm-dwarfdump - Debug info 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H +#define LLVM_TOOLS_LLVM_DWARFDUMP_LLVM_DWARFDUMP_H + +#include "llvm/ADT/Twine.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace dwarfdump { + +/// Holds cumulative section sizes for an object file. +struct SectionSizes { + /// Map of .debug section names and their sizes across all such-named + /// sections. + StringMap<uint64_t> DebugSectionSizes; + /// Total number of bytes of all sections. + uint64_t TotalObjectSize = 0; + /// Total number of bytes of all debug sections. + uint64_t TotalDebugSectionsSize = 0; +}; + +/// Calculate the section sizes. +void calculateSectionSizes(const object::ObjectFile &Obj, SectionSizes &Sizes, + const Twine &Filename); + +bool collectStatsForObjectFile(object::ObjectFile &Obj, DWARFContext &DICtx, + const Twine &Filename, raw_ostream &OS); +bool collectObjectSectionSizes(object::ObjectFile &Obj, DWARFContext &DICtx, + const Twine &Filename, raw_ostream &OS); + +} // namespace dwarfdump +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-dwp/DWPError.cpp b/llvm/tools/llvm-dwp/DWPError.cpp new file mode 100644 index 0000000000000..21d53ed6d198c --- /dev/null +++ b/llvm/tools/llvm-dwp/DWPError.cpp @@ -0,0 +1,3 @@ +#include "DWPError.h" +using namespace llvm; +char DWPError::ID; diff --git a/llvm/tools/llvm-dwp/DWPError.h b/llvm/tools/llvm-dwp/DWPError.h new file mode 100644 index 0000000000000..62025ed4caa55 --- /dev/null +++ b/llvm/tools/llvm-dwp/DWPError.h @@ -0,0 +1,23 @@ +#ifndef TOOLS_LLVM_DWP_DWPERROR +#define TOOLS_LLVM_DWP_DWPERROR + +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include <string> + +namespace llvm { +class DWPError : public ErrorInfo<DWPError> { +public: + DWPError(std::string Info) : Info(std::move(Info)) {} + void log(raw_ostream &OS) const override { OS << Info; } + std::error_code convertToErrorCode() const override { + llvm_unreachable("Not implemented"); + } + static char ID; + +private: + std::string Info; +}; +} + +#endif diff --git a/llvm/tools/llvm-dwp/DWPStringPool.h b/llvm/tools/llvm-dwp/DWPStringPool.h new file mode 100644 index 0000000000000..e423076f43333 --- /dev/null +++ b/llvm/tools/llvm-dwp/DWPStringPool.h @@ -0,0 +1,56 @@ +#ifndef TOOLS_LLVM_DWP_DWPSTRINGPOOL +#define TOOLS_LLVM_DWP_DWPSTRINGPOOL + +#include "llvm/ADT/DenseMap.h" +#include "llvm/MC/MCSection.h" +#include "llvm/MC/MCStreamer.h" +#include <cassert> + +namespace llvm { +class DWPStringPool { + + struct CStrDenseMapInfo { + static inline const char *getEmptyKey() { + return reinterpret_cast<const char *>(~static_cast<uintptr_t>(0)); + } + static inline const char *getTombstoneKey() { + return reinterpret_cast<const char *>(~static_cast<uintptr_t>(1)); + } + static unsigned getHashValue(const char *Val) { + assert(Val != getEmptyKey() && "Cannot hash the empty key!"); + assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!"); + return (unsigned)hash_value(StringRef(Val)); + } + static bool isEqual(const char *LHS, const char *RHS) { + if (RHS == getEmptyKey()) + return LHS == getEmptyKey(); + if (RHS == getTombstoneKey()) + return LHS == getTombstoneKey(); + return strcmp(LHS, RHS) == 0; + } + }; + + MCStreamer &Out; + MCSection *Sec; + DenseMap<const char *, uint32_t, CStrDenseMapInfo> Pool; + uint32_t Offset = 0; + +public: + DWPStringPool(MCStreamer &Out, MCSection *Sec) : Out(Out), Sec(Sec) {} + + uint32_t getOffset(const char *Str, unsigned Length) { + assert(strlen(Str) + 1 == Length && "Ensure length hint is correct"); + + auto Pair = Pool.insert(std::make_pair(Str, Offset)); + if (Pair.second) { + Out.SwitchSection(Sec); + Out.emitBytes(StringRef(Str, Length)); + Offset += Length; + } + + return Pair.first->second; + } +}; +} + +#endif diff --git a/llvm/tools/llvm-dwp/llvm-dwp.cpp b/llvm/tools/llvm-dwp/llvm-dwp.cpp new file mode 100644 index 0000000000000..d5ebe5ab0a57a --- /dev/null +++ b/llvm/tools/llvm-dwp/llvm-dwp.cpp @@ -0,0 +1,795 @@ +//===-- llvm-dwp.cpp - Split DWARF merging tool for llvm ------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// A utility for merging DWARF 5 Split DWARF .dwo files into .dwp (DWARF +// package files). +// +//===----------------------------------------------------------------------===// +#include "DWPError.h" +#include "DWPStringPool.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/Object/Decompressor.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::object; + +static mc::RegisterMCTargetOptionsFlags MCTargetOptionsFlags; + +cl::OptionCategory DwpCategory("Specific Options"); +static cl::list<std::string> InputFiles(cl::Positional, cl::ZeroOrMore, + cl::desc("<input files>"), + cl::cat(DwpCategory)); + +static cl::list<std::string> ExecFilenames( + "e", cl::ZeroOrMore, + cl::desc("Specify the executable/library files to get the list of *.dwo from"), + cl::value_desc("filename"), cl::cat(DwpCategory)); + +static cl::opt<std::string> OutputFilename(cl::Required, "o", + cl::desc("Specify the output file."), + cl::value_desc("filename"), + cl::cat(DwpCategory)); + +static void writeStringsAndOffsets(MCStreamer &Out, DWPStringPool &Strings, + MCSection *StrOffsetSection, + StringRef CurStrSection, + StringRef CurStrOffsetSection) { + // Could possibly produce an error or warning if one of these was non-null but + // the other was null. + if (CurStrSection.empty() || CurStrOffsetSection.empty()) + return; + + DenseMap<uint64_t, uint32_t> OffsetRemapping; + + DataExtractor Data(CurStrSection, true, 0); + uint64_t LocalOffset = 0; + uint64_t PrevOffset = 0; + while (const char *s = Data.getCStr(&LocalOffset)) { + OffsetRemapping[PrevOffset] = + Strings.getOffset(s, LocalOffset - PrevOffset); + PrevOffset = LocalOffset; + } + + Data = DataExtractor(CurStrOffsetSection, true, 0); + + Out.SwitchSection(StrOffsetSection); + + uint64_t Offset = 0; + uint64_t Size = CurStrOffsetSection.size(); + while (Offset < Size) { + auto OldOffset = Data.getU32(&Offset); + auto NewOffset = OffsetRemapping[OldOffset]; + Out.emitIntValue(NewOffset, 4); + } +} + +static uint64_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) { + uint64_t CurCode; + uint64_t Offset = 0; + DataExtractor AbbrevData(Abbrev, true, 0); + while ((CurCode = AbbrevData.getULEB128(&Offset)) != AbbrCode) { + // Tag + AbbrevData.getULEB128(&Offset); + // DW_CHILDREN + AbbrevData.getU8(&Offset); + // Attributes + while (AbbrevData.getULEB128(&Offset) | AbbrevData.getULEB128(&Offset)) + ; + } + return Offset; +} + +struct CompileUnitIdentifiers { + uint64_t Signature = 0; + const char *Name = ""; + const char *DWOName = ""; +}; + +static Expected<const char *> +getIndexedString(dwarf::Form Form, DataExtractor InfoData, + uint64_t &InfoOffset, StringRef StrOffsets, StringRef Str) { + if (Form == dwarf::DW_FORM_string) + return InfoData.getCStr(&InfoOffset); + if (Form != dwarf::DW_FORM_GNU_str_index) + return make_error<DWPError>( + "string field encoded without DW_FORM_string or DW_FORM_GNU_str_index"); + auto StrIndex = InfoData.getULEB128(&InfoOffset); + DataExtractor StrOffsetsData(StrOffsets, true, 0); + uint64_t StrOffsetsOffset = 4 * StrIndex; + uint64_t StrOffset = StrOffsetsData.getU32(&StrOffsetsOffset); + DataExtractor StrData(Str, true, 0); + return StrData.getCStr(&StrOffset); +} + +static Expected<CompileUnitIdentifiers> getCUIdentifiers(StringRef Abbrev, + StringRef Info, + StringRef StrOffsets, + StringRef Str) { + uint64_t Offset = 0; + DataExtractor InfoData(Info, true, 0); + dwarf::DwarfFormat Format = dwarf::DwarfFormat::DWARF32; + uint64_t Length = InfoData.getU32(&Offset); + CompileUnitIdentifiers ID; + Optional<uint64_t> Signature = None; + // If the length is 0xffffffff, then this indictes that this is a DWARF 64 + // stream and the length is actually encoded into a 64 bit value that follows. + if (Length == 0xffffffffU) { + Format = dwarf::DwarfFormat::DWARF64; + Length = InfoData.getU64(&Offset); + } + uint16_t Version = InfoData.getU16(&Offset); + if (Version >= 5) { + auto UnitType = InfoData.getU8(&Offset); + if (UnitType != dwarf::DW_UT_split_compile) + return make_error<DWPError>( + std::string("unit type DW_UT_split_compile type not found in " + "debug_info header. Unexpected unit type 0x" + + utostr(UnitType) + " found")); + } + InfoData.getU32(&Offset); // Abbrev offset (should be zero) + uint8_t AddrSize = InfoData.getU8(&Offset); + if (Version >= 5) + Signature = InfoData.getU64(&Offset); + uint32_t AbbrCode = InfoData.getULEB128(&Offset); + + DataExtractor AbbrevData(Abbrev, true, 0); + uint64_t AbbrevOffset = getCUAbbrev(Abbrev, AbbrCode); + auto Tag = static_cast<dwarf::Tag>(AbbrevData.getULEB128(&AbbrevOffset)); + if (Tag != dwarf::DW_TAG_compile_unit) + return make_error<DWPError>("top level DIE is not a compile unit"); + // DW_CHILDREN + AbbrevData.getU8(&AbbrevOffset); + uint32_t Name; + dwarf::Form Form; + while ((Name = AbbrevData.getULEB128(&AbbrevOffset)) | + (Form = static_cast<dwarf::Form>(AbbrevData.getULEB128(&AbbrevOffset))) && + (Name != 0 || Form != 0)) { + switch (Name) { + case dwarf::DW_AT_name: { + Expected<const char *> EName = + getIndexedString(Form, InfoData, Offset, StrOffsets, Str); + if (!EName) + return EName.takeError(); + ID.Name = *EName; + break; + } + case dwarf::DW_AT_GNU_dwo_name: + case dwarf::DW_AT_dwo_name: { + Expected<const char *> EName = + getIndexedString(Form, InfoData, Offset, StrOffsets, Str); + if (!EName) + return EName.takeError(); + ID.DWOName = *EName; + break; + } + case dwarf::DW_AT_GNU_dwo_id: + Signature = InfoData.getU64(&Offset); + break; + default: + DWARFFormValue::skipValue(Form, InfoData, &Offset, + dwarf::FormParams({Version, AddrSize, Format})); + } + } + if (!Signature) + return make_error<DWPError>("compile unit missing dwo_id"); + ID.Signature = *Signature; + return ID; +} + +struct UnitIndexEntry { + DWARFUnitIndex::Entry::SectionContribution Contributions[8]; + std::string Name; + std::string DWOName; + StringRef DWPName; +}; + +static bool isSupportedSectionKind(DWARFSectionKind Kind) { + return Kind != DW_SECT_EXT_unknown; +} + +// Convert an internal section identifier into the index to use with +// UnitIndexEntry::Contributions. +static unsigned getContributionIndex(DWARFSectionKind Kind) { + // Assuming the pre-standard DWP format. + assert(serializeSectionKind(Kind, 2) >= DW_SECT_INFO); + return serializeSectionKind(Kind, 2) - DW_SECT_INFO; +} + +// Convert a UnitIndexEntry::Contributions index to the corresponding on-disk +// value of the section identifier. +static unsigned getOnDiskSectionId(unsigned Index) { + return Index + DW_SECT_INFO; +} + +static StringRef getSubsection(StringRef Section, + const DWARFUnitIndex::Entry &Entry, + DWARFSectionKind Kind) { + const auto *Off = Entry.getContribution(Kind); + if (!Off) + return StringRef(); + return Section.substr(Off->Offset, Off->Length); +} + +static void addAllTypesFromDWP( + MCStreamer &Out, MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries, + const DWARFUnitIndex &TUIndex, MCSection *OutputTypes, StringRef Types, + const UnitIndexEntry &TUEntry, uint32_t &TypesOffset) { + Out.SwitchSection(OutputTypes); + for (const DWARFUnitIndex::Entry &E : TUIndex.getRows()) { + auto *I = E.getContributions(); + if (!I) + continue; + auto P = TypeIndexEntries.insert(std::make_pair(E.getSignature(), TUEntry)); + if (!P.second) + continue; + auto &Entry = P.first->second; + // Zero out the debug_info contribution + Entry.Contributions[0] = {}; + for (auto Kind : TUIndex.getColumnKinds()) { + if (!isSupportedSectionKind(Kind)) + continue; + auto &C = Entry.Contributions[getContributionIndex(Kind)]; + C.Offset += I->Offset; + C.Length = I->Length; + ++I; + } + unsigned TypesIndex = getContributionIndex(DW_SECT_EXT_TYPES); + auto &C = Entry.Contributions[TypesIndex]; + Out.emitBytes(Types.substr( + C.Offset - TUEntry.Contributions[TypesIndex].Offset, C.Length)); + C.Offset = TypesOffset; + TypesOffset += C.Length; + } +} + +static void addAllTypes(MCStreamer &Out, + MapVector<uint64_t, UnitIndexEntry> &TypeIndexEntries, + MCSection *OutputTypes, + const std::vector<StringRef> &TypesSections, + const UnitIndexEntry &CUEntry, uint32_t &TypesOffset) { + for (StringRef Types : TypesSections) { + Out.SwitchSection(OutputTypes); + uint64_t Offset = 0; + DataExtractor Data(Types, true, 0); + while (Data.isValidOffset(Offset)) { + UnitIndexEntry Entry = CUEntry; + // Zero out the debug_info contribution + Entry.Contributions[0] = {}; + auto &C = Entry.Contributions[getContributionIndex(DW_SECT_EXT_TYPES)]; + C.Offset = TypesOffset; + auto PrevOffset = Offset; + // Length of the unit, including the 4 byte length field. + C.Length = Data.getU32(&Offset) + 4; + + Data.getU16(&Offset); // Version + Data.getU32(&Offset); // Abbrev offset + Data.getU8(&Offset); // Address size + auto Signature = Data.getU64(&Offset); + Offset = PrevOffset + C.Length; + + auto P = TypeIndexEntries.insert(std::make_pair(Signature, Entry)); + if (!P.second) + continue; + + Out.emitBytes(Types.substr(PrevOffset, C.Length)); + TypesOffset += C.Length; + } + } +} + +static void +writeIndexTable(MCStreamer &Out, ArrayRef<unsigned> ContributionOffsets, + const MapVector<uint64_t, UnitIndexEntry> &IndexEntries, + uint32_t DWARFUnitIndex::Entry::SectionContribution::*Field) { + for (const auto &E : IndexEntries) + for (size_t i = 0; i != array_lengthof(E.second.Contributions); ++i) + if (ContributionOffsets[i]) + Out.emitIntValue(E.second.Contributions[i].*Field, 4); +} + +static void +writeIndex(MCStreamer &Out, MCSection *Section, + ArrayRef<unsigned> ContributionOffsets, + const MapVector<uint64_t, UnitIndexEntry> &IndexEntries) { + if (IndexEntries.empty()) + return; + + unsigned Columns = 0; + for (auto &C : ContributionOffsets) + if (C) + ++Columns; + + std::vector<unsigned> Buckets(NextPowerOf2(3 * IndexEntries.size() / 2)); + uint64_t Mask = Buckets.size() - 1; + size_t i = 0; + for (const auto &P : IndexEntries) { + auto S = P.first; + auto H = S & Mask; + auto HP = ((S >> 32) & Mask) | 1; + while (Buckets[H]) { + assert(S != IndexEntries.begin()[Buckets[H] - 1].first && + "Duplicate unit"); + H = (H + HP) & Mask; + } + Buckets[H] = i + 1; + ++i; + } + + Out.SwitchSection(Section); + Out.emitIntValue(2, 4); // Version + Out.emitIntValue(Columns, 4); // Columns + Out.emitIntValue(IndexEntries.size(), 4); // Num Units + Out.emitIntValue(Buckets.size(), 4); // Num Buckets + + // Write the signatures. + for (const auto &I : Buckets) + Out.emitIntValue(I ? IndexEntries.begin()[I - 1].first : 0, 8); + + // Write the indexes. + for (const auto &I : Buckets) + Out.emitIntValue(I, 4); + + // Write the column headers (which sections will appear in the table) + for (size_t i = 0; i != ContributionOffsets.size(); ++i) + if (ContributionOffsets[i]) + Out.emitIntValue(getOnDiskSectionId(i), 4); + + // Write the offsets. + writeIndexTable(Out, ContributionOffsets, IndexEntries, + &DWARFUnitIndex::Entry::SectionContribution::Offset); + + // Write the lengths. + writeIndexTable(Out, ContributionOffsets, IndexEntries, + &DWARFUnitIndex::Entry::SectionContribution::Length); +} + +std::string buildDWODescription(StringRef Name, StringRef DWPName, StringRef DWOName) { + std::string Text = "\'"; + Text += Name; + Text += '\''; + if (!DWPName.empty()) { + Text += " (from "; + if (!DWOName.empty()) { + Text += '\''; + Text += DWOName; + Text += "' in "; + } + Text += '\''; + Text += DWPName; + Text += "')"; + } + return Text; +} + +static Error createError(StringRef Name, Error E) { + return make_error<DWPError>( + ("failure while decompressing compressed section: '" + Name + "', " + + llvm::toString(std::move(E))) + .str()); +} + +static Error +handleCompressedSection(std::deque<SmallString<32>> &UncompressedSections, + StringRef &Name, StringRef &Contents) { + if (!Decompressor::isGnuStyle(Name)) + return Error::success(); + + Expected<Decompressor> Dec = + Decompressor::create(Name, Contents, false /*IsLE*/, false /*Is64Bit*/); + if (!Dec) + return createError(Name, Dec.takeError()); + + UncompressedSections.emplace_back(); + if (Error E = Dec->resizeAndDecompress(UncompressedSections.back())) + return createError(Name, std::move(E)); + + Name = Name.substr(2); // Drop ".z" + Contents = UncompressedSections.back(); + return Error::success(); +} + +static Error handleSection( + const StringMap<std::pair<MCSection *, DWARFSectionKind>> &KnownSections, + const MCSection *StrSection, const MCSection *StrOffsetSection, + const MCSection *TypesSection, const MCSection *CUIndexSection, + const MCSection *TUIndexSection, const SectionRef &Section, MCStreamer &Out, + std::deque<SmallString<32>> &UncompressedSections, + uint32_t (&ContributionOffsets)[8], UnitIndexEntry &CurEntry, + StringRef &CurStrSection, StringRef &CurStrOffsetSection, + std::vector<StringRef> &CurTypesSection, StringRef &InfoSection, + StringRef &AbbrevSection, StringRef &CurCUIndexSection, + StringRef &CurTUIndexSection) { + if (Section.isBSS()) + return Error::success(); + + if (Section.isVirtual()) + return Error::success(); + + Expected<StringRef> NameOrErr = Section.getName(); + if (!NameOrErr) + return NameOrErr.takeError(); + StringRef Name = *NameOrErr; + + Expected<StringRef> ContentsOrErr = Section.getContents(); + if (!ContentsOrErr) + return ContentsOrErr.takeError(); + StringRef Contents = *ContentsOrErr; + + if (auto Err = handleCompressedSection(UncompressedSections, Name, Contents)) + return Err; + + Name = Name.substr(Name.find_first_not_of("._")); + + auto SectionPair = KnownSections.find(Name); + if (SectionPair == KnownSections.end()) + return Error::success(); + + if (DWARFSectionKind Kind = SectionPair->second.second) { + auto Index = getContributionIndex(Kind); + if (Kind != DW_SECT_EXT_TYPES) { + CurEntry.Contributions[Index].Offset = ContributionOffsets[Index]; + ContributionOffsets[Index] += + (CurEntry.Contributions[Index].Length = Contents.size()); + } + + switch (Kind) { + case DW_SECT_INFO: + InfoSection = Contents; + break; + case DW_SECT_ABBREV: + AbbrevSection = Contents; + break; + default: + break; + } + } + + MCSection *OutSection = SectionPair->second.first; + if (OutSection == StrOffsetSection) + CurStrOffsetSection = Contents; + else if (OutSection == StrSection) + CurStrSection = Contents; + else if (OutSection == TypesSection) + CurTypesSection.push_back(Contents); + else if (OutSection == CUIndexSection) + CurCUIndexSection = Contents; + else if (OutSection == TUIndexSection) + CurTUIndexSection = Contents; + else { + Out.SwitchSection(OutSection); + Out.emitBytes(Contents); + } + return Error::success(); +} + +static Error +buildDuplicateError(const std::pair<uint64_t, UnitIndexEntry> &PrevE, + const CompileUnitIdentifiers &ID, StringRef DWPName) { + return make_error<DWPError>( + std::string("duplicate DWO ID (") + utohexstr(PrevE.first) + ") in " + + buildDWODescription(PrevE.second.Name, PrevE.second.DWPName, + PrevE.second.DWOName) + + " and " + buildDWODescription(ID.Name, DWPName, ID.DWOName)); +} + +static Expected<SmallVector<std::string, 16>> +getDWOFilenames(StringRef ExecFilename) { + auto ErrOrObj = object::ObjectFile::createObjectFile(ExecFilename); + if (!ErrOrObj) + return ErrOrObj.takeError(); + + const ObjectFile &Obj = *ErrOrObj.get().getBinary(); + std::unique_ptr<DWARFContext> DWARFCtx = DWARFContext::create(Obj); + + SmallVector<std::string, 16> DWOPaths; + for (const auto &CU : DWARFCtx->compile_units()) { + const DWARFDie &Die = CU->getUnitDIE(); + std::string DWOName = dwarf::toString( + Die.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); + if (DWOName.empty()) + continue; + std::string DWOCompDir = + dwarf::toString(Die.find(dwarf::DW_AT_comp_dir), ""); + if (!DWOCompDir.empty()) { + SmallString<16> DWOPath; + sys::path::append(DWOPath, DWOCompDir, DWOName); + DWOPaths.emplace_back(DWOPath.data(), DWOPath.size()); + } else { + DWOPaths.push_back(std::move(DWOName)); + } + } + return std::move(DWOPaths); +} + +static Error write(MCStreamer &Out, ArrayRef<std::string> Inputs) { + const auto &MCOFI = *Out.getContext().getObjectFileInfo(); + MCSection *const StrSection = MCOFI.getDwarfStrDWOSection(); + MCSection *const StrOffsetSection = MCOFI.getDwarfStrOffDWOSection(); + MCSection *const TypesSection = MCOFI.getDwarfTypesDWOSection(); + MCSection *const CUIndexSection = MCOFI.getDwarfCUIndexSection(); + MCSection *const TUIndexSection = MCOFI.getDwarfTUIndexSection(); + const StringMap<std::pair<MCSection *, DWARFSectionKind>> KnownSections = { + {"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}}, + {"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_EXT_TYPES}}, + {"debug_str_offsets.dwo", {StrOffsetSection, DW_SECT_STR_OFFSETS}}, + {"debug_str.dwo", {StrSection, static_cast<DWARFSectionKind>(0)}}, + {"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_EXT_LOC}}, + {"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}}, + {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}}, + {"debug_cu_index", {CUIndexSection, static_cast<DWARFSectionKind>(0)}}, + {"debug_tu_index", {TUIndexSection, static_cast<DWARFSectionKind>(0)}}}; + + MapVector<uint64_t, UnitIndexEntry> IndexEntries; + MapVector<uint64_t, UnitIndexEntry> TypeIndexEntries; + + uint32_t ContributionOffsets[8] = {}; + + DWPStringPool Strings(Out, StrSection); + + SmallVector<OwningBinary<object::ObjectFile>, 128> Objects; + Objects.reserve(Inputs.size()); + + std::deque<SmallString<32>> UncompressedSections; + + for (const auto &Input : Inputs) { + auto ErrOrObj = object::ObjectFile::createObjectFile(Input); + if (!ErrOrObj) + return ErrOrObj.takeError(); + + auto &Obj = *ErrOrObj->getBinary(); + Objects.push_back(std::move(*ErrOrObj)); + + UnitIndexEntry CurEntry = {}; + + StringRef CurStrSection; + StringRef CurStrOffsetSection; + std::vector<StringRef> CurTypesSection; + StringRef InfoSection; + StringRef AbbrevSection; + StringRef CurCUIndexSection; + StringRef CurTUIndexSection; + + for (const auto &Section : Obj.sections()) + if (auto Err = handleSection( + KnownSections, StrSection, StrOffsetSection, TypesSection, + CUIndexSection, TUIndexSection, Section, Out, + UncompressedSections, ContributionOffsets, CurEntry, + CurStrSection, CurStrOffsetSection, CurTypesSection, InfoSection, + AbbrevSection, CurCUIndexSection, CurTUIndexSection)) + return Err; + + if (InfoSection.empty()) + continue; + + writeStringsAndOffsets(Out, Strings, StrOffsetSection, CurStrSection, + CurStrOffsetSection); + + if (CurCUIndexSection.empty()) { + Expected<CompileUnitIdentifiers> EID = getCUIdentifiers( + AbbrevSection, InfoSection, CurStrOffsetSection, CurStrSection); + if (!EID) + return createFileError(Input, EID.takeError()); + const auto &ID = *EID; + auto P = IndexEntries.insert(std::make_pair(ID.Signature, CurEntry)); + if (!P.second) + return buildDuplicateError(*P.first, ID, ""); + P.first->second.Name = ID.Name; + P.first->second.DWOName = ID.DWOName; + addAllTypes(Out, TypeIndexEntries, TypesSection, CurTypesSection, + CurEntry, + ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)]); + continue; + } + + DWARFUnitIndex CUIndex(DW_SECT_INFO); + DataExtractor CUIndexData(CurCUIndexSection, Obj.isLittleEndian(), 0); + if (!CUIndex.parse(CUIndexData)) + return make_error<DWPError>("failed to parse cu_index"); + if (CUIndex.getVersion() != 2) + return make_error<DWPError>( + "unsupported cu_index version: " + utostr(CUIndex.getVersion()) + + " (only version 2 is supported)"); + + for (const DWARFUnitIndex::Entry &E : CUIndex.getRows()) { + auto *I = E.getContributions(); + if (!I) + continue; + auto P = IndexEntries.insert(std::make_pair(E.getSignature(), CurEntry)); + Expected<CompileUnitIdentifiers> EID = getCUIdentifiers( + getSubsection(AbbrevSection, E, DW_SECT_ABBREV), + getSubsection(InfoSection, E, DW_SECT_INFO), + getSubsection(CurStrOffsetSection, E, DW_SECT_STR_OFFSETS), + CurStrSection); + if (!EID) + return createFileError(Input, EID.takeError()); + const auto &ID = *EID; + if (!P.second) + return buildDuplicateError(*P.first, ID, Input); + auto &NewEntry = P.first->second; + NewEntry.Name = ID.Name; + NewEntry.DWOName = ID.DWOName; + NewEntry.DWPName = Input; + for (auto Kind : CUIndex.getColumnKinds()) { + if (!isSupportedSectionKind(Kind)) + continue; + auto &C = NewEntry.Contributions[getContributionIndex(Kind)]; + C.Offset += I->Offset; + C.Length = I->Length; + ++I; + } + } + + if (!CurTypesSection.empty()) { + if (CurTypesSection.size() != 1) + return make_error<DWPError>("multiple type unit sections in .dwp file"); + DWARFUnitIndex TUIndex(DW_SECT_EXT_TYPES); + DataExtractor TUIndexData(CurTUIndexSection, Obj.isLittleEndian(), 0); + if (!TUIndex.parse(TUIndexData)) + return make_error<DWPError>("failed to parse tu_index"); + if (TUIndex.getVersion() != 2) + return make_error<DWPError>( + "unsupported tu_index version: " + utostr(TUIndex.getVersion()) + + " (only version 2 is supported)"); + + addAllTypesFromDWP( + Out, TypeIndexEntries, TUIndex, TypesSection, CurTypesSection.front(), + CurEntry, + ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)]); + } + } + + // Lie about there being no info contributions so the TU index only includes + // the type unit contribution + ContributionOffsets[0] = 0; + writeIndex(Out, MCOFI.getDwarfTUIndexSection(), ContributionOffsets, + TypeIndexEntries); + + // Lie about the type contribution + ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES)] = 0; + // Unlie about the info contribution + ContributionOffsets[0] = 1; + + writeIndex(Out, MCOFI.getDwarfCUIndexSection(), ContributionOffsets, + IndexEntries); + + return Error::success(); +} + +static int error(const Twine &Error, const Twine &Context) { + errs() << Twine("while processing ") + Context + ":\n"; + errs() << Twine("error: ") + Error + "\n"; + return 1; +} + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files\n"); + + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + + std::string ErrorStr; + StringRef Context = "dwarf streamer init"; + + Triple TheTriple("x86_64-linux-gnu"); + + // Get the target. + const Target *TheTarget = + TargetRegistry::lookupTarget("", TheTriple, ErrorStr); + if (!TheTarget) + return error(ErrorStr, Context); + std::string TripleName = TheTriple.getTriple(); + + // Create all the MC Objects. + std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); + if (!MRI) + return error(Twine("no register info for target ") + TripleName, Context); + + MCTargetOptions MCOptions = llvm::mc::InitMCTargetOptionsFromFlags(); + std::unique_ptr<MCAsmInfo> MAI( + TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); + if (!MAI) + return error("no asm info for target " + TripleName, Context); + + MCObjectFileInfo MOFI; + MCContext MC(MAI.get(), MRI.get(), &MOFI); + MOFI.InitMCObjectFileInfo(TheTriple, /*PIC*/ false, MC); + + std::unique_ptr<MCSubtargetInfo> MSTI( + TheTarget->createMCSubtargetInfo(TripleName, "", "")); + if (!MSTI) + return error("no subtarget info for target " + TripleName, Context); + + MCTargetOptions Options; + auto MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options); + if (!MAB) + return error("no asm backend for target " + TripleName, Context); + + std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo()); + if (!MII) + return error("no instr info info for target " + TripleName, Context); + + MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, MC); + if (!MCE) + return error("no code emitter for target " + TripleName, Context); + + // Create the output file. + std::error_code EC; + ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None); + Optional<buffer_ostream> BOS; + raw_pwrite_stream *OS; + if (EC) + return error(Twine(OutputFilename) + ": " + EC.message(), Context); + if (OutFile.os().supportsSeeking()) { + OS = &OutFile.os(); + } else { + BOS.emplace(OutFile.os()); + OS = BOS.getPointer(); + } + + std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer( + TheTriple, MC, std::unique_ptr<MCAsmBackend>(MAB), + MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(MCE), *MSTI, + MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ false)); + if (!MS) + return error("no object streamer for target " + TripleName, Context); + + std::vector<std::string> DWOFilenames = InputFiles; + for (const auto &ExecFilename : ExecFilenames) { + auto DWOs = getDWOFilenames(ExecFilename); + if (!DWOs) { + logAllUnhandledErrors(DWOs.takeError(), WithColor::error()); + return 1; + } + DWOFilenames.insert(DWOFilenames.end(), + std::make_move_iterator(DWOs->begin()), + std::make_move_iterator(DWOs->end())); + } + + if (auto Err = write(*MS, DWOFilenames)) { + logAllUnhandledErrors(std::move(Err), WithColor::error()); + return 1; + } + + MS->Finish(); + OutFile.keep(); + return 0; +} diff --git a/llvm/tools/llvm-extract/llvm-extract.cpp b/llvm/tools/llvm-extract/llvm-extract.cpp index dddc0d9baa089..cb1c4116ff192 100644 --- a/llvm/tools/llvm-extract/llvm-extract.cpp +++ b/llvm/tools/llvm-extract/llvm-extract.cpp @@ -31,6 +31,7 @@ #include "llvm/Support/ToolOutputFile.h" #include "llvm/Transforms/IPO.h" #include <memory> +#include <utility> using namespace llvm; cl::OptionCategory ExtractCat("llvm-extract Options"); @@ -53,6 +54,10 @@ static cl::opt<bool> DeleteFn("delete", cl::desc("Delete specified Globals from Module"), cl::cat(ExtractCat)); +static cl::opt<bool> KeepConstInit("keep-const-init", + cl::desc("Keep initializers of constants"), + cl::cat(ExtractCat)); + static cl::opt<bool> Recursive("recursive", cl::desc("Recursively extract all called functions"), cl::cat(ExtractCat)); @@ -252,8 +257,9 @@ int main(int argc, char **argv) { } // Figure out which BasicBlocks we should extract. - SmallVector<SmallVector<BasicBlock *, 16>, 4> GroupOfBBs; + SmallVector<std::pair<Function *, SmallVector<StringRef, 16>>, 2> BBMap; for (StringRef StrPair : ExtractBlocks) { + SmallVector<StringRef, 16> BBNames; auto BBInfo = StrPair.split(':'); // Get the function. Function *F = M->getFunction(BBInfo.first); @@ -262,26 +268,11 @@ int main(int argc, char **argv) { << BBInfo.first << "'!\n"; return 1; } - // Do not materialize this function. + // Add the function to the materialize list, and store the basic block names + // to check after materialization. GVs.insert(F); - // 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); - } - GroupOfBBs.push_back(BBs); + BBInfo.second.split(BBNames, ';', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + BBMap.push_back({F, std::move(BBNames)}); } // Use *argv instead of argv[0] to work around a wrong GCC warning. @@ -333,7 +324,7 @@ int main(int argc, char **argv) { { std::vector<GlobalValue *> Gvs(GVs.begin(), GVs.end()); legacy::PassManager Extract; - Extract.add(createGVExtractionPass(Gvs, DeleteFn)); + Extract.add(createGVExtractionPass(Gvs, DeleteFn, KeepConstInit)); Extract.run(*M); // Now that we have all the GVs we want, mark the module as fully @@ -345,6 +336,27 @@ int main(int argc, char **argv) { // Extract the specified basic blocks from the module and erase the existing // functions. if (!ExtractBlocks.empty()) { + // Figure out which BasicBlocks we should extract. + SmallVector<SmallVector<BasicBlock *, 16>, 4> GroupOfBBs; + for (auto &P : BBMap) { + SmallVector<BasicBlock *, 16> BBs; + for (StringRef BBName : P.second) { + // The function has been materialized, so add its matching basic blocks + // to the block extractor list, or fail if a name is not found. + auto Res = llvm::find_if(*P.first, [&](const BasicBlock &BB) { + return BB.getName().equals(BBName); + }); + if (Res == P.first->end()) { + errs() << argv[0] << ": function " << P.first->getName() + << " doesn't contain a basic block named '" << BBName + << "'!\n"; + return 1; + } + BBs.push_back(&*Res); + } + GroupOfBBs.push_back(BBs); + } + legacy::PassManager PM; PM.add(createBlockExtractorPass(GroupOfBBs, true)); PM.run(*M); @@ -369,7 +381,7 @@ int main(int argc, char **argv) { if (OutputAssembly) Passes.add( createPrintModulePass(Out.os(), "", PreserveAssemblyUseListOrder)); - else if (Force || !CheckBitcodeOutputToConsole(Out.os(), true)) + else if (Force || !CheckBitcodeOutputToConsole(Out.os())) Passes.add(createBitcodeWriterPass(Out.os(), PreserveBitcodeUseListOrder)); Passes.run(*M.get()); diff --git a/llvm/tools/llvm-link/llvm-link.cpp b/llvm/tools/llvm-link/llvm-link.cpp index fa36e083b6f8c..7141bd1ca7a15 100644 --- a/llvm/tools/llvm-link/llvm-link.cpp +++ b/llvm/tools/llvm-link/llvm-link.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Object/Archive.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" @@ -139,6 +140,73 @@ static std::unique_ptr<Module> loadFile(const char *argv0, return Result; } +static std::unique_ptr<Module> loadArFile(const char *Argv0, + const std::string &ArchiveName, + LLVMContext &Context, Linker &L, + unsigned OrigFlags, + unsigned ApplicableFlags) { + std::unique_ptr<Module> Result(new Module("ArchiveModule", Context)); + if (Verbose) + errs() << "Reading library archive file '" << ArchiveName + << "' to memory\n"; + ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = + MemoryBuffer::getFile(ArchiveName, -1, false); + ExitOnErr(errorCodeToError(Buf.getError())); + Error Err = Error::success(); + object::Archive Archive(Buf.get()->getMemBufferRef(), Err); + ExitOnErr(std::move(Err)); + for (const object::Archive::Child &C : Archive.children(Err)) { + Expected<StringRef> Ename = C.getName(); + if (Error E = Ename.takeError()) { + errs() << Argv0 << ": "; + WithColor::error() + << " failed to read name of archive member" + << ArchiveName << "'\n"; + return nullptr; + }; + std::string ChildName = Ename.get().str(); + if (Verbose) + errs() << "Parsing member '" << ChildName + << "' of archive library to module.\n"; + SMDiagnostic ParseErr; + Expected<MemoryBufferRef> MemBuf = C.getMemoryBufferRef(); + if (Error E = MemBuf.takeError()) { + errs() << Argv0 << ": "; + WithColor::error() << " loading memory for member '" << ChildName + << "' of archive library failed'" << ArchiveName + << "'\n"; + return nullptr; + }; + + if (!isBitcode(reinterpret_cast<const unsigned char *> + (MemBuf.get().getBufferStart()), + reinterpret_cast<const unsigned char *> + (MemBuf.get().getBufferEnd()))) { + errs() << Argv0 << ": "; + WithColor::error() << " member of archive is not a bitcode file: '" + << ChildName << "'\n"; + return nullptr; + } + + std::unique_ptr<Module> M = parseIR(MemBuf.get(), ParseErr, Context); + + if (!M.get()) { + errs() << Argv0 << ": "; + WithColor::error() << " parsing member '" << ChildName + << "' of archive library failed'" << ArchiveName + << "'\n"; + return nullptr; + } + if (Verbose) + errs() << "Linking member '" << ChildName << "' of archive library.\n"; + if (L.linkModules(*Result, std::move(M), ApplicableFlags)) + return nullptr; + ApplicableFlags = OrigFlags; + } // end for each child + ExitOnErr(std::move(Err)); + return Result; +} + namespace { /// Helper to load on demand a Module from file and cache it for subsequent @@ -264,9 +332,10 @@ static bool importFunctions(const char *argv0, Module &DestModule) { Entry.insert(F->getGUID()); } auto CachedModuleLoader = [&](StringRef Identifier) { - return ModuleLoaderCache.takeModule(Identifier); + return ModuleLoaderCache.takeModule(std::string(Identifier)); }; - FunctionImporter Importer(*Index, CachedModuleLoader); + FunctionImporter Importer(*Index, CachedModuleLoader, + /*ClearDSOLocalOnDeclarations=*/false); ExitOnErr(Importer.importFunctions(DestModule, ImportList)); return true; @@ -280,7 +349,10 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, // Similar to some flags, internalization doesn't apply to the first file. bool InternalizeLinkedSymbols = false; for (const auto &File : Files) { - std::unique_ptr<Module> M = loadFile(argv0, File, Context); + std::unique_ptr<Module> M = + (llvm::sys::path::extension(File) == ".a") + ? loadArFile(argv0, File, Context, L, Flags, ApplicableFlags) + : loadFile(argv0, File, Context); if (!M.get()) { errs() << argv0 << ": "; WithColor::error() << " loading file '" << File << "'\n"; @@ -313,7 +385,8 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, } // Promotion - if (renameModuleForThinLTO(*M, *Index)) + if (renameModuleForThinLTO(*M, *Index, + /*ClearDSOLocalOnDeclarations=*/false)) return true; } @@ -397,7 +470,7 @@ int main(int argc, char **argv) { errs() << "Writing bitcode...\n"; if (OutputAssembly) { Composite->print(Out.os(), nullptr, PreserveAssemblyUseListOrder); - } else if (Force || !CheckBitcodeOutputToConsole(Out.os(), true)) + } else if (Force || !CheckBitcodeOutputToConsole(Out.os())) WriteBitcodeToFile(*Composite, Out.os(), PreserveBitcodeUseListOrder); // Declare success. diff --git a/llvm/tools/llvm-lto/llvm-lto.cpp b/llvm/tools/llvm-lto/llvm-lto.cpp index b47e68e82850c..0bd9078f2d8ca 100644 --- a/llvm/tools/llvm-lto/llvm-lto.cpp +++ b/llvm/tools/llvm-lto/llvm-lto.cpp @@ -21,7 +21,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" -#include "llvm/CodeGen/CommandFlags.inc" +#include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" @@ -62,6 +62,8 @@ using namespace llvm; +static codegen::RegisterCodeGenFlags CGF; + static cl::opt<char> OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " "(default = '-O2')"), @@ -223,6 +225,10 @@ static cl::opt<bool> CheckHasObjC( "check-for-objc", cl::init(false), cl::desc("Only check if the module has objective-C defined in it")); +static cl::opt<bool> PrintMachOCPUOnly( + "print-macho-cpu-only", cl::init(false), + cl::desc("Instead of running LTO, print the mach-o cpu in each IR file")); + namespace { struct ModuleInfo { @@ -404,6 +410,30 @@ static void listDependentLibraries() { } } +static void printMachOCPUOnly() { + LLVMContext Context; + Context.setDiagnosticHandler(std::make_unique<LLVMLTODiagnosticHandler>(), + true); + TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(); + for (auto &Filename : InputFilenames) { + ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr = + LTOModule::createFromFile(Context, Filename, Options); + if (!ModuleOrErr) + error(ModuleOrErr, "llvm-lto: "); + + Expected<uint32_t> CPUType = (*ModuleOrErr)->getMachOCPUType(); + Expected<uint32_t> CPUSubType = (*ModuleOrErr)->getMachOCPUSubType(); + if (!CPUType) + error("Error while printing mach-o cputype: " + + toString(CPUType.takeError())); + if (!CPUSubType) + error("Error while printing mach-o cpusubtype: " + + toString(CPUSubType.takeError())); + outs() << llvm::format("%s:\ncputype: %u\ncpusubtype: %u\n", + Filename.c_str(), *CPUType, *CPUSubType); + } +} + /// Create a combined index file from the input IR files and write it. /// /// This is meant to enable testing of ThinLTO combined index generation, @@ -454,7 +484,7 @@ static std::string getThinLTOOutputFile(const std::string &Path, if (std::error_code EC = llvm::sys::fs::create_directories(ParentPath)) error(EC, "error creating the directory '" + ParentPath + "'"); } - return NewPath.str(); + return std::string(NewPath.str()); } namespace thinlto { @@ -521,7 +551,7 @@ public: ThinLTOCodeGenerator ThinGenerator; ThinLTOProcessing(const TargetOptions &Options) { - ThinGenerator.setCodePICModel(getRelocModel()); + ThinGenerator.setCodePICModel(codegen::getExplicitRelocModel()); ThinGenerator.setTargetOptions(Options); ThinGenerator.setCacheDir(ThinLTOCacheDir); ThinGenerator.setCachePruningInterval(ThinLTOCachePruningInterval); @@ -873,7 +903,7 @@ int main(int argc, char **argv) { InitializeAllAsmParsers(); // set up the TargetOptions for the machine - TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(); if (ListSymbolsOnly) { listSymbols(Options); @@ -905,6 +935,11 @@ int main(int argc, char **argv) { return 0; } + if (PrintMachOCPUOnly) { + printMachOCPUOnly(); + return 0; + } + if (ThinLTOMode.getNumOccurrences()) { if (ThinLTOMode.getNumOccurrences() > 1) report_fatal_error("You can't specify more than one -thinlto-action"); @@ -929,7 +964,7 @@ int main(int argc, char **argv) { if (UseDiagnosticHandler) CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr); - CodeGen.setCodePICModel(getRelocModel()); + CodeGen.setCodePICModel(codegen::getExplicitRelocModel()); CodeGen.setFreestanding(EnableFreestanding); CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF); @@ -957,7 +992,7 @@ int main(int argc, char **argv) { lto_symbol_attributes Attrs = Module->getSymbolAttributes(I); unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK; if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN) - KeptDSOSyms.push_back(Name); + KeptDSOSyms.push_back(std::string(Name)); } // We use the first input module as the destination module when @@ -980,22 +1015,18 @@ int main(int argc, char **argv) { CodeGen.addMustPreserveSymbol(KeptDSOSyms[i]); // Set cpu and attrs strings for the default target/subtarget. - CodeGen.setCpu(MCPU.c_str()); + CodeGen.setCpu(codegen::getMCPU().c_str()); CodeGen.setOptLevel(OptLevel - '0'); - std::string attrs; - for (unsigned i = 0; i < MAttrs.size(); ++i) { - if (i > 0) - attrs.append(","); - attrs.append(MAttrs[i]); - } - - if (!attrs.empty()) + auto MAttrs = codegen::getMAttrs(); + if (!MAttrs.empty()) { + std::string attrs = join(MAttrs, ","); CodeGen.setAttr(attrs); + } - if (FileType.getNumOccurrences()) - CodeGen.setFileType(FileType); + if (auto FT = codegen::getExplicitFileType()) + CodeGen.setFileType(FT.getValue()); if (!OutputFilename.empty()) { if (!CodeGen.optimize(DisableVerify, DisableInline, DisableGVNLoadPRE, diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp index 67a677dd45fb9..9dd1f13bd3c39 100644 --- a/llvm/tools/llvm-lto2/llvm-lto2.cpp +++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp @@ -16,19 +16,23 @@ //===----------------------------------------------------------------------===// #include "llvm/Bitcode/BitcodeReader.h" -#include "llvm/CodeGen/CommandFlags.inc" +#include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/LTO/Caching.h" #include "llvm/LTO/LTO.h" +#include "llvm/Passes/PassPlugin.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/PluginLoader.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/Threading.h" using namespace llvm; using namespace lto; +static codegen::RegisterCodeGenFlags CGF; + static cl::opt<char> OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " "(default = '-O2')"), @@ -65,8 +69,11 @@ static cl::opt<bool> "import files for the " "distributed backend case")); -static cl::opt<int> Threads("thinlto-threads", - cl::init(llvm::heavyweight_hardware_concurrency())); +// Default to using all available threads in the system, but using only one +// thread per core (no SMT). +// Use -thinlto-threads=all to use hardware_concurrency() instead, which means +// to use all hardware threads or cores in the system. +static cl::opt<std::string> Threads("thinlto-threads"); static cl::list<std::string> SymbolResolutions( "r", @@ -137,6 +144,10 @@ static cl::opt<bool> static cl::opt<std::string> StatsFile("stats-file", cl::desc("Filename to write statistics to")); +static cl::list<std::string> + PassPlugins("load-pass-plugin", + cl::desc("Load passes from plugin library")); + static void check(Error E, std::string Msg) { if (!E) return; @@ -203,7 +214,8 @@ static int run(int argc, char **argv) { return 1; } } - CommandLineResolutions[{FileName, SymbolName}].push_back(Res); + CommandLineResolutions[{std::string(FileName), std::string(SymbolName)}] + .push_back(Res); } std::vector<std::unique_ptr<MemoryBuffer>> MBs; @@ -217,12 +229,12 @@ static int run(int argc, char **argv) { exit(1); }; - Conf.CPU = MCPU; - Conf.Options = InitTargetOptionsFromCodeGenFlags(); - Conf.MAttrs = MAttrs; - if (auto RM = getRelocModel()) - Conf.RelocModel = *RM; - Conf.CodeModel = getCodeModel(); + Conf.CPU = codegen::getMCPU(); + Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(); + Conf.MAttrs = codegen::getMAttrs(); + if (auto RM = codegen::getExplicitRelocModel()) + Conf.RelocModel = RM.getValue(); + Conf.CodeModel = codegen::getExplicitCodeModel(); Conf.DebugPassManager = DebugPassManager; @@ -246,6 +258,8 @@ static int run(int argc, char **argv) { Conf.OptLevel = OptLevel - '0'; Conf.UseNewPM = UseNewPM; + for (auto &PluginFN : PassPlugins) + Conf.PassPlugins.push_back(PluginFN); switch (CGOptLevel) { case '0': Conf.CGOptLevel = CodeGenOpt::None; @@ -264,8 +278,8 @@ static int run(int argc, char **argv) { return 1; } - if (FileType.getNumOccurrences()) - Conf.CGFileType = FileType; + if (auto FT = codegen::getExplicitFileType()) + Conf.CGFileType = FT.getValue(); Conf.OverrideTriple = OverrideTriple; Conf.DefaultTriple = DefaultTriple; @@ -281,7 +295,8 @@ static int run(int argc, char **argv) { /* LinkedObjectsFile */ nullptr, /* OnWrite */ {}); else - Backend = createInProcessThinBackend(Threads); + Backend = createInProcessThinBackend( + llvm::heavyweight_hardware_concurrency(Threads)); LTO Lto(std::move(Conf), std::move(Backend)); bool HasErrors = false; @@ -292,14 +307,14 @@ static int run(int argc, char **argv) { std::vector<SymbolResolution> Res; for (const InputFile::Symbol &Sym : Input->symbols()) { - auto I = CommandLineResolutions.find({F, Sym.getName()}); + auto I = CommandLineResolutions.find({F, std::string(Sym.getName())}); // If it isn't found, look for "$", which would have been added // (followed by a hash) when the symbol was promoted during module // splitting if it was defined in one part and used in the other. // Try looking up the symbol name before the "$". if (I == CommandLineResolutions.end()) { auto SplitName = Sym.getName().rsplit("$"); - I = CommandLineResolutions.find({F, SplitName.first}); + I = CommandLineResolutions.find({F, std::string(SplitName.first)}); } if (I == CommandLineResolutions.end()) { llvm::errs() << argv[0] << ": missing symbol resolution for " << F @@ -354,8 +369,10 @@ static int run(int argc, char **argv) { static int dumpSymtab(int argc, char **argv) { for (StringRef F : make_range(argv + 1, argv + argc)) { - std::unique_ptr<MemoryBuffer> MB = check(MemoryBuffer::getFile(F), F); - BitcodeFileContents BFC = check(getBitcodeFileContents(*MB), F); + std::unique_ptr<MemoryBuffer> MB = + check(MemoryBuffer::getFile(F), std::string(F)); + BitcodeFileContents BFC = + check(getBitcodeFileContents(*MB), std::string(F)); if (BFC.Symtab.size() >= sizeof(irsymtab::storage::Header)) { auto *Hdr = reinterpret_cast<const irsymtab::storage::Header *>( @@ -367,7 +384,7 @@ static int dumpSymtab(int argc, char **argv) { } std::unique_ptr<InputFile> Input = - check(InputFile::create(MB->getMemBufferRef()), F); + check(InputFile::create(MB->getMemBufferRef()), std::string(F)); outs() << "target triple: " << Input->getTargetTriple() << '\n'; Triple TT(Input->getTargetTriple()); diff --git a/llvm/tools/llvm-mc/Disassembler.cpp b/llvm/tools/llvm-mc/Disassembler.cpp index e286c0fff6e15..16ab99548adfc 100644 --- a/llvm/tools/llvm-mc/Disassembler.cpp +++ b/llvm/tools/llvm-mc/Disassembler.cpp @@ -68,7 +68,7 @@ static bool PrintInsts(const MCDisassembler &DisAsm, LLVM_FALLTHROUGH; case MCDisassembler::Success: - Streamer.EmitInstruction(Inst, STI); + Streamer.emitInstruction(Inst, STI); break; } } diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp index 6aa347d98be24..66b55abc48983 100644 --- a/llvm/tools/llvm-mc/llvm-mc.cpp +++ b/llvm/tools/llvm-mc/llvm-mc.cpp @@ -25,7 +25,7 @@ #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.inc" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" #include "llvm/Support/FileUtilities.h" @@ -41,6 +41,8 @@ using namespace llvm; +static mc::RegisterMCTargetOptionsFlags MOF; + static cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-")); @@ -317,7 +319,7 @@ int main(int argc, char **argv) { cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n"); - const MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + const MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); setDwarfDebugFlags(argc, argv); setDwarfDebugProducer(); @@ -385,6 +387,31 @@ int main(int argc, char **argv) { return 1; } Ctx.setDwarfVersion(DwarfVersion); + if (MCOptions.Dwarf64) { + // The 64-bit DWARF format was introduced in DWARFv3. + if (DwarfVersion < 3) { + errs() << ProgName + << ": the 64-bit DWARF format is not supported for DWARF versions " + "prior to 3\n"; + return 1; + } + // 32-bit targets don't support DWARF64, which requires 64-bit relocations. + if (MAI->getCodePointerSize() < 8) { + errs() << ProgName + << ": the 64-bit DWARF format is only supported for 64-bit " + "targets\n"; + return 1; + } + // If needsDwarfSectionOffsetDirective is true, we would eventually call + // MCStreamer::emitSymbolValue() with IsSectionRelative = true, but that + // is supported only for 4-byte long references. + if (MAI->needsDwarfSectionOffsetDirective()) { + errs() << ProgName << ": the 64-bit DWARF format is not supported for " + << TheTriple.normalize() << "\n"; + return 1; + } + Ctx.setDwarfFormat(dwarf::DWARF64); + } if (!DwarfDebugFlags.empty()) Ctx.setDwarfDebugFlags(StringRef(DwarfDebugFlags)); if (!DwarfDebugProducer.empty()) @@ -399,7 +426,7 @@ int main(int argc, char **argv) { } for (const auto &Arg : DebugPrefixMap) { const auto &KV = StringRef(Arg).split('='); - Ctx.addDebugPrefixMapEntry(KV.first, KV.second); + Ctx.addDebugPrefixMapEntry(std::string(KV.first), std::string(KV.second)); } if (!MainFileName.empty()) Ctx.setMainFileName(MainFileName); @@ -474,9 +501,6 @@ int main(int argc, char **argv) { } else { assert(FileType == OFT_ObjectFile && "Invalid file type!"); - // Don't waste memory on names of temp labels. - Ctx.setUseNamesOnTempLabels(false); - if (!Out->os().supportsSeeking()) { BOS = std::make_unique<buffer_ostream>(Out->os()); OS = BOS.get(); diff --git a/llvm/tools/llvm-mca/CodeRegion.h b/llvm/tools/llvm-mca/CodeRegion.h index cabb4a5d44842..d2b05fa80c54e 100644 --- a/llvm/tools/llvm-mca/CodeRegion.h +++ b/llvm/tools/llvm-mca/CodeRegion.h @@ -35,8 +35,10 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCInst.h" +#include "llvm/Support/Error.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/SourceMgr.h" #include <vector> diff --git a/llvm/tools/llvm-mca/CodeRegionGenerator.cpp b/llvm/tools/llvm-mca/CodeRegionGenerator.cpp index 8ddcd2f4abe21..831b76ab80cf7 100644 --- a/llvm/tools/llvm-mca/CodeRegionGenerator.cpp +++ b/llvm/tools/llvm-mca/CodeRegionGenerator.cpp @@ -47,21 +47,21 @@ public: : MCStreamer(Context), Regions(R) {} // We only want to intercept the emission of new instructions. - virtual void EmitInstruction(const MCInst &Inst, - const MCSubtargetInfo &/* unused */) override { + virtual void emitInstruction(const MCInst &Inst, + const MCSubtargetInfo & /* unused */) override { Regions.addInstruction(Inst); } - bool EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override { + bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override { return true; } - void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size, + void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment) override {} - void EmitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr, + void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr, uint64_t Size = 0, unsigned ByteAlignment = 0, SMLoc Loc = SMLoc()) override {} - void EmitGPRel32Value(const MCExpr *Value) override {} + void emitGPRel32Value(const MCExpr *Value) override {} void BeginCOFFSymbolDef(const MCSymbol *Symbol) override {} void EmitCOFFSymbolStorageClass(int StorageClass) override {} void EmitCOFFSymbolType(int Type) override {} diff --git a/llvm/tools/llvm-mca/llvm-mca.cpp b/llvm/tools/llvm-mca/llvm-mca.cpp index fff5906bb59ba..9f3bf41ff3f8b 100644 --- a/llvm/tools/llvm-mca/llvm-mca.cpp +++ b/llvm/tools/llvm-mca/llvm-mca.cpp @@ -39,7 +39,7 @@ #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.inc" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/MCA/CodeEmitter.h" #include "llvm/MCA/Context.h" #include "llvm/MCA/InstrBuilder.h" @@ -62,6 +62,8 @@ using namespace llvm; +static mc::RegisterMCTargetOptionsFlags MOF; + static cl::OptionCategory ToolOptions("Tool Options"); static cl::OptionCategory ViewOptions("View Options"); @@ -324,7 +326,7 @@ int main(int argc, char **argv) { processViewOptions(); if (!MCPU.compare("native")) - MCPU = llvm::sys::getHostCPUName(); + MCPU = std::string(llvm::sys::getHostCPUName()); std::unique_ptr<MCSubtargetInfo> STI( TheTarget->createMCSubtargetInfo(TripleName, MCPU, MATTR)); @@ -353,7 +355,7 @@ int main(int argc, char **argv) { std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); assert(MRI && "Unable to create target register info!"); - MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); std::unique_ptr<MCAsmInfo> MAI( TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); assert(MAI && "Unable to create target asm info!"); @@ -443,7 +445,7 @@ int main(int argc, char **argv) { TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); std::unique_ptr<MCAsmBackend> MAB(TheTarget->createMCAsmBackend( - *STI, *MRI, InitMCTargetOptionsFromFlags())); + *STI, *MRI, mc::InitMCTargetOptionsFromFlags())); for (const std::unique_ptr<mca::CodeRegion> &Region : Regions) { // Skip empty code regions. diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp index 107d62b1f2b92..ecd1e21e15bfb 100644 --- a/llvm/tools/llvm-nm/llvm-nm.cpp +++ b/llvm/tools/llvm-nm/llvm-nm.cpp @@ -28,6 +28,8 @@ #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/TapiFile.h" +#include "llvm/Object/TapiUniversal.h" #include "llvm/Object/Wasm.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -180,8 +182,10 @@ cl::opt<bool> JustSymbolName("just-symbol-name", cl::alias JustSymbolNames("j", cl::desc("Alias for --just-symbol-name"), cl::aliasopt(JustSymbolName), cl::Grouping); -cl::opt<bool> SpecialSyms("special-syms", - cl::desc("No-op. Used for GNU compatibility only")); +cl::opt<bool> + SpecialSyms("special-syms", + cl::desc("Do not filter special symbols from the output"), + cl::cat(NMCat)); cl::list<std::string> SegSect("s", cl::multi_val(2), cl::ZeroOrMore, cl::value_desc("segment section"), cl::Hidden, @@ -210,6 +214,11 @@ cl::opt<bool> NoLLVMBitcode("no-llvm-bc", cl::desc("Disable LLVM bitcode reader"), cl::cat(NMCat)); +cl::opt<bool> AddInlinedInfo("add-inlinedinfo", + cl::desc("Add symbols from the inlined libraries, " + "TBD(Mach-O) only"), + cl::cat(NMCat)); + cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); bool PrintAddress = true; @@ -306,13 +315,17 @@ struct NMSymbol { static bool compareSymbolAddress(const NMSymbol &A, const NMSymbol &B) { bool ADefined; + // Symbol flags have been checked in the caller. + uint32_t AFlags = cantFail(A.Sym.getFlags()); if (A.Sym.getRawDataRefImpl().p) - ADefined = !(A.Sym.getFlags() & SymbolRef::SF_Undefined); + ADefined = !(AFlags & SymbolRef::SF_Undefined); else ADefined = A.TypeChar != 'U'; bool BDefined; + // Symbol flags have been checked in the caller. + uint32_t BFlags = cantFail(B.Sym.getFlags()); if (B.Sym.getRawDataRefImpl().p) - BDefined = !(B.Sym.getFlags() & SymbolRef::SF_Undefined); + BDefined = !(BFlags & SymbolRef::SF_Undefined); else BDefined = B.TypeChar != 'U'; return std::make_tuple(ADefined, A.Address, A.Name, A.Size) < @@ -336,6 +349,8 @@ static char isSymbolList64Bit(SymbolicFile &Obj) { return false; if (isa<WasmObjectFile>(Obj)) return false; + if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj)) + return Tapi->is64Bit(); if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) return MachO->is64Bit(); return cast<ELFObjectFileBase>(Obj).getBytesInAddress() == 8; @@ -366,7 +381,7 @@ static void darwinPrintSymbol(SymbolicFile &Obj, const NMSymbol &S, uint64_t NValue = 0; MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj); if (Obj.isIR()) { - uint32_t SymFlags = S.Sym.getFlags(); + uint32_t SymFlags = cantFail(S.Sym.getFlags()); if (SymFlags & SymbolRef::SF_Global) NType |= MachO::N_EXT; if (SymFlags & SymbolRef::SF_Hidden) @@ -707,9 +722,32 @@ static bool symbolIsDefined(const NMSymbol &Sym) { return Sym.TypeChar != 'U' && Sym.TypeChar != 'w' && Sym.TypeChar != 'v'; } +static void writeFileName(raw_ostream &S, StringRef ArchiveName, + StringRef ArchitectureName) { + if (!ArchitectureName.empty()) + S << "(for architecture " << ArchitectureName << "):"; + if (OutputFormat == posix && !ArchiveName.empty()) + S << ArchiveName << "[" << CurrentFilename << "]: "; + else { + if (!ArchiveName.empty()) + S << ArchiveName << ":"; + S << CurrentFilename << ": "; + } +} + +static bool isSpecialSym(SymbolicFile &Obj, StringRef Name) { + auto *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj); + if (!ELFObj) + return false; + uint16_t EMachine = ELFObj->getEMachine(); + if (EMachine != ELF::EM_ARM && EMachine != ELF::EM_AARCH64) + return false; + return !Name.empty() && Name[0] == '$'; +} + static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, - const std::string &ArchiveName, - const std::string &ArchitectureName) { + StringRef ArchiveName, + StringRef ArchitectureName) { if (!NoSort) { using Comparator = bool (*)(const NMSymbol &, const NMSymbol &); Comparator Cmp; @@ -773,24 +811,6 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, } } - auto writeFileName = [&](raw_ostream &S) { - if (!ArchitectureName.empty()) - S << "(for architecture " << ArchitectureName << "):"; - if (OutputFormat == posix && !ArchiveName.empty()) - S << ArchiveName << "[" << CurrentFilename << "]: "; - else { - if (!ArchiveName.empty()) - S << ArchiveName << ":"; - S << CurrentFilename << ": "; - } - }; - - if (SymbolList.empty()) { - if (PrintFileName) - writeFileName(errs()); - errs() << "no symbols\n"; - } - for (const NMSymbol &S : SymbolList) { uint32_t SymFlags; std::string Name = S.Name.str(); @@ -799,19 +819,26 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, if (Optional<std::string> Opt = demangle(S.Name, MachO)) Name = *Opt; } - if (S.Sym.getRawDataRefImpl().p) - SymFlags = S.Sym.getFlags(); - else + if (S.Sym.getRawDataRefImpl().p) { + Expected<uint32_t> SymFlagsOrErr = S.Sym.getFlags(); + if (!SymFlagsOrErr) { + // TODO: Test this error. + error(SymFlagsOrErr.takeError(), Obj.getFileName()); + return; + } + SymFlags = *SymFlagsOrErr; + } else 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) || (Weak && NoWeakSymbols)) + (!Global && ExternalOnly) || (Weak && NoWeakSymbols) || + (!SpecialSyms && isSpecialSym(Obj, Name))) continue; if (PrintFileName) - writeFileName(outs()); + writeFileName(outs(), ArchiveName, ArchitectureName); if ((JustSymbolName || (UndefinedOnly && MachO && OutputFormat != darwin)) && OutputFormat != posix) { @@ -1041,15 +1068,19 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) { return '?'; } +static char getSymbolNMTypeChar(TapiFile &Obj, basic_symbol_iterator I) { + return 's'; +} + static char getSymbolNMTypeChar(WasmObjectFile &Obj, basic_symbol_iterator I) { - uint32_t Flags = I->getFlags(); + uint32_t Flags = cantFail(I->getFlags()); if (Flags & SymbolRef::SF_Executable) return 't'; return 'd'; } static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I) { - uint32_t Flags = I->getFlags(); + uint32_t Flags = cantFail(I->getFlags()); // FIXME: should we print 'b'? At the IR level we cannot be sure if this // will be in bss or not, but we could approximate. if (Flags & SymbolRef::SF_Executable) @@ -1081,7 +1112,8 @@ static StringRef getNMTypeName(SymbolicFile &Obj, basic_symbol_iterator I) { // 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(); + // Symbol Flags have been checked in the caller. + uint32_t Symflags = cantFail(I->getFlags()); if (ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj)) { if (Symflags & object::SymbolRef::SF_Absolute) SecName = "*ABS*"; @@ -1133,6 +1165,8 @@ static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I, Ret = getSymbolNMTypeChar(*MachO, I); else if (WasmObjectFile *Wasm = dyn_cast<WasmObjectFile>(&Obj)) Ret = getSymbolNMTypeChar(*Wasm, I); + else if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj)) + Ret = getSymbolNMTypeChar(*Tapi, I); else if (ELFObjectFileBase *ELF = dyn_cast<ELFObjectFileBase>(&Obj)) { if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC) return 'i'; @@ -1184,10 +1218,9 @@ static unsigned getNsectInMachO(MachOObjectFile &Obj, BasicSymbolRef Sym) { return (STE.n_type & MachO::N_TYPE) == MachO::N_SECT ? STE.n_sect : 0; } -static void -dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, - const std::string &ArchiveName = std::string(), - const std::string &ArchitectureName = std::string()) { +static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, + StringRef ArchiveName = {}, + StringRef ArchitectureName = {}) { auto Symbols = Obj.symbols(); if (DynamicSyms) { const auto *E = dyn_cast<ELFObjectFileBase>(&Obj); @@ -1209,12 +1242,16 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, if (Nsect == 0) return; } - if (!MachO || !DyldInfoOnly) { + if (!(MachO && DyldInfoOnly)) { for (BasicSymbolRef Sym : Symbols) { - uint32_t SymFlags = Sym.getFlags(); - if (!DebugSyms && (SymFlags & SymbolRef::SF_FormatSpecific)) + Expected<uint32_t> SymFlagsOrErr = Sym.getFlags(); + if (!SymFlagsOrErr) { + error(SymFlagsOrErr.takeError(), Obj.getFileName()); + return; + } + if (!DebugSyms && (*SymFlagsOrErr & SymbolRef::SF_FormatSpecific)) continue; - if (WithoutAliases && (SymFlags & SymbolRef::SF_Indirect)) + if (WithoutAliases && (*SymFlagsOrErr & SymbolRef::SF_Indirect)) continue; // If a "-s segname sectname" option was specified and this is a Mach-O // file and this section appears in this file, Nsect will be non-zero then @@ -1739,6 +1776,12 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, } CurrentFilename = Obj.getFileName(); + + if (Symbols.empty() && SymbolList.empty()) { + writeFileName(errs(), ArchiveName, ArchitectureName); + errs() << "no symbols\n"; + } + sortAndPrintSymbolList(Obj, printName, ArchiveName, ArchitectureName); } @@ -1903,7 +1946,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { if (PrintFileName) { - ArchiveName = A->getFileName(); + ArchiveName = std::string(A->getFileName()); if (ArchFlags.size() > 1) ArchitectureName = I->getArchFlagName(); } else { @@ -1972,7 +2015,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { if (PrintFileName) - ArchiveName = A->getFileName(); + ArchiveName = std::string(A->getFileName()); else outs() << "\n" << A->getFileName() << "(" << O->getFileName() << ")" @@ -2037,7 +2080,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { } if (SymbolicFile *F = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { if (PrintFileName) { - ArchiveName = A->getFileName(); + ArchiveName = std::string(A->getFileName()); if (isa<MachOObjectFile>(F) && moreThanOneArch) ArchitectureName = O.getArchFlagName(); } else { @@ -2066,6 +2109,31 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { } return; } + + if (TapiUniversal *TU = dyn_cast<TapiUniversal>(&Bin)) { + for (const TapiUniversal::ObjectForArch &I : TU->objects()) { + StringRef ArchName = I.getArchFlagName(); + const bool ShowArch = + ArchFlags.empty() || + any_of(ArchFlags, [&](StringRef Name) { return Name == ArchName; }); + if (!ShowArch) + continue; + if (!AddInlinedInfo && !I.isTopLevelLib()) + continue; + if (auto ObjOrErr = I.getAsObjectFile()) { + outs() << "\n" + << I.getInstallName() << " (for architecture " << ArchName << ")" + << ":\n"; + dumpSymbolNamesFromObject(*ObjOrErr.get(), false, {}, ArchName); + } else if (Error E = + isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { + error(std::move(E), Filename, ArchName); + } + } + + return; + } + if (SymbolicFile *O = dyn_cast<SymbolicFile>(&Bin)) { if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) { WithColor::warning(errs(), ToolName) diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp index b172fae527eb5..43ec2b1fa82f2 100644 --- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -89,6 +89,43 @@ static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) { IMAGE_SCN_MEM_DISCARDABLE); } +static void setSectionFlags(Section &Sec, SectionFlag AllFlags) { + // Need to preserve alignment flags. + const uint32_t PreserveMask = + IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_ALIGN_4BYTES | + IMAGE_SCN_ALIGN_8BYTES | IMAGE_SCN_ALIGN_16BYTES | + IMAGE_SCN_ALIGN_32BYTES | IMAGE_SCN_ALIGN_64BYTES | + IMAGE_SCN_ALIGN_128BYTES | IMAGE_SCN_ALIGN_256BYTES | + IMAGE_SCN_ALIGN_512BYTES | IMAGE_SCN_ALIGN_1024BYTES | + IMAGE_SCN_ALIGN_2048BYTES | IMAGE_SCN_ALIGN_4096BYTES | + IMAGE_SCN_ALIGN_8192BYTES; + + // Setup new section characteristics based on the flags provided in command + // line. + uint32_t NewCharacteristics = + (Sec.Header.Characteristics & PreserveMask) | IMAGE_SCN_MEM_READ; + + if ((AllFlags & SectionFlag::SecAlloc) && !(AllFlags & SectionFlag::SecLoad)) + NewCharacteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA; + if (AllFlags & SectionFlag::SecNoload) + NewCharacteristics |= IMAGE_SCN_LNK_REMOVE; + if (!(AllFlags & SectionFlag::SecReadonly)) + NewCharacteristics |= IMAGE_SCN_MEM_WRITE; + if (AllFlags & SectionFlag::SecDebug) + NewCharacteristics |= + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE; + if (AllFlags & SectionFlag::SecCode) + NewCharacteristics |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE; + if (AllFlags & SectionFlag::SecData) + NewCharacteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA; + if (AllFlags & SectionFlag::SecShare) + NewCharacteristics |= IMAGE_SCN_MEM_SHARED; + if (AllFlags & SectionFlag::SecExclude) + NewCharacteristics |= IMAGE_SCN_LNK_REMOVE; + + Sec.Header.Characteristics = NewCharacteristics; +} + static Error handleArgs(const CopyConfig &Config, Object &Obj) { // Perform the actual section removals. Obj.removeSections([&Config](const Section &Sec) { @@ -178,6 +215,13 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { return false; }); + if (!Config.SetSectionFlags.empty()) + for (Section &Sec : Obj.getMutableSections()) { + const auto It = Config.SetSectionFlags.find(Sec.Name); + if (It != Config.SetSectionFlags.end()) + setSectionFlags(Sec, It->second.NewFlags); + } + for (const auto &Flag : Config.AddSection) { StringRef SecName, FileName; std::tie(SecName, FileName) = Flag.split("="); @@ -205,10 +249,11 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || - !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || - Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || - Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc || - Config.StripSections || Config.Weaken || Config.DecompressDebugSections || + !Config.SetSectionAlignment.empty() || Config.ExtractDWO || + Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO || + Config.StripNonAlloc || Config.StripSections || + Config.StripSwiftSymbols || Config.Weaken || + Config.DecompressDebugSections || Config.DiscardMode == DiscardType::Locals || !Config.SymbolsToAdd.empty() || Config.EntryExpr) { return createStringError(llvm::errc::invalid_argument, diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/llvm/tools/llvm-objcopy/COFF/Reader.cpp index 7be9cce2be3d9..d1beacb3bd67e 100644 --- a/llvm/tools/llvm-objcopy/COFF/Reader.cpp +++ b/llvm/tools/llvm-objcopy/COFF/Reader.cpp @@ -45,9 +45,9 @@ Error COFFReader::readExecutableHeaders(Object &Obj) const { } for (size_t I = 0; I < Obj.PeHeader.NumberOfRvaAndSize; I++) { - const data_directory *Dir; - if (auto EC = COFFObj.getDataDirectory(I, Dir)) - return errorCodeToError(EC); + const data_directory *Dir = COFFObj.getDataDirectory(I); + if (!Dir) + return errorCodeToError(object_error::parse_failed); Obj.DataDirectories.emplace_back(*Dir); } return Error::success(); @@ -57,9 +57,10 @@ 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); + Expected<const coff_section *> SecOrErr = COFFObj.getSection(I); + if (!SecOrErr) + return SecOrErr.takeError(); + const coff_section *Sec = *SecOrErr; Sections.push_back(Section()); Section &S = Sections.back(); S.Header = *Sec; @@ -99,8 +100,10 @@ Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const { else copySymbol(Sym.Sym, *reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr())); - if (auto EC = COFFObj.getSymbolName(SymRef, Sym.Name)) - return errorCodeToError(EC); + auto NameOrErr = COFFObj.getSymbolName(SymRef); + if (!NameOrErr) + return NameOrErr.takeError(); + Sym.Name = *NameOrErr; ArrayRef<uint8_t> AuxData = COFFObj.getSymbolAuxData(SymRef); size_t SymSize = IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16); diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp index e35e0474a36d5..6b560890a4c16 100644 --- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp +++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp @@ -383,6 +383,16 @@ Error COFFWriter::write(bool IsBigObj) { return Buf.commit(); } +Expected<uint32_t> COFFWriter::virtualAddressToFileAddress(uint32_t RVA) { + for (const auto &S : Obj.getSections()) { + if (RVA >= S.Header.VirtualAddress && + RVA < S.Header.VirtualAddress + S.Header.SizeOfRawData) + return S.Header.PointerToRawData + RVA - S.Header.VirtualAddress; + } + return createStringError(object_error::parse_failed, + "debug directory payload not found"); +} + // Locate which sections contain the debug directories, iterate over all // the debug_directory structs in there, and set the PointerToRawData field // in all of them, according to their new physical location in the file. @@ -406,10 +416,17 @@ Error COFFWriter::patchDebugDirectory() { uint8_t *End = Ptr + Dir->Size; while (Ptr < End) { debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr); - Debug->PointerToRawData = - S.Header.PointerToRawData + Offset + sizeof(debug_directory); - Ptr += sizeof(debug_directory) + Debug->SizeOfData; - Offset += sizeof(debug_directory) + Debug->SizeOfData; + if (!Debug->AddressOfRawData) + return createStringError(object_error::parse_failed, + "debug directory payload outside of " + "mapped sections not supported"); + if (Expected<uint32_t> FilePosOrErr = + virtualAddressToFileAddress(Debug->AddressOfRawData)) + Debug->PointerToRawData = *FilePosOrErr; + else + return FilePosOrErr.takeError(); + Ptr += sizeof(debug_directory); + Offset += sizeof(debug_directory); } // Debug directory found and patched, all done. return Error::success(); diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.h b/llvm/tools/llvm-objcopy/COFF/Writer.h index 681a8d5e4a66c..3c0bdcbd5d6f6 100644 --- a/llvm/tools/llvm-objcopy/COFF/Writer.h +++ b/llvm/tools/llvm-objcopy/COFF/Writer.h @@ -45,6 +45,7 @@ class COFFWriter { Error write(bool IsBigObj); Error patchDebugDirectory(); + Expected<uint32_t> virtualAddressToFileAddress(uint32_t RVA); public: virtual ~COFFWriter() {} diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/CopyConfig.cpp index 73ed00b5cb2ab..1fde54dd290ae 100644 --- a/llvm/tools/llvm-objcopy/CopyConfig.cpp +++ b/llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -146,6 +146,7 @@ static SectionFlag parseSectionRenameFlag(StringRef SectionName) { .CaseLower("strings", SectionFlag::SecStrings) .CaseLower("contents", SectionFlag::SecContents) .CaseLower("share", SectionFlag::SecShare) + .CaseLower("exclude", SectionFlag::SecExclude) .Default(SectionFlag::SecNone); } @@ -158,8 +159,8 @@ parseSectionFlagSet(ArrayRef<StringRef> SectionFlags) { 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", + "compatibility: alloc, load, noload, readonly, exclude, debug, " + "code, data, rom, share, contents, merge, strings", Flag.str().c_str()); ParsedFlags |= ParsedFlag; } @@ -272,6 +273,7 @@ static const StringMap<MachineInfo> TargetMap{ // SPARC {"elf32-sparc", {ELF::EM_SPARC, false, false}}, {"elf32-sparcel", {ELF::EM_SPARC, false, true}}, + {"elf32-hexagon", {ELF::EM_HEXAGON, false, true}}, }; static Expected<TargetInfo> @@ -391,9 +393,30 @@ template <class T> static ErrorOr<T> getAsInteger(StringRef Val) { return Result; } +namespace { + +enum class ToolType { Objcopy, Strip, InstallNameTool }; + +} // anonymous namespace + static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS, - StringRef ToolName) { - OptTable.PrintHelp(OS, (ToolName + " input [output]").str().c_str(), + ToolType Tool) { + StringRef HelpText, ToolName; + switch (Tool) { + case ToolType::Objcopy: + ToolName = "llvm-objcopy"; + HelpText = " [options] input [output]"; + break; + case ToolType::Strip: + ToolName = "llvm-strip"; + HelpText = " [options] inputs..."; + break; + case ToolType::InstallNameTool: + ToolName = "llvm-install-name-tool"; + HelpText = " [options] input"; + break; + } + OptTable.PrintHelp(OS, (ToolName + HelpText).str().c_str(), (ToolName + " tool").str().c_str()); // TODO: Replace this with libOption call once it adds extrahelp support. // The CommandLine library has a cl::extrahelp class to support this, @@ -414,12 +437,12 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); if (InputArgs.size() == 0) { - printHelp(T, errs(), "llvm-objcopy"); + printHelp(T, errs(), ToolType::Objcopy); exit(1); } if (InputArgs.hasArg(OBJCOPY_help)) { - printHelp(T, outs(), "llvm-objcopy"); + printHelp(T, outs(), ToolType::Objcopy); exit(0); } @@ -665,8 +688,10 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); Config.DecompressDebugSections = InputArgs.hasArg(OBJCOPY_decompress_debug_sections); - if (Config.DiscardMode == DiscardType::All) + if (Config.DiscardMode == DiscardType::All) { Config.StripDebug = true; + Config.KeepFileSymbols = true; + } for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create( Arg->getValue(), SymbolMatchStyle, ErrorCallback))) @@ -802,13 +827,20 @@ parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); + if (MissingArgumentCount) + return createStringError( + errc::invalid_argument, + "missing argument to " + + StringRef(InputArgs.getArgString(MissingArgumentIndex)) + + " option"); + if (InputArgs.size() == 0) { - printHelp(T, errs(), "llvm-install-name-tool"); + printHelp(T, errs(), ToolType::InstallNameTool); exit(1); } if (InputArgs.hasArg(INSTALL_NAME_TOOL_help)) { - printHelp(T, outs(), "llvm-install-name-tool"); + printHelp(T, outs(), ToolType::InstallNameTool); exit(0); } @@ -822,6 +854,61 @@ parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath)) Config.RPathToAdd.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_delete_rpath)) { + StringRef RPath = Arg->getValue(); + + // Cannot add and delete the same rpath at the same time. + if (is_contained(Config.RPathToAdd, RPath)) + return createStringError( + errc::invalid_argument, + "cannot specify both -add_rpath %s and -delete_rpath %s", + RPath.str().c_str(), RPath.str().c_str()); + + Config.RPathsToRemove.insert(RPath); + } + + for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_rpath)) { + StringRef Old = Arg->getValue(0); + StringRef New = Arg->getValue(1); + + auto Match = [=](StringRef RPath) { return RPath == Old || RPath == New; }; + + // Cannot specify duplicate -rpath entries + auto It1 = find_if( + Config.RPathsToUpdate, + [&Match](const DenseMap<StringRef, StringRef>::value_type &OldNew) { + return Match(OldNew.getFirst()) || Match(OldNew.getSecond()); + }); + if (It1 != Config.RPathsToUpdate.end()) + return createStringError(errc::invalid_argument, + "cannot specify both -rpath " + It1->getFirst() + + " " + It1->getSecond() + " and -rpath " + + Old + " " + New); + + // Cannot specify the same rpath under both -delete_rpath and -rpath + auto It2 = find_if(Config.RPathsToRemove, Match); + if (It2 != Config.RPathsToRemove.end()) + return createStringError(errc::invalid_argument, + "cannot specify both -delete_rpath " + *It2 + + " and -rpath " + Old + " " + New); + + // Cannot specify the same rpath under both -add_rpath and -rpath + auto It3 = find_if(Config.RPathToAdd, Match); + if (It3 != Config.RPathToAdd.end()) + return createStringError(errc::invalid_argument, + "cannot specify both -add_rpath " + *It3 + + " and -rpath " + Old + " " + New); + + Config.RPathsToUpdate.insert({Old, New}); + } + + if (auto *Arg = InputArgs.getLastArg(INSTALL_NAME_TOOL_id)) + Config.SharedLibId = Arg->getValue(); + + for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_change)) { + Config.InstallNamesToUpdate.insert({Arg->getValue(0), Arg->getValue(1)}); + } + SmallVector<StringRef, 2> Positional; for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN)) return createStringError(errc::invalid_argument, "unknown argument '%s'", @@ -853,12 +940,12 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); if (InputArgs.size() == 0) { - printHelp(T, errs(), "llvm-strip"); + printHelp(T, errs(), ToolType::Strip); exit(1); } if (InputArgs.hasArg(STRIP_help)) { - printHelp(T, outs(), "llvm-strip"); + printHelp(T, outs(), ToolType::Strip); exit(0); } @@ -908,6 +995,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, 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); + Config.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols); Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug); Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); @@ -936,8 +1024,10 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, !Config.StripAllGNU && Config.SymbolsToRemove.empty()) Config.StripAll = true; - if (Config.DiscardMode == DiscardType::All) + if (Config.DiscardMode == DiscardType::All) { Config.StripDebug = true; + Config.KeepFileSymbols = true; + } Config.DeterministicArchives = InputArgs.hasFlag(STRIP_enable_deterministic_archives, diff --git a/llvm/tools/llvm-objcopy/CopyConfig.h b/llvm/tools/llvm-objcopy/CopyConfig.h index c262934b4a419..1341dd674c7b7 100644 --- a/llvm/tools/llvm-objcopy/CopyConfig.h +++ b/llvm/tools/llvm-objcopy/CopyConfig.h @@ -12,6 +12,7 @@ #include "ELF/ELFConfig.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" @@ -69,7 +70,8 @@ enum SectionFlag { SecStrings = 1 << 9, SecContents = 1 << 10, SecShare = 1 << 11, - LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ SecShare) + SecExclude = 1 << 12, + LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecExclude) }; struct SectionRename { @@ -176,6 +178,12 @@ struct CopyConfig { std::vector<StringRef> DumpSection; std::vector<StringRef> SymbolsToAdd; std::vector<StringRef> RPathToAdd; + DenseMap<StringRef, StringRef> RPathsToUpdate; + DenseMap<StringRef, StringRef> InstallNamesToUpdate; + DenseSet<StringRef> RPathsToRemove; + + // install-name-tool's id option + Optional<StringRef> SharedLibId; // Section matchers NameMatcher KeepSection; @@ -218,6 +226,7 @@ struct CopyConfig { bool StripDebug = false; bool StripNonAlloc = false; bool StripSections = false; + bool StripSwiftSymbols = false; bool StripUnneeded = false; bool Weaken = false; bool DecompressDebugSections = false; diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp index a0cfd9a5ff868..66953f9ef0d56 100644 --- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -11,7 +11,6 @@ #include "CopyConfig.h" #include "Object.h" #include "llvm-objcopy.h" - #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" @@ -32,6 +31,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -83,6 +83,8 @@ uint64_t getNewShfFlags(SectionFlag AllFlags) { NewFlags |= ELF::SHF_MERGE; if (AllFlags & SectionFlag::SecStrings) NewFlags |= ELF::SHF_STRINGS; + if (AllFlags & SectionFlag::SecExclude) + NewFlags |= ELF::SHF_EXCLUDE; return NewFlags; } @@ -90,10 +92,11 @@ 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; + const uint64_t PreserveMask = + (ELF::SHF_COMPRESSED | ELF::SHF_GROUP | ELF::SHF_LINK_ORDER | + ELF::SHF_MASKOS | ELF::SHF_MASKPROC | ELF::SHF_TLS | + ELF::SHF_INFO_LINK) & + ~ELF::SHF_EXCLUDE; return (OldFlags & PreserveMask) | (NewFlags & ~PreserveMask); } @@ -267,7 +270,7 @@ static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader, auto OnlyKeepDWOPred = [&DWOFile](const SectionBase &Sec) { return onlyKeepDWOPred(*DWOFile, Sec); }; - if (Error E = DWOFile->removeSections(Config.AllowBrokenLinks, + if (Error E = DWOFile->removeSections(Config.AllowBrokenLinks, OnlyKeepDWOPred)) return E; if (Config.OutputArch) { @@ -285,7 +288,7 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename, Object &Obj) { for (auto &Sec : Obj.sections()) { if (Sec.Name == SecName) { - if (Sec.OriginalData.empty()) + if (Sec.Type == SHT_NOBITS) return createStringError(object_error::parse_failed, "cannot dump section '%s': it has no contents", SecName.str().c_str()); @@ -387,7 +390,7 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { const auto I = Config.SymbolsToRename.find(Sym.Name); if (I != Config.SymbolsToRename.end()) - Sym.Name = I->getValue(); + Sym.Name = std::string(I->getValue()); if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION) Sym.Name = (Config.SymbolsPrefix + Sym.Name).str(); @@ -417,6 +420,9 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { if (Config.StripAll || Config.StripAllGNU) return true; + if (Config.StripDebug && Sym.Type == STT_FILE) + return true; + if (Config.SymbolsToRemove.matches(Sym.Name)) return true; @@ -572,11 +578,11 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { } if (Config.CompressionType != DebugCompressionType::None) - replaceDebugSections(Obj, RemovePred, isCompressable, + replaceDebugSections(Obj, RemovePred, isCompressable, [&Config, &Obj](const SectionBase *S) { return &Obj.addSection<CompressedSection>( *S, Config.CompressionType); - }); + }); else if (Config.DecompressDebugSections) replaceDebugSections( Obj, RemovePred, @@ -598,7 +604,9 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { // system. The only priority is that keeps/copies overrule removes. static Error handleArgs(const CopyConfig &Config, Object &Obj, const Reader &Reader, ElfType OutputElfType) { - + if (Config.StripSwiftSymbols) + return createStringError(llvm::errc::invalid_argument, + "option not supported by llvm-objcopy for ELF"); if (!Config.SplitDWO.empty()) if (Error E = splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType)) @@ -609,6 +617,15 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj, Obj.OSABI = Config.OutputArch.getValue().OSABI; } + // Dump sections before add/remove for compatibility with GNU objcopy. + for (StringRef Flag : Config.DumpSection) { + StringRef SectionName; + StringRef FileName; + std::tie(SectionName, FileName) = Flag.split('='); + if (Error E = dumpSectionToFile(SectionName, FileName, Obj)) + return E; + } + // 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 @@ -624,7 +641,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj, const auto Iter = Config.SectionsToRename.find(Sec.Name); if (Iter != Config.SectionsToRename.end()) { const SectionRename &SR = Iter->second; - Sec.Name = SR.NewName; + Sec.Name = std::string(SR.NewName); if (SR.NewFlags.hasValue()) setSectionFlagsAndType(Sec, SR.NewFlags.getValue()); } @@ -717,18 +734,16 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj, 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, Config.GnuDebugLinkCRC32); + // If the symbol table was previously removed, we need to create a new one + // before adding new symbols. + if (!Obj.SymbolTable && !Config.ELF->SymbolsToAdd.empty()) { + Obj.addNewSymbolTable(); + } + for (const NewSymbolInfo &SI : Config.ELF->SymbolsToAdd) { SectionBase *Sec = Obj.findSection(SI.SectionName); uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value; diff --git a/llvm/tools/llvm-objcopy/ELF/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp index ad53c75663ec7..e15fb24f4c425 100644 --- a/llvm/tools/llvm-objcopy/ELF/Object.cpp +++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp @@ -65,6 +65,7 @@ void SectionBase::finalize() {} void SectionBase::markSymbols() {} void SectionBase::replaceSectionReferences( const DenseMap<SectionBase *, SectionBase *> &) {} +void SectionBase::onRemove() {} template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) { uint8_t *B = Buf.getBufferStart() + Sec.HeaderOffset; @@ -111,7 +112,9 @@ void ELFSectionSizer<ELFT>::visit(RelocationSection &Sec) { template <class ELFT> void ELFSectionSizer<ELFT>::visit(GnuDebugLinkSection &Sec) {} -template <class ELFT> void ELFSectionSizer<ELFT>::visit(GroupSection &Sec) {} +template <class ELFT> void ELFSectionSizer<ELFT>::visit(GroupSection &Sec) { + Sec.Size = sizeof(Elf_Word) + Sec.GroupMembers.size() * sizeof(Elf_Word); +} template <class ELFT> void ELFSectionSizer<ELFT>::visit(SectionIndexSection &Sec) {} @@ -605,6 +608,7 @@ static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) { if (Machine == EM_HEXAGON) { switch (Index) { case SHN_HEXAGON_SCOMMON: + case SHN_HEXAGON_SCOMMON_1: case SHN_HEXAGON_SCOMMON_2: case SHN_HEXAGON_SCOMMON_4: case SHN_HEXAGON_SCOMMON_8: @@ -741,7 +745,7 @@ void SymbolTableSection::prepareForLayout() { // 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) + if (SectionIndexTable) SectionIndexTable->reserve(Symbols.size()); // Add all of our strings to SymbolNames so that SymbolNames has the right @@ -963,8 +967,24 @@ Error Section::removeSectionReferences( } void GroupSection::finalize() { - this->Info = Sym->Index; - this->Link = SymTab->Index; + this->Info = Sym ? Sym->Index : 0; + this->Link = SymTab ? SymTab->Index : 0; +} + +Error GroupSection::removeSectionReferences( + bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) { + if (ToRemove(SymTab)) { + if (!AllowBrokenLinks) + return createStringError( + llvm::errc::invalid_argument, + "section '.symtab' cannot be removed because it is " + "referenced by the group section '%s'", + this->Name.data()); + SymTab = nullptr; + Sym = nullptr; + } + llvm::erase_if(GroupMembers, ToRemove); + return Error::success(); } Error GroupSection::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { @@ -988,6 +1008,13 @@ void GroupSection::replaceSectionReferences( Sec = To; } +void GroupSection::onRemove() { + // As the header section of the group is removed, drop the Group flag in its + // former members. + for (SectionBase *Sec : GroupMembers) + Sec->Flags &= ~SHF_GROUP; +} + void Section::initialize(SectionTableRef SecTable) { if (Link == ELF::SHN_UNDEF) return; @@ -1101,14 +1128,6 @@ static bool compareSegmentsByOffset(const Segment *A, const Segment *B) { return A->Index < B->Index; } -static bool compareSegmentsByPAddr(const Segment *A, const Segment *B) { - if (A->PAddr < B->PAddr) - return true; - if (A->PAddr > B->PAddr) - return false; - return A->Index < B->Index; -} - void BasicELFBuilder::initFileHeader() { Obj->Flags = 0x0; Obj->Type = ET_REL; @@ -1241,7 +1260,7 @@ std::unique_ptr<Object> IHexELFBuilder::build() { template <class ELFT> void ELFBuilder<ELFT>::setParentSegment(Segment &Child) { 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. + // be its own parent so we avoid that situation. if (&Child != &Parent && segmentOverlapsSegment(Child, Parent)) { // We want a canonical "most parental" segment but this requires // inspecting the ParentSegment. @@ -1330,18 +1349,20 @@ void ELFBuilder<ELFT>::initGroupSection(GroupSection *GroupSec) { 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"); - 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"); - GroupSec->setSymTab(SymTab); - GroupSec->setSymbol(Sym); + if (GroupSec->Link != SHN_UNDEF) { + 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"); + 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"); + 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"); @@ -1520,7 +1541,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { continue; } auto &Sec = makeSection(Shdr); - Sec.Name = unwrapOrError(ElfFile.getSectionName(&Shdr)); + Sec.Name = std::string(unwrapOrError(ElfFile.getSectionName(&Shdr))); Sec.Type = Sec.OriginalType = Shdr.sh_type; Sec.Flags = Sec.OriginalFlags = Shdr.sh_flags; Sec.Addr = Shdr.sh_addr; @@ -1567,27 +1588,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readSections(bool EnsureSymtab) { Obj.SymbolTable->initialize(Obj.sections()); initSymbolTable(Obj.SymbolTable); } else if (EnsureSymtab) { - // Reuse an existing SHT_STRTAB section if it exists. - StringTableSection *StrTab = nullptr; - for (auto &Sec : Obj.sections()) { - if (Sec.Type == ELF::SHT_STRTAB && !(Sec.Flags & SHF_ALLOC)) { - StrTab = static_cast<StringTableSection *>(&Sec); - - // Prefer a string table that is not the section header string table, if - // such a table exists. - if (Obj.SectionNames != &Sec) - break; - } - } - if (!StrTab) - StrTab = &Obj.addSection<StringTableSection>(); - - SymbolTableSection &SymTab = Obj.addSection<SymbolTableSection>(); - SymTab.Name = ".symtab"; - SymTab.Link = StrTab->Index; - SymTab.initialize(Obj.sections()); - SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0); - Obj.SymbolTable = &SymTab; + Obj.addNewSymbolTable(); } // Now that all sections and symbols have been added we can add @@ -1846,6 +1847,7 @@ Error Object::removeSections(bool AllowBrokenLinks, for (auto &RemoveSec : make_range(Iter, std::end(Sections))) { for (auto &Segment : Segments) Segment->removeSection(RemoveSec.get()); + RemoveSec->onRemove(); RemoveSections.insert(RemoveSec.get()); } @@ -1878,6 +1880,33 @@ Error Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { return Error::success(); } +void Object::addNewSymbolTable() { + assert(!SymbolTable && "Object must not has a SymbolTable."); + + // Reuse an existing SHT_STRTAB section if it exists. + StringTableSection *StrTab = nullptr; + for (SectionBase &Sec : sections()) { + if (Sec.Type == ELF::SHT_STRTAB && !(Sec.Flags & SHF_ALLOC)) { + StrTab = static_cast<StringTableSection *>(&Sec); + + // Prefer a string table that is not the section header string table, if + // such a table exists. + if (SectionNames != &Sec) + break; + } + } + if (!StrTab) + StrTab = &addSection<StringTableSection>(); + + SymbolTableSection &SymTab = addSection<SymbolTableSection>(); + SymTab.Name = ".symtab"; + SymTab.Link = StrTab->Index; + SymTab.initialize(sections()); + SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0); + + SymbolTable = &SymTab; +} + void Object::sortSections() { // Use stable_sort to maintain the original ordering as closely as possible. llvm::stable_sort(Sections, [](const SecPtr &A, const SecPtr &B) { @@ -1902,8 +1931,7 @@ static void orderSegments(std::vector<Segment *> &Segments) { // returns an Offset one past the end of the last segment. static uint64_t layoutSegments(std::vector<Segment *> &Segments, uint64_t Offset) { - assert(std::is_sorted(std::begin(Segments), std::end(Segments), - compareSegmentsByOffset)); + assert(llvm::is_sorted(Segments, compareSegmentsByOffset)); // The only way a segment should move is if a section was between two // segments and that section was removed. If that section isn't in a segment // then it's acceptable, but not ideal, to simply move it to after the @@ -2225,56 +2253,29 @@ Error BinaryWriter::write() { } Error BinaryWriter::finalize() { - // We need a temporary list of segments that has a special order to it - // so that we know that anytime ->ParentSegment is set that segment has - // 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 (const SectionBase &Sec : Obj.allocSections()) - if (Sec.ParentSegment != nullptr) - OrderedSegments.push_back(Sec.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 - // loading and physical addresses are intended for ROM loading. - // However, if no segment has a physical address, we'll fallback to using - // virtual addresses for all. - if (all_of(OrderedSegments, - [](const Segment *Seg) { return Seg->PAddr == 0; })) - for (Segment *Seg : OrderedSegments) - Seg->PAddr = Seg->VAddr; - - 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 - // would do very strange things. - auto End = - std::unique(std::begin(OrderedSegments), std::end(OrderedSegments)); - OrderedSegments.erase(End, std::end(OrderedSegments)); - // Compute the section LMA based on its sh_offset and the containing segment's - // p_offset and p_paddr. Also compute the minimum LMA of all sections as - // MinAddr. In the output, the contents between address 0 and MinAddr will be - // skipped. + // p_offset and p_paddr. Also compute the minimum LMA of all non-empty + // sections as MinAddr. In the output, the contents between address 0 and + // MinAddr will be skipped. uint64_t MinAddr = UINT64_MAX; for (SectionBase &Sec : Obj.allocSections()) { if (Sec.ParentSegment != nullptr) Sec.Addr = Sec.Offset - Sec.ParentSegment->Offset + Sec.ParentSegment->PAddr; - MinAddr = std::min(MinAddr, Sec.Addr); + if (Sec.Size > 0) + MinAddr = std::min(MinAddr, Sec.Addr); } // Now that every section has been laid out we just need to compute the total // file size. This might not be the same as the offset returned by // layoutSections, because we want to truncate the last segment to the end of - // its last section, to match GNU objcopy's behaviour. + // its last non-empty section, to match GNU objcopy's behaviour. TotalSize = 0; - for (SectionBase &Sec : Obj.allocSections()) { - Sec.Offset = Sec.Addr - MinAddr; - if (Sec.Type != SHT_NOBITS) + for (SectionBase &Sec : Obj.allocSections()) + if (Sec.Type != SHT_NOBITS && Sec.Size > 0) { + Sec.Offset = Sec.Addr - MinAddr; TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size); - } + } if (Error E = Buf.allocate(TotalSize)) return E; diff --git a/llvm/tools/llvm-objcopy/ELF/Object.h b/llvm/tools/llvm-objcopy/ELF/Object.h index 97702a66bc479..ed89e916b8385 100644 --- a/llvm/tools/llvm-objcopy/ELF/Object.h +++ b/llvm/tools/llvm-objcopy/ELF/Object.h @@ -424,6 +424,8 @@ public: virtual void markSymbols(); virtual void replaceSectionReferences(const DenseMap<SectionBase *, SectionBase *> &); + // Notify the section that it is subject to removal. + virtual void onRemove(); }; class Segment { @@ -799,10 +801,14 @@ public: void accept(SectionVisitor &) const override; void accept(MutableSectionVisitor &Visitor) override; void finalize() 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; + void onRemove() override; static bool classof(const SectionBase *S) { return S->OriginalType == ELF::SHT_GROUP; @@ -1066,6 +1072,7 @@ public: Ptr->Index = Sections.size(); return *Ptr; } + void addNewSymbolTable(); Segment &addSegment(ArrayRef<uint8_t> Data) { Segments.emplace_back(std::make_unique<Segment>(Data)); return *Segments.back(); diff --git a/llvm/tools/llvm-objcopy/InstallNameToolOpts.td b/llvm/tools/llvm-objcopy/InstallNameToolOpts.td index 35047a57994cb..04ffe62c42fca 100644 --- a/llvm/tools/llvm-objcopy/InstallNameToolOpts.td +++ b/llvm/tools/llvm-objcopy/InstallNameToolOpts.td @@ -18,5 +18,17 @@ def h : Flag<["-"], "h">, Alias<help>; def add_rpath : Option<["-", "--"], "add_rpath", KIND_SEPARATE>, HelpText<"Add new rpath">; +def delete_rpath: Option<["-", "--"], "delete_rpath", KIND_SEPARATE>, + HelpText<"Delete specified rpath">; + +def rpath: MultiArg<["-", "--"], "rpath", 2>, + HelpText<"Change rpath path name">; + +def id : Option<["-","--"], "id", KIND_SEPARATE>, + HelpText<"Change dynamic shared library id">; + +def change: MultiArg<["-", "--"], "change", 2>, + HelpText<"Change dependent shared library install name">; + def version : Flag<["--"], "version">, HelpText<"Print the version and exit.">; diff --git a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp index 380f2e989fe4c..256c830a44a46 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp @@ -17,7 +17,7 @@ namespace macho { uint32_t MachOLayoutBuilder::computeSizeOfCmds() const { uint32_t Size = 0; - for (const auto &LC : O.LoadCommands) { + for (const LoadCommand &LC : O.LoadCommands) { const MachO::macho_load_command &MLC = LC.MachOLoadCommand; auto cmd = MLC.load_command_data.cmd; switch (cmd) { @@ -61,15 +61,16 @@ void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) { assert(MLC.load_command_data.cmd == MachO::LC_DYSYMTAB); // Make sure that nlist entries in the symbol table are sorted by the those // types. The order is: local < defined external < undefined external. - assert(std::is_sorted(O.SymTable.Symbols.begin(), O.SymTable.Symbols.end(), - [](const std::unique_ptr<SymbolEntry> &A, - const std::unique_ptr<SymbolEntry> &B) { - bool AL = A->isLocalSymbol(), BL = B->isLocalSymbol(); - if (AL != BL) - return AL; - return !AL && !A->isUndefinedSymbol() && - B->isUndefinedSymbol(); - }) && + assert(llvm::is_sorted(O.SymTable.Symbols, + [](const std::unique_ptr<SymbolEntry> &A, + const std::unique_ptr<SymbolEntry> &B) { + bool AL = A->isLocalSymbol(), + BL = B->isLocalSymbol(); + if (AL != BL) + return AL; + return !AL && !A->isUndefinedSymbol() && + B->isUndefinedSymbol(); + }) && "Symbols are not sorted by their types."); uint32_t NumLocalSymbols = 0; @@ -107,7 +108,7 @@ uint64_t MachOLayoutBuilder::layoutSegments() { const bool IsObjectFile = O.Header.FileType == MachO::HeaderFileType::MH_OBJECT; uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0; - for (auto &LC : O.LoadCommands) { + for (LoadCommand &LC : O.LoadCommands) { auto &MLC = LC.MachOLoadCommand; StringRef Segname; uint64_t SegmentVmAddr; @@ -142,30 +143,30 @@ uint64_t MachOLayoutBuilder::layoutSegments() { uint64_t SegOffset = Offset; uint64_t SegFileSize = 0; uint64_t VMSize = 0; - for (auto &Sec : LC.Sections) { + for (std::unique_ptr<Section> &Sec : LC.Sections) { + assert(SegmentVmAddr <= Sec->Addr && + "Section's address cannot be smaller than Segment's one"); + uint32_t SectOffset = Sec->Addr - SegmentVmAddr; if (IsObjectFile) { - if (Sec.isVirtualSection()) { - Sec.Offset = 0; + if (Sec->isVirtualSection()) { + Sec->Offset = 0; } else { uint64_t PaddingSize = - offsetToAlignment(SegFileSize, Align(1ull << Sec.Align)); - Sec.Offset = SegOffset + SegFileSize + PaddingSize; - Sec.Size = Sec.Content.size(); - SegFileSize += PaddingSize + Sec.Size; + offsetToAlignment(SegFileSize, Align(1ull << Sec->Align)); + Sec->Offset = SegOffset + SegFileSize + PaddingSize; + Sec->Size = Sec->Content.size(); + SegFileSize += PaddingSize + Sec->Size; } - VMSize = std::max(VMSize, Sec.Addr + Sec.Size); } else { - if (Sec.isVirtualSection()) { - Sec.Offset = 0; - VMSize += Sec.Size; + if (Sec->isVirtualSection()) { + Sec->Offset = 0; } else { - uint32_t SectOffset = Sec.Addr - SegmentVmAddr; - Sec.Offset = SegOffset + SectOffset; - Sec.Size = Sec.Content.size(); - SegFileSize = std::max(SegFileSize, SectOffset + Sec.Size); - VMSize = std::max(VMSize, SegFileSize); + Sec->Offset = SegOffset + SectOffset; + Sec->Size = Sec->Content.size(); + SegFileSize = std::max(SegFileSize, SectOffset + Sec->Size); } } + VMSize = std::max(VMSize, SectOffset + Sec->Size); } if (IsObjectFile) { @@ -204,21 +205,33 @@ uint64_t MachOLayoutBuilder::layoutSegments() { } uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) { - 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; + for (LoadCommand &LC : O.LoadCommands) + for (std::unique_ptr<Section> &Sec : LC.Sections) { + Sec->RelOff = Sec->Relocations.empty() ? 0 : Offset; + Sec->NReloc = Sec->Relocations.size(); + Offset += sizeof(MachO::any_relocation_info) * Sec->NReloc; } return Offset; } Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { + // If we are building the layout of an executable or dynamic library + // which does not have any segments other than __LINKEDIT, + // the Offset can be equal to zero by this time. It happens because of the + // convention that in such cases the file offsets specified by LC_SEGMENT + // start with zero (unlike the case of a relocatable object file). + const uint64_t HeaderSize = + Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); + assert((!(O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) || + Offset >= HeaderSize + O.Header.SizeOfCmds) && + "Incorrect tail offset"); + Offset = std::max(Offset, HeaderSize + O.Header.SizeOfCmds); + // The order of LINKEDIT elements is as follows: // rebase info, binding info, weak binding info, lazy binding info, export // trie, data-in-code, symbol table, indirect symbol table, symbol table - // strings. + // strings, code signature. uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); uint64_t StartOfLinkEdit = Offset; uint64_t StartOfRebaseInfo = StartOfLinkEdit; @@ -237,8 +250,10 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { uint64_t StartOfSymbolStrings = StartOfIndirectSymbols + sizeof(uint32_t) * O.IndirectSymTable.Symbols.size(); + uint64_t StartOfCodeSignature = + StartOfSymbolStrings + StrTableBuilder.getSize(); uint64_t LinkEditSize = - (StartOfSymbolStrings + StrTableBuilder.getSize()) - StartOfLinkEdit; + (StartOfCodeSignature + O.CodeSignature.Data.size()) - StartOfLinkEdit; // Now we have determined the layout of the contents of the __LINKEDIT // segment. Update its load command. @@ -260,10 +275,14 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { } } - for (auto &LC : O.LoadCommands) { + for (LoadCommand &LC : O.LoadCommands) { auto &MLC = LC.MachOLoadCommand; auto cmd = MLC.load_command_data.cmd; switch (cmd) { + case MachO::LC_CODE_SIGNATURE: + MLC.linkedit_data_command_data.dataoff = StartOfCodeSignature; + MLC.linkedit_data_command_data.datasize = O.CodeSignature.Data.size(); + break; case MachO::LC_SYMTAB: MLC.symtab_command_data.symoff = StartOfSymbols; MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size(); @@ -314,6 +333,19 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { O.Exports.Trie.empty() ? 0 : StartOfExportTrie; MLC.dyld_info_command_data.export_size = O.Exports.Trie.size(); break; + // Note that LC_ENCRYPTION_INFO.cryptoff despite its name and the comment in + // <mach-o/loader.h> is not an offset in the binary file, instead, it is a + // relative virtual address. At the moment modification of the __TEXT + // segment of executables isn't supported anyway (e.g. data in code entries + // are not recalculated). Moreover, in general + // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 are nontrivial to update because + // without making additional assumptions (e.g. that the entire __TEXT + // segment should be encrypted) we do not know how to recalculate the + // boundaries of the encrypted part. For now just copy over these load + // commands until we encounter a real world usecase where + // LC_ENCRYPT_INFO/LC_ENCRYPTION_INFO_64 need to be adjusted. + case MachO::LC_ENCRYPTION_INFO: + case MachO::LC_ENCRYPTION_INFO_64: case MachO::LC_LOAD_DYLINKER: case MachO::LC_MAIN: case MachO::LC_RPATH: @@ -326,6 +358,7 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { case MachO::LC_BUILD_VERSION: case MachO::LC_ID_DYLIB: case MachO::LC_LOAD_DYLIB: + case MachO::LC_LOAD_WEAK_DYLIB: case MachO::LC_UUID: case MachO::LC_SOURCE_VERSION: // Nothing to update. diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp index 4578d0bb75d4f..5ca5b133572b4 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -10,6 +10,7 @@ #include "../CopyConfig.h" #include "MachOReader.h" #include "MachOWriter.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" @@ -18,21 +19,44 @@ namespace objcopy { namespace macho { using namespace object; -using SectionPred = std::function<bool(const Section &Sec)>; +using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>; +using LoadCommandPred = std::function<bool(const LoadCommand &LC)>; + +#ifndef NDEBUG +static bool isLoadCommandWithPayloadString(const LoadCommand &LC) { + // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and + // LC_LAZY_LOAD_DYLIB + return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH || + LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB || + LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_DYLIB || + LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_WEAK_DYLIB; +} +#endif + +static StringRef getPayloadString(const LoadCommand &LC) { + assert(isLoadCommandWithPayloadString(LC) && + "unsupported load command encountered"); + + return StringRef(reinterpret_cast<const char *>(LC.Payload.data()), + LC.Payload.size()) + .rtrim('\0'); +} -static void removeSections(const CopyConfig &Config, Object &Obj) { - SectionPred RemovePred = [](const Section &) { return false; }; +static Error removeSections(const CopyConfig &Config, Object &Obj) { + SectionPred RemovePred = [](const std::unique_ptr<Section> &) { + return false; + }; if (!Config.ToRemove.empty()) { - RemovePred = [&Config, RemovePred](const Section &Sec) { - return Config.ToRemove.matches(Sec.CanonicalName); + RemovePred = [&Config, RemovePred](const std::unique_ptr<Section> &Sec) { + return Config.ToRemove.matches(Sec->CanonicalName); }; } if (Config.StripAll || Config.StripDebug) { // Remove all debug sections. - RemovePred = [RemovePred](const Section &Sec) { - if (Sec.Segname == "__DWARF") + RemovePred = [RemovePred](const std::unique_ptr<Section> &Sec) { + if (Sec->Segname == "__DWARF") return true; return RemovePred(Sec); @@ -41,8 +65,8 @@ static void removeSections(const CopyConfig &Config, Object &Obj) { if (!Config.OnlySection.empty()) { // Overwrite RemovePred because --only-section takes priority. - RemovePred = [&Config](const Section &Sec) { - return !Config.OnlySection.matches(Sec.CanonicalName); + RemovePred = [&Config](const std::unique_ptr<Section> &Sec) { + return !Config.OnlySection.matches(Sec->CanonicalName); }; } @@ -60,41 +84,158 @@ static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { for (SymbolEntry &Sym : Obj.SymTable) { auto I = Config.SymbolsToRename.find(Sym.Name); if (I != Config.SymbolsToRename.end()) - Sym.Name = I->getValue(); + Sym.Name = std::string(I->getValue()); } - auto RemovePred = [Config](const std::unique_ptr<SymbolEntry> &N) { + auto RemovePred = [Config, &Obj](const std::unique_ptr<SymbolEntry> &N) { if (N->Referenced) return false; - return Config.StripAll; + if (Config.StripAll) + return true; + if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT)) + return true; + // This behavior is consistent with cctools' strip. + if (Config.StripSwiftSymbols && (Obj.Header.Flags & MachO::MH_DYLDLINK) && + Obj.SwiftVersion && *Obj.SwiftVersion && N->isSwiftSymbol()) + return true; + return false; }; Obj.SymTable.removeSymbols(RemovePred); } +template <typename LCType> +static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) { + assert(isLoadCommandWithPayloadString(LC) && + "unsupported load command encountered"); + + uint32_t NewCmdsize = alignTo(sizeof(LCType) + S.size() + 1, 8); + + LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize; + LC.Payload.assign(NewCmdsize - sizeof(LCType), 0); + std::copy(S.begin(), S.end(), LC.Payload.begin()); +} + static LoadCommand buildRPathLoadCommand(StringRef Path) { LoadCommand LC; MachO::rpath_command RPathLC; RPathLC.cmd = MachO::LC_RPATH; RPathLC.path = sizeof(MachO::rpath_command); - RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size(), 8); + RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size() + 1, 8); LC.MachOLoadCommand.rpath_command_data = RPathLC; LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0); std::copy(Path.begin(), Path.end(), LC.Payload.begin()); return LC; } +static Error processLoadCommands(const CopyConfig &Config, Object &Obj) { + // Remove RPaths. + DenseSet<StringRef> RPathsToRemove(Config.RPathsToRemove.begin(), + Config.RPathsToRemove.end()); + + LoadCommandPred RemovePred = [&RPathsToRemove](const LoadCommand &LC) { + if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) { + StringRef RPath = getPayloadString(LC); + if (RPathsToRemove.count(RPath)) { + RPathsToRemove.erase(RPath); + return true; + } + } + return false; + }; + + if (Error E = Obj.removeLoadCommands(RemovePred)) + return E; + + // Emit an error if the Mach-O binary does not contain an rpath path name + // specified in -delete_rpath. + for (StringRef RPath : Config.RPathsToRemove) { + if (RPathsToRemove.count(RPath)) + return createStringError(errc::invalid_argument, + "no LC_RPATH load command with path: %s", + RPath.str().c_str()); + } + + DenseSet<StringRef> RPaths; + + // Get all existing RPaths. + for (LoadCommand &LC : Obj.LoadCommands) { + if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) + RPaths.insert(getPayloadString(LC)); + } + + // Throw errors for invalid RPaths. + for (const auto &OldNew : Config.RPathsToUpdate) { + StringRef Old = OldNew.getFirst(); + StringRef New = OldNew.getSecond(); + if (RPaths.count(Old) == 0) + return createStringError(errc::invalid_argument, + "no LC_RPATH load command with path: " + Old); + if (RPaths.count(New) != 0) + return createStringError(errc::invalid_argument, + "rpath " + New + + " would create a duplicate load command"); + } + + // Update load commands. + for (LoadCommand &LC : Obj.LoadCommands) { + switch (LC.MachOLoadCommand.load_command_data.cmd) { + case MachO::LC_ID_DYLIB: + if (Config.SharedLibId) { + StringRef Id = Config.SharedLibId.getValue(); + if (Id.empty()) + return createStringError(errc::invalid_argument, + "cannot specify an empty id"); + updateLoadCommandPayloadString<MachO::dylib_command>(LC, Id); + } + break; + + case MachO::LC_RPATH: { + StringRef RPath = getPayloadString(LC); + StringRef NewRPath = Config.RPathsToUpdate.lookup(RPath); + if (!NewRPath.empty()) + updateLoadCommandPayloadString<MachO::rpath_command>(LC, NewRPath); + break; + } + + // TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB + // here once llvm-objcopy supports them. + case MachO::LC_LOAD_DYLIB: + case MachO::LC_LOAD_WEAK_DYLIB: + StringRef InstallName = getPayloadString(LC); + StringRef NewInstallName = + Config.InstallNamesToUpdate.lookup(InstallName); + if (!NewInstallName.empty()) + updateLoadCommandPayloadString<MachO::dylib_command>(LC, + NewInstallName); + break; + } + } + + // Add new RPaths. + for (StringRef RPath : Config.RPathToAdd) { + if (RPaths.count(RPath) != 0) + return createStringError(errc::invalid_argument, + "rpath " + RPath + + " would create a duplicate load command"); + RPaths.insert(RPath); + Obj.addLoadCommand(buildRPathLoadCommand(RPath)); + } + + return Error::success(); +} + static Error dumpSectionToFile(StringRef SecName, StringRef Filename, Object &Obj) { for (LoadCommand &LC : Obj.LoadCommands) - for (Section &Sec : LC.Sections) { - if (Sec.CanonicalName == SecName) { + for (const std::unique_ptr<Section> &Sec : LC.Sections) { + if (Sec->CanonicalName == SecName) { Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = - FileOutputBuffer::create(Filename, Sec.Content.size()); + FileOutputBuffer::create(Filename, Sec->Content.size()); if (!BufferOrErr) return BufferOrErr.takeError(); std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); - llvm::copy(Sec.Content, Buf->getBufferStart()); + llvm::copy(Sec->Content, Buf->getBufferStart()); if (Error E = Buf->commit()) return E; @@ -122,7 +263,7 @@ static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) { for (LoadCommand &LC : Obj.LoadCommands) { Optional<StringRef> SegName = LC.getSegmentName(); if (SegName && SegName == TargetSegName) { - LC.Sections.push_back(Sec); + LC.Sections.push_back(std::make_unique<Section>(Sec)); return Error::success(); } } @@ -130,7 +271,7 @@ static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) { // There's no segment named TargetSegName. Create a new load command and // Insert a new section into it. LoadCommand &NewSegment = Obj.addSegment(TargetSegName); - NewSegment.Sections.push_back(Sec); + NewSegment.Sections.push_back(std::make_unique<Section>(Sec)); return Error::success(); } @@ -167,17 +308,27 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { !Config.SectionsToRename.empty() || !Config.UnneededSymbolsToRemove.empty() || !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || - Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || - Config.PreserveDates || Config.StripAllGNU || Config.StripDWO || - Config.StripNonAlloc || Config.StripSections || Config.Weaken || - Config.DecompressDebugSections || Config.StripNonAlloc || - Config.StripSections || Config.StripUnneeded || - Config.DiscardMode != DiscardType::None || !Config.SymbolsToAdd.empty() || - Config.EntryExpr) { + Config.ExtractDWO || Config.LocalizeHidden || Config.PreserveDates || + Config.StripAllGNU || Config.StripDWO || Config.StripNonAlloc || + Config.StripSections || Config.Weaken || Config.DecompressDebugSections || + Config.StripNonAlloc || Config.StripSections || Config.StripUnneeded || + Config.DiscardMode == DiscardType::Locals || + !Config.SymbolsToAdd.empty() || Config.EntryExpr) { return createStringError(llvm::errc::invalid_argument, "option not supported by llvm-objcopy for MachO"); } - removeSections(Config, Obj); + + // Dump sections before add/remove for compatibility with GNU objcopy. + for (StringRef Flag : Config.DumpSection) { + StringRef SectionName; + StringRef FileName; + std::tie(SectionName, FileName) = Flag.split('='); + if (Error E = dumpSectionToFile(SectionName, FileName, Obj)) + return E; + } + + if (Error E = removeSections(Config, Obj)) + return E; // Mark symbols to determine which symbols are still needed. if (Config.StripAll) @@ -187,16 +338,8 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { if (Config.StripAll) for (LoadCommand &LC : Obj.LoadCommands) - for (Section &Sec : LC.Sections) - Sec.Relocations.clear(); - - for (const StringRef &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; - } + for (std::unique_ptr<Section> &Sec : LC.Sections) + Sec->Relocations.clear(); for (const auto &Flag : Config.AddSection) { std::pair<StringRef, StringRef> SecPair = Flag.split("="); @@ -208,19 +351,9 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { return E; } - for (StringRef RPath : Config.RPathToAdd) { - for (LoadCommand &LC : Obj.LoadCommands) { - if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH && - RPath == StringRef(reinterpret_cast<char *>(LC.Payload.data()), - LC.Payload.size()) - .trim(0)) { - return createStringError(errc::invalid_argument, - "rpath " + RPath + - " would create a duplicate load command"); - } - } - Obj.addLoadCommand(buildRPathLoadCommand(RPath)); - } + if (Error E = processLoadCommands(Config, Obj)) + return E; + return Error::success(); } @@ -237,9 +370,18 @@ Error executeObjcopyOnBinary(const CopyConfig &Config, if (Error E = handleArgs(Config, *O)) return createFileError(Config.InputFilename, std::move(E)); - // TODO: Support 16KB pages which are employed in iOS arm64 binaries: - // https://github.com/llvm/llvm-project/commit/1bebb2832ee312d3b0316dacff457a7a29435edb - const uint64_t PageSize = 4096; + // Page size used for alignment of segment sizes in Mach-O executables and + // dynamic libraries. + uint64_t PageSize; + switch (In.getArch()) { + case Triple::ArchType::arm: + case Triple::ArchType::aarch64: + case Triple::ArchType::aarch64_32: + PageSize = 16384; + break; + default: + PageSize = 4096; + } MachOWriter Writer(*O, In.is64Bit(), In.isLittleEndian(), PageSize, Out); if (auto E = Writer.finalize()) diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp index 46bb117273229..99bcec7f6b51e 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp @@ -28,10 +28,11 @@ void MachOReader::readHeader(Object &O) const { } template <typename SectionType> -Section constructSectionCommon(SectionType Sec) { +Section constructSectionCommon(SectionType Sec, uint32_t Index) { StringRef SegName(Sec.segname, strnlen(Sec.segname, sizeof(Sec.segname))); StringRef SectName(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname))); Section S(SegName, SectName); + S.Index = Index; S.Addr = Sec.addr; S.Size = Sec.size; S.Offset = Sec.offset; @@ -45,39 +46,42 @@ Section constructSectionCommon(SectionType Sec) { return S; } -template <typename SectionType> Section constructSection(SectionType Sec); +template <typename SectionType> +Section constructSection(SectionType Sec, uint32_t Index); -template <> Section constructSection(MachO::section Sec) { - return constructSectionCommon(Sec); +template <> Section constructSection(MachO::section Sec, uint32_t Index) { + return constructSectionCommon(Sec, Index); } -template <> Section constructSection(MachO::section_64 Sec) { - Section S = constructSectionCommon(Sec); +template <> Section constructSection(MachO::section_64 Sec, uint32_t Index) { + Section S = constructSectionCommon(Sec, Index); 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> +std::vector<std::unique_ptr<Section>> extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, const object::MachOObjectFile &MachOObj, - size_t &NextSectionIndex) { + uint32_t &NextSectionIndex) { auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize; const SectionType *Curr = reinterpret_cast<const SectionType *>(LoadCmd.Ptr + sizeof(SegmentType)); - std::vector<Section> Sections; + std::vector<std::unique_ptr<Section>> Sections; for (; reinterpret_cast<const void *>(Curr) < End; Curr++) { if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { SectionType Sec; memcpy((void *)&Sec, Curr, sizeof(SectionType)); MachO::swapStruct(Sec); - Sections.push_back(constructSection(Sec)); + Sections.push_back( + std::make_unique<Section>(constructSection(Sec, NextSectionIndex))); } else { - Sections.push_back(constructSection(*Curr)); + Sections.push_back( + std::make_unique<Section>(constructSection(*Curr, NextSectionIndex))); } - Section &S = Sections.back(); + Section &S = *Sections.back(); Expected<object::SectionRef> SecRef = MachOObj.getSection(NextSectionIndex++); @@ -99,6 +103,7 @@ extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, R.Symbol = nullptr; // We'll fill this field later. R.Info = MachOObj.getRelocation(RI->getRawDataRefImpl()); R.Scattered = MachOObj.isRelocationScattered(R.Info); + R.Extern = !R.Scattered && MachOObj.getPlainRelocationExternal(R.Info); S.Relocations.push_back(R); } @@ -110,10 +115,13 @@ extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, void MachOReader::readLoadCommands(Object &O) const { // For MachO sections indices start from 1. - size_t NextSectionIndex = 1; + uint32_t NextSectionIndex = 1; for (auto LoadCmd : MachOObj.load_commands()) { LoadCommand LC; switch (LoadCmd.C.cmd) { + case MachO::LC_CODE_SIGNATURE: + O.CodeSignatureCommandIndex = O.LoadCommands.size(); + break; case MachO::LC_SEGMENT: LC.Sections = extractSections<MachO::section, MachO::segment_command>( LoadCmd, MachOObj, NextSectionIndex); @@ -189,24 +197,36 @@ void MachOReader::readSymbolTable(Object &O) const { for (auto Symbol : MachOObj.symbols()) { SymbolEntry SE = (MachOObj.is64Bit() - ? constructSymbolEntry( - StrTable, - MachOObj.getSymbol64TableEntry(Symbol.getRawDataRefImpl())) - : constructSymbolEntry( - StrTable, - MachOObj.getSymbolTableEntry(Symbol.getRawDataRefImpl()))); + ? constructSymbolEntry(StrTable, MachOObj.getSymbol64TableEntry( + Symbol.getRawDataRefImpl())) + : constructSymbolEntry(StrTable, MachOObj.getSymbolTableEntry( + Symbol.getRawDataRefImpl()))); O.SymTable.Symbols.push_back(std::make_unique<SymbolEntry>(SE)); } } void MachOReader::setSymbolInRelocationInfo(Object &O) const { + std::vector<const Section *> Sections; for (auto &LC : O.LoadCommands) - for (auto &Sec : LC.Sections) - for (auto &Reloc : Sec.Relocations) + for (std::unique_ptr<Section> &Sec : LC.Sections) + Sections.push_back(Sec.get()); + + for (LoadCommand &LC : O.LoadCommands) + for (std::unique_ptr<Section> &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); + const uint32_t SymbolNum = + Reloc.getPlainRelocationSymbolNum(MachOObj.isLittleEndian()); + if (Reloc.Extern) { + Reloc.Symbol = O.SymTable.getSymbolByIndex(SymbolNum); + } else { + // FIXME: Refactor error handling in MachOReader and report an error + // if we encounter an invalid relocation. + assert(SymbolNum >= 1 && SymbolNum <= Sections.size() && + "Invalid section index."); + Reloc.Sec = Sections[SymbolNum - 1]; + } } } @@ -230,26 +250,26 @@ void MachOReader::readExportInfo(Object &O) const { O.Exports.Trie = MachOObj.getDyldInfoExportsTrie(); } -void MachOReader::readDataInCodeData(Object &O) const { - if (!O.DataInCodeCommandIndex) +void MachOReader::readLinkData(Object &O, Optional<size_t> LCIndex, + LinkData &LD) const { + if (!LCIndex) return; - const MachO::linkedit_data_command &LDC = - O.LoadCommands[*O.DataInCodeCommandIndex] - .MachOLoadCommand.linkedit_data_command_data; + const MachO::linkedit_data_command &LC = + O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data; + LD.Data = + arrayRefFromStringRef(MachOObj.getData().substr(LC.dataoff, LC.datasize)); +} - O.DataInCode.Data = arrayRefFromStringRef( - MachOObj.getData().substr(LDC.dataoff, LDC.datasize)); +void MachOReader::readCodeSignature(Object &O) const { + return readLinkData(O, O.CodeSignatureCommandIndex, O.CodeSignature); } -void MachOReader::readFunctionStartsData(Object &O) const { - if (!O.FunctionStartsCommandIndex) - return; - const MachO::linkedit_data_command &LDC = - O.LoadCommands[*O.FunctionStartsCommandIndex] - .MachOLoadCommand.linkedit_data_command_data; +void MachOReader::readDataInCodeData(Object &O) const { + return readLinkData(O, O.DataInCodeCommandIndex, O.DataInCode); +} - O.FunctionStarts.Data = arrayRefFromStringRef( - MachOObj.getData().substr(LDC.dataoff, LDC.datasize)); +void MachOReader::readFunctionStartsData(Object &O) const { + return readLinkData(O, O.FunctionStartsCommandIndex, O.FunctionStarts); } void MachOReader::readIndirectSymbolTable(Object &O) const { @@ -266,6 +286,28 @@ void MachOReader::readIndirectSymbolTable(Object &O) const { } } +void MachOReader::readSwiftVersion(Object &O) const { + struct ObjCImageInfo { + uint32_t Version; + uint32_t Flags; + } ImageInfo; + + for (const LoadCommand &LC : O.LoadCommands) + for (const std::unique_ptr<Section> &Sec : LC.Sections) + if (Sec->Sectname == "__objc_imageinfo" && + (Sec->Segname == "__DATA" || Sec->Segname == "__DATA_CONST" || + Sec->Segname == "__DATA_DIRTY") && + Sec->Content.size() >= sizeof(ObjCImageInfo)) { + memcpy(&ImageInfo, Sec->Content.data(), sizeof(ObjCImageInfo)); + if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) { + sys::swapByteOrder(ImageInfo.Version); + sys::swapByteOrder(ImageInfo.Flags); + } + O.SwiftVersion = (ImageInfo.Flags >> 8) & 0xff; + return; + } +} + std::unique_ptr<Object> MachOReader::create() const { auto Obj = std::make_unique<Object>(); readHeader(*Obj); @@ -277,9 +319,11 @@ std::unique_ptr<Object> MachOReader::create() const { readWeakBindInfo(*Obj); readLazyBindInfo(*Obj); readExportInfo(*Obj); + readCodeSignature(*Obj); readDataInCodeData(*Obj); readFunctionStartsData(*Obj); readIndirectSymbolTable(*Obj); + readSwiftVersion(*Obj); return Obj; } diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.h b/llvm/tools/llvm-objcopy/MachO/MachOReader.h index 00c8f0d55f61f..65824b6eb3893 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOReader.h +++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.h @@ -36,9 +36,12 @@ class MachOReader : public Reader { void readWeakBindInfo(Object &O) const; void readLazyBindInfo(Object &O) const; void readExportInfo(Object &O) const; + void readLinkData(Object &O, Optional<size_t> LCIndex, LinkData &LD) const; + void readCodeSignature(Object &O) const; void readDataInCodeData(Object &O) const; void readFunctionStartsData(Object &O) const; void readIndirectSymbolTable(Object &O) const; + void readSwiftVersion(Object &O) const; public: explicit MachOReader(const object::MachOObjectFile &Obj) : MachOObj(Obj) {} diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp index 0d9590612eca0..3c41e73b2b013 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp @@ -89,6 +89,15 @@ size_t MachOWriter::totalSize() const { sizeof(uint32_t) * O.IndirectSymTable.Symbols.size()); } + if (O.CodeSignatureCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.CodeSignatureCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + if (LinkEditDataCommand.dataoff) + Ends.push_back(LinkEditDataCommand.dataoff + + LinkEditDataCommand.datasize); + } + if (O.DataInCodeCommandIndex) { const MachO::linkedit_data_command &LinkEditDataCommand = O.LoadCommands[*O.DataInCodeCommandIndex] @@ -110,12 +119,12 @@ size_t MachOWriter::totalSize() const { } // 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)); + for (const LoadCommand &LC : O.LoadCommands) + for (const std::unique_ptr<Section> &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()) @@ -147,7 +156,7 @@ void MachOWriter::writeHeader() { void MachOWriter::writeLoadCommands() { uint8_t *Begin = B.getBufferStart() + headerSize(); - for (const auto &LC : O.LoadCommands) { + for (const LoadCommand &LC : O.LoadCommands) { // Construct a load command. MachO::macho_load_command MLC = LC.MachOLoadCommand; switch (MLC.load_command_data.cmd) { @@ -157,8 +166,8 @@ void MachOWriter::writeLoadCommands() { 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); + for (const std::unique_ptr<Section> &Sec : LC.Sections) + writeSectionInLoadCommand<MachO::section>(*Sec, Begin); continue; case MachO::LC_SEGMENT_64: if (IsLittleEndian != sys::IsLittleEndianHost) @@ -167,8 +176,8 @@ void MachOWriter::writeLoadCommands() { sizeof(MachO::segment_command_64)); Begin += sizeof(MachO::segment_command_64); - for (const auto &Sec : LC.Sections) - writeSectionInLoadCommand<MachO::section_64>(Sec, Begin); + for (const std::unique_ptr<Section> &Sec : LC.Sections) + writeSectionInLoadCommand<MachO::section_64>(*Sec, Begin); continue; } @@ -229,27 +238,27 @@ void MachOWriter::writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out) { } void MachOWriter::writeSections() { - for (const auto &LC : O.LoadCommands) - for (const auto &Sec : LC.Sections) { - if (Sec.isVirtualSection()) + for (const LoadCommand &LC : O.LoadCommands) + for (const std::unique_ptr<Section> &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]; + 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) { + RelocationInfo RelocInfo = Sec->Relocations[Index]; if (!RelocInfo.Scattered) { - auto *Info = - reinterpret_cast<MachO::relocation_info *>(&RelocInfo.Info); - Info->r_symbolnum = RelocInfo.Symbol->Index; + const uint32_t SymbolNum = RelocInfo.Extern + ? (*RelocInfo.Symbol)->Index + : (*RelocInfo.Sec)->Index; + RelocInfo.setPlainRelocationSymbolNum(SymbolNum, IsLittleEndian); } - if (IsLittleEndian != sys::IsLittleEndianHost) MachO::swapStruct( reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info)); - memcpy(B.getBufferStart() + Sec.RelOff + + memcpy(B.getBufferStart() + Sec->RelOff + Index * sizeof(MachO::any_relocation_info), &RelocInfo.Info, sizeof(RelocInfo.Info)); } @@ -381,28 +390,27 @@ void MachOWriter::writeIndirectSymbolTable() { } } -void MachOWriter::writeDataInCodeData() { - if (!O.DataInCodeCommandIndex) +void MachOWriter::writeLinkData(Optional<size_t> LCIndex, const LinkData &LD) { + if (!LCIndex) return; const MachO::linkedit_data_command &LinkEditDataCommand = - O.LoadCommands[*O.DataInCodeCommandIndex] - .MachOLoadCommand.linkedit_data_command_data; + O.LoadCommands[*LCIndex].MachOLoadCommand.linkedit_data_command_data; char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff; - assert((LinkEditDataCommand.datasize == O.DataInCode.Data.size()) && - "Incorrect data in code data size"); - memcpy(Out, O.DataInCode.Data.data(), O.DataInCode.Data.size()); + assert((LinkEditDataCommand.datasize == LD.Data.size()) && + "Incorrect data size"); + memcpy(Out, LD.Data.data(), LD.Data.size()); +} + +void MachOWriter::writeCodeSignatureData() { + return writeLinkData(O.CodeSignatureCommandIndex, O.CodeSignature); +} + +void MachOWriter::writeDataInCodeData() { + return writeLinkData(O.DataInCodeCommandIndex, O.DataInCode); } void MachOWriter::writeFunctionStartsData() { - if (!O.FunctionStartsCommandIndex) - return; - const MachO::linkedit_data_command &LinkEditDataCommand = - O.LoadCommands[*O.FunctionStartsCommandIndex] - .MachOLoadCommand.linkedit_data_command_data; - char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff; - assert((LinkEditDataCommand.datasize == O.FunctionStarts.Data.size()) && - "Incorrect function starts data size"); - memcpy(Out, O.FunctionStarts.Data.data(), O.FunctionStarts.Data.size()); + return writeLinkData(O.FunctionStartsCommandIndex, O.FunctionStarts); } void MachOWriter::writeTail() { @@ -450,6 +458,16 @@ void MachOWriter::writeTail() { &MachOWriter::writeIndirectSymbolTable); } + if (O.CodeSignatureCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.CodeSignatureCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Queue.emplace_back(LinkEditDataCommand.dataoff, + &MachOWriter::writeCodeSignatureData); + } + if (O.DataInCodeCommandIndex) { const MachO::linkedit_data_command &LinkEditDataCommand = O.LoadCommands[*O.DataInCodeCommandIndex] diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h index 22abbad56f41c..c2c6f5a55e9af 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h +++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h @@ -45,6 +45,8 @@ class MachOWriter { void writeLazyBindInfo(); void writeExportInfo(); void writeIndirectSymbolTable(); + void writeLinkData(Optional<size_t> LCIndex, const LinkData &LD); + void writeCodeSignatureData(); void writeDataInCodeData(); void writeFunctionStartsData(); void writeTail(); diff --git a/llvm/tools/llvm-objcopy/MachO/Object.cpp b/llvm/tools/llvm-objcopy/MachO/Object.cpp index d3b4fdc2f6338..de8cb0af108d7 100644 --- a/llvm/tools/llvm-objcopy/MachO/Object.cpp +++ b/llvm/tools/llvm-objcopy/MachO/Object.cpp @@ -1,5 +1,15 @@ +//===- Object.cpp - 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 +// +//===----------------------------------------------------------------------===// + #include "Object.h" #include "../llvm-objcopy.h" +#include "llvm/ADT/SmallPtrSet.h" +#include <unordered_set> namespace llvm { namespace objcopy { @@ -22,11 +32,83 @@ void SymbolTable::removeSymbols( std::end(Symbols)); } -void Object::removeSections(function_ref<bool(const Section &)> ToRemove) { - for (LoadCommand &LC : LoadCommands) - LC.Sections.erase(std::remove_if(std::begin(LC.Sections), - std::end(LC.Sections), ToRemove), - std::end(LC.Sections)); +void Object::updateLoadCommandIndexes() { + // Update indices of special load commands + for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) { + LoadCommand &LC = LoadCommands[Index]; + switch (LC.MachOLoadCommand.load_command_data.cmd) { + case MachO::LC_SYMTAB: + SymTabCommandIndex = Index; + break; + case MachO::LC_DYSYMTAB: + DySymTabCommandIndex = Index; + break; + case MachO::LC_DYLD_INFO: + case MachO::LC_DYLD_INFO_ONLY: + DyLdInfoCommandIndex = Index; + break; + case MachO::LC_DATA_IN_CODE: + DataInCodeCommandIndex = Index; + break; + case MachO::LC_FUNCTION_STARTS: + FunctionStartsCommandIndex = Index; + break; + } + } +} + +Error Object::removeLoadCommands( + function_ref<bool(const LoadCommand &)> ToRemove) { + auto It = std::stable_partition( + LoadCommands.begin(), LoadCommands.end(), + [&](const LoadCommand &LC) { return !ToRemove(LC); }); + LoadCommands.erase(It, LoadCommands.end()); + + updateLoadCommandIndexes(); + return Error::success(); +} + +Error Object::removeSections( + function_ref<bool(const std::unique_ptr<Section> &)> ToRemove) { + DenseMap<uint32_t, const Section *> OldIndexToSection; + uint32_t NextSectionIndex = 1; + for (LoadCommand &LC : LoadCommands) { + auto It = std::stable_partition( + std::begin(LC.Sections), std::end(LC.Sections), + [&](const std::unique_ptr<Section> &Sec) { return !ToRemove(Sec); }); + for (auto I = LC.Sections.begin(), End = It; I != End; ++I) { + OldIndexToSection[(*I)->Index] = I->get(); + (*I)->Index = NextSectionIndex++; + } + LC.Sections.erase(It, LC.Sections.end()); + } + + auto IsDead = [&](const std::unique_ptr<SymbolEntry> &S) -> bool { + Optional<uint32_t> Section = S->section(); + return (Section && !OldIndexToSection.count(*Section)); + }; + + SmallPtrSet<const SymbolEntry *, 2> DeadSymbols; + for (const std::unique_ptr<SymbolEntry> &Sym : SymTable.Symbols) + if (IsDead(Sym)) + DeadSymbols.insert(Sym.get()); + + for (const LoadCommand &LC : LoadCommands) + for (const std::unique_ptr<Section> &Sec : LC.Sections) + for (const RelocationInfo &R : Sec->Relocations) + if (R.Symbol && *R.Symbol && DeadSymbols.count(*R.Symbol)) + return createStringError(std::errc::invalid_argument, + "symbol '%s' defined in section with index " + "'%u' cannot be removed because it is " + "referenced by a relocation in section '%s'", + (*R.Symbol)->Name.c_str(), + *((*R.Symbol)->section()), + Sec->CanonicalName.c_str()); + SymTable.removeSymbols(IsDead); + for (std::unique_ptr<SymbolEntry> &S : SymTable.Symbols) + if (S->section()) + S->n_sect = OldIndexToSection[S->n_sect]->Index; + return Error::success(); } void Object::addLoadCommand(LoadCommand LC) { @@ -52,7 +134,7 @@ LoadCommand &Object::addSegment(StringRef SegName) { constructSegment(LC.MachOLoadCommand.segment_command_data, MachO::LC_SEGMENT, SegName); - LoadCommands.push_back(LC); + LoadCommands.push_back(std::move(LC)); return LoadCommands.back(); } diff --git a/llvm/tools/llvm-objcopy/MachO/Object.h b/llvm/tools/llvm-objcopy/MachO/Object.h index dc2606eefa4a2..e825d1867b09a 100644 --- a/llvm/tools/llvm-objcopy/MachO/Object.h +++ b/llvm/tools/llvm-objcopy/MachO/Object.h @@ -37,6 +37,7 @@ struct MachHeader { struct RelocationInfo; struct Section { + uint32_t Index; std::string Segname; std::string Sectname; // CanonicalName is a string formatted as “<Segname>,<Sectname>". @@ -55,11 +56,11 @@ struct Section { std::vector<RelocationInfo> Relocations; Section(StringRef SegName, StringRef SectName) - : Segname(SegName), Sectname(SectName), + : Segname(std::string(SegName)), Sectname(std::string(SectName)), CanonicalName((Twine(SegName) + Twine(',') + SectName).str()) {} Section(StringRef SegName, StringRef SectName, StringRef Content) - : Segname(SegName), Sectname(SectName), + : Segname(std::string(SegName)), Sectname(std::string(SectName)), CanonicalName((Twine(SegName) + Twine(',') + SectName).str()), Content(Content) {} @@ -83,13 +84,13 @@ struct LoadCommand { // 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. - std::vector<uint8_t> Payload; + std::vector<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; + std::vector<std::unique_ptr<Section>> Sections; // Returns the segment name if the load command is a segment command. Optional<StringRef> getSegmentName() const; @@ -106,15 +107,22 @@ struct SymbolEntry { uint16_t n_desc; uint64_t n_value; - bool isExternalSymbol() const { - return n_type & ((MachO::N_EXT | MachO::N_PEXT)); - } + bool isExternalSymbol() const { return n_type & MachO::N_EXT; } bool isLocalSymbol() const { return !isExternalSymbol(); } bool isUndefinedSymbol() const { return (n_type & MachO::N_TYPE) == MachO::N_UNDF; } + + bool isSwiftSymbol() const { + return StringRef(Name).startswith("_$s") || + StringRef(Name).startswith("_$S"); + } + + Optional<uint32_t> section() const { + return n_sect == MachO::NO_SECT ? None : Optional<uint32_t>(n_sect); + } }; /// The location of the symbol table inside the binary is described by LC_SYMTAB @@ -157,10 +165,29 @@ struct StringTable { }; struct RelocationInfo { - const SymbolEntry *Symbol; + // The referenced symbol entry. Set if !Scattered && Extern. + Optional<const SymbolEntry *> Symbol; + // The referenced section. Set if !Scattered && !Extern. + Optional<const Section *> Sec; // True if Info is a scattered_relocation_info. bool Scattered; + // True if the r_symbolnum points to a section number (i.e. r_extern=0). + bool Extern; MachO::any_relocation_info Info; + + unsigned getPlainRelocationSymbolNum(bool IsLittleEndian) { + if (IsLittleEndian) + return Info.r_word1 & 0xffffff; + return Info.r_word1 >> 8; + } + + void setPlainRelocationSymbolNum(unsigned SymbolNum, bool IsLittleEndian) { + assert(SymbolNum < (1 << 24) && "SymbolNum out of range"); + if (IsLittleEndian) + Info.r_word1 = (Info.r_word1 & ~0x00ffffff) | SymbolNum; + else + Info.r_word1 = (Info.r_word1 & ~0xffffff00) | (SymbolNum << 8); + } }; /// The location of the rebase info inside the binary is described by @@ -275,7 +302,12 @@ struct Object { IndirectSymbolTable IndirectSymTable; LinkData DataInCode; LinkData FunctionStarts; + LinkData CodeSignature; + + Optional<uint32_t> SwiftVersion; + /// The index of LC_CODE_SIGNATURE load command if present. + Optional<size_t> CodeSignatureCommandIndex; /// 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. @@ -292,7 +324,13 @@ struct Object { Object() : NewSectionsContents(Alloc) {} - void removeSections(function_ref<bool(const Section &)> ToRemove); + Error + removeSections(function_ref<bool(const std::unique_ptr<Section> &)> ToRemove); + + Error removeLoadCommands(function_ref<bool(const LoadCommand &)> ToRemove); + + void updateLoadCommandIndexes(); + void addLoadCommand(LoadCommand LC); /// Creates a new segment load command in the object and returns a reference diff --git a/llvm/tools/llvm-objcopy/StripOpts.td b/llvm/tools/llvm-objcopy/StripOpts.td index cd02cffae6732..001da23528d78 100644 --- a/llvm/tools/llvm-objcopy/StripOpts.td +++ b/llvm/tools/llvm-objcopy/StripOpts.td @@ -15,3 +15,6 @@ def d : Flag<["-"], "d">, def S : Flag<["-"], "S">, Alias<strip_debug>, HelpText<"Alias for --strip-debug">; + +def strip_swift_symbols : Flag<["-"], "T">, + HelpText<"Remove Swift symbols">; diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp index e662f35f4b084..69b23b6cf9756 100644 --- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -8,10 +8,11 @@ #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 "wasm/WasmObjcopy.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" @@ -25,6 +26,7 @@ #include "llvm/Object/ELFTypes.h" #include "llvm/Object/Error.h" #include "llvm/Object/MachO.h" +#include "llvm/Object/Wasm.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" @@ -33,6 +35,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" @@ -172,6 +175,8 @@ static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); + else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In)) + return objcopy::wasm::executeObjcopyOnBinary(Config, *WasmBinary, Out); else return createStringError(object_error::invalid_file_type, "unsupported object file format"); @@ -322,11 +327,25 @@ enum class ToolType { Objcopy, Strip, InstallNameTool }; int main(int argc, char **argv) { InitLLVM X(argc, argv); ToolName = argv[0]; - ToolType Tool = StringSwitch<ToolType>(sys::path::stem(ToolName)) - .EndsWith("strip", ToolType::Strip) - .EndsWith("install-name-tool", ToolType::InstallNameTool) - .EndsWith("install_name_tool", ToolType::InstallNameTool) - .Default(ToolType::Objcopy); + + StringRef Stem = sys::path::stem(ToolName); + auto Is = [=](StringRef Tool) { + // We need to recognize the following filenames: + // + // llvm-objcopy -> objcopy + // strip-10.exe -> strip + // powerpc64-unknown-freebsd13-objcopy -> objcopy + // llvm-install-name-tool -> install-name-tool + auto I = Stem.rfind_lower(Tool); + return I != StringRef::npos && + (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); + }; + ToolType Tool = ToolType::Objcopy; + if (Is("strip")) + Tool = ToolType::Strip; + else if (Is("install-name-tool") || Is("install_name_tool")) + Tool = ToolType::InstallNameTool; + // Expand response files. // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp, // into a separate function in the CommandLine library and call that function diff --git a/llvm/tools/llvm-objcopy/wasm/Object.cpp b/llvm/tools/llvm-objcopy/wasm/Object.cpp new file mode 100644 index 0000000000000..0c416483663f2 --- /dev/null +++ b/llvm/tools/llvm-objcopy/wasm/Object.cpp @@ -0,0 +1,36 @@ +//===- Object.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Object.h" + +#include "llvm/Support/LEB128.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +using namespace object; +using namespace llvm::wasm; + +void Object::addSectionWithOwnedContents( + Section NewSection, std::unique_ptr<MemoryBuffer> &&Content) { + Sections.push_back(NewSection); + OwnedContents.emplace_back(std::move(Content)); +} + +void Object::removeSections(function_ref<bool(const Section &)> ToRemove) { + // TODO: remove reloc sections for the removed section, handle symbols, etc. + Sections.erase( + std::remove_if(std::begin(Sections), std::end(Sections), ToRemove), + std::end(Sections)); +} + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/wasm/Object.h b/llvm/tools/llvm-objcopy/wasm/Object.h new file mode 100644 index 0000000000000..9db91c41e2e26 --- /dev/null +++ b/llvm/tools/llvm-objcopy/wasm/Object.h @@ -0,0 +1,47 @@ +//===- Object.h -------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H +#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/Wasm.h" +#include "llvm/Support/MemoryBuffer.h" +#include <vector> + +namespace llvm { +namespace objcopy { +namespace wasm { + +struct Section { + // For now, each section is only an opaque binary blob with no distinction + // between custom and known sections. + uint8_t SectionType; + StringRef Name; + ArrayRef<uint8_t> Contents; +}; + +struct Object { + llvm::wasm::WasmObjectHeader Header; + // For now don't discriminate between kinds of sections. + std::vector<Section> Sections; + + void addSectionWithOwnedContents(Section NewSection, + std::unique_ptr<MemoryBuffer> &&Content); + void removeSections(function_ref<bool(const Section &)> ToRemove); + +private: + std::vector<std::unique_ptr<MemoryBuffer>> OwnedContents; +}; + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_OBJECT_H diff --git a/llvm/tools/llvm-objcopy/wasm/Reader.cpp b/llvm/tools/llvm-objcopy/wasm/Reader.cpp new file mode 100644 index 0000000000000..13fa84ad80201 --- /dev/null +++ b/llvm/tools/llvm-objcopy/wasm/Reader.cpp @@ -0,0 +1,33 @@ +//===- Reader.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Reader.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +using namespace object; +using namespace llvm::wasm; + +Expected<std::unique_ptr<Object>> Reader::create() const { + auto Obj = std::make_unique<Object>(); + Obj->Header = WasmObj.getHeader(); + std::vector<Section> Sections; + Obj->Sections.reserve(WasmObj.getNumSections()); + for (const SectionRef &Sec : WasmObj.sections()) { + const WasmSection &WS = WasmObj.getWasmSection(Sec); + Obj->Sections.push_back( + {static_cast<uint8_t>(WS.Type), WS.Name, WS.Content}); + } + return std::move(Obj); +} + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/wasm/Reader.h b/llvm/tools/llvm-objcopy/wasm/Reader.h new file mode 100644 index 0000000000000..2dcf7dde029a0 --- /dev/null +++ b/llvm/tools/llvm-objcopy/wasm/Reader.h @@ -0,0 +1,31 @@ +//===- Reader.h -------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H +#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H + +#include "Object.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +class Reader { +public: + explicit Reader(const object::WasmObjectFile &O) : WasmObj(O) {} + Expected<std::unique_ptr<Object>> create() const; + +private: + const object::WasmObjectFile &WasmObj; +}; + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_READER_H diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp new file mode 100644 index 0000000000000..20781cef2d33a --- /dev/null +++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp @@ -0,0 +1,114 @@ +//===- WasmObjcopy.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "WasmObjcopy.h" +#include "Buffer.h" +#include "CopyConfig.h" +#include "Object.h" +#include "Reader.h" +#include "Writer.h" +#include "llvm-objcopy.h" +#include "llvm/Support/Errc.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +using namespace object; + +static Error dumpSectionToFile(StringRef SecName, StringRef Filename, + Object &Obj) { + for (const Section &Sec : Obj.Sections) { + if (Sec.Name == SecName) { + ArrayRef<uint8_t> Contents = Sec.Contents; + Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(Filename, Contents.size()); + if (!BufferOrErr) + return BufferOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); + std::copy(Contents.begin(), Contents.end(), Buf->getBufferStart()); + if (Error E = Buf->commit()) + return E; + return Error::success(); + } + } + return createStringError(errc::invalid_argument, "section '%s' not found", + SecName.str().c_str()); +} +static Error handleArgs(const CopyConfig &Config, Object &Obj) { + // Only support AddSection, DumpSection, RemoveSection for now. + for (StringRef Flag : Config.DumpSection) { + StringRef SecName; + StringRef FileName; + std::tie(SecName, FileName) = Flag.split("="); + if (Error E = dumpSectionToFile(SecName, FileName, Obj)) + return createFileError(FileName, std::move(E)); + } + + Obj.removeSections([&Config](const Section &Sec) { + if (Config.ToRemove.matches(Sec.Name)) + return true; + return false; + }); + + for (StringRef Flag : Config.AddSection) { + StringRef SecName, FileName; + std::tie(SecName, FileName) = Flag.split("="); + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return createFileError(FileName, errorCodeToError(BufOrErr.getError())); + Section Sec; + Sec.SectionType = llvm::wasm::WASM_SEC_CUSTOM; + Sec.Name = SecName; + std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); + Sec.Contents = makeArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), + Buf->getBufferSize()); + Obj.addSectionWithOwnedContents(Sec, std::move(Buf)); + } + + if (!Config.AddGnuDebugLink.empty() || !Config.BuildIdLinkDir.empty() || + Config.BuildIdLinkInput || Config.BuildIdLinkOutput || + Config.ExtractPartition || !Config.SplitDWO.empty() || + !Config.SymbolsPrefix.empty() || !Config.AllocSectionsPrefix.empty() || + Config.DiscardMode != DiscardType::None || Config.NewSymbolVisibility || + !Config.SymbolsToAdd.empty() || !Config.RPathToAdd.empty() || + !Config.OnlySection.empty() || !Config.SymbolsToGlobalize.empty() || + !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || + !Config.SymbolsToRemove.empty() || + !Config.UnneededSymbolsToRemove.empty() || + !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || + !Config.SectionsToRename.empty() || !Config.SetSectionAlignment.empty() || + !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty()) { + return createStringError( + llvm::errc::invalid_argument, + "only add-section, dump-section, and remove-section are supported"); + } + return Error::success(); +} + +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::WasmObjectFile &In, Buffer &Out) { + Reader TheReader(In); + Expected<std::unique_ptr<Object>> ObjOrErr = TheReader.create(); + if (!ObjOrErr) + return createFileError(Config.InputFilename, ObjOrErr.takeError()); + Object *Obj = ObjOrErr->get(); + assert(Obj && "Unable to deserialize Wasm object"); + if (Error E = handleArgs(Config, *Obj)) + return E; + Writer TheWriter(*Obj, Out); + if (Error E = TheWriter.write()) + return createFileError(Config.OutputFilename, std::move(E)); + return Error::success(); +} + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h new file mode 100644 index 0000000000000..3557d5c0a50df --- /dev/null +++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.h @@ -0,0 +1,31 @@ +//===- WasmObjcopy.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H +#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H + +namespace llvm { +class Error; + +namespace object { +class WasmObjectFile; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace wasm { +Error executeObjcopyOnBinary(const CopyConfig &Config, + object::WasmObjectFile &In, Buffer &Out); + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WASMOBJCOPY_H diff --git a/llvm/tools/llvm-objcopy/wasm/Writer.cpp b/llvm/tools/llvm-objcopy/wasm/Writer.cpp new file mode 100644 index 0000000000000..50d26507b4983 --- /dev/null +++ b/llvm/tools/llvm-objcopy/wasm/Writer.cpp @@ -0,0 +1,78 @@ +//===- Writer.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Writer.h" +#include "llvm/BinaryFormat/Wasm.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +using namespace object; +using namespace llvm::wasm; + +Writer::SectionHeader Writer::createSectionHeader(const Section &S, + size_t &SectionSize) { + SectionHeader Header; + raw_svector_ostream OS(Header); + OS << S.SectionType; + bool HasName = S.SectionType == WASM_SEC_CUSTOM; + SectionSize = S.Contents.size(); + if (HasName) + SectionSize += getULEB128Size(S.Name.size()) + S.Name.size(); + // Pad the LEB value out to 5 bytes to make it a predictable size, and + // match the behavior of clang. + encodeULEB128(SectionSize, OS, 5); + if (HasName) { + encodeULEB128(S.Name.size(), OS); + OS << S.Name; + } + // Total section size is the content size plus 1 for the section type and + // 5 for the LEB-encoded size. + SectionSize = SectionSize + 1 + 5; + return Header; +} + +size_t Writer::finalize() { + size_t ObjectSize = sizeof(WasmMagic) + sizeof(WasmVersion); + SectionHeaders.reserve(Obj.Sections.size()); + // Finalize the headers of each section so we know the total size. + for (const Section &S : Obj.Sections) { + size_t SectionSize; + SectionHeaders.push_back(createSectionHeader(S, SectionSize)); + ObjectSize += SectionSize; + } + return ObjectSize; +} + +Error Writer::write() { + size_t FileSize = finalize(); + if (Error E = Buf.allocate(FileSize)) + return E; + + // Write the header. + uint8_t *Ptr = Buf.getBufferStart(); + Ptr = std::copy(Obj.Header.Magic.begin(), Obj.Header.Magic.end(), Ptr); + support::endian::write32le(Ptr, Obj.Header.Version); + Ptr += sizeof(Obj.Header.Version); + + // Write each section. + for (size_t I = 0, S = SectionHeaders.size(); I < S; ++I) { + Ptr = std::copy(SectionHeaders[I].begin(), SectionHeaders[I].end(), Ptr); + ArrayRef<uint8_t> Contents = Obj.Sections[I].Contents; + Ptr = std::copy(Contents.begin(), Contents.end(), Ptr); + } + return Buf.commit(); +} + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/wasm/Writer.h b/llvm/tools/llvm-objcopy/wasm/Writer.h new file mode 100644 index 0000000000000..da48ee730c3ba --- /dev/null +++ b/llvm/tools/llvm-objcopy/wasm/Writer.h @@ -0,0 +1,50 @@ +//===- Writer.h -------------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H +#define LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H + +#include "Buffer.h" +#include "Object.h" +#include <cstdint> +#include <vector> + +namespace llvm { +namespace objcopy { +namespace wasm { + +class Writer { +public: + Writer(Object &Obj, Buffer &Buf) : Obj(Obj), Buf(Buf) {} + Error write(); + +private: + using SectionHeader = SmallVector<char, 8>; + Object &Obj; + Buffer &Buf; + std::vector<SectionHeader> SectionHeaders; + + /// Generate a wasm section section header for S. + /// The header consists of + /// * A one-byte section ID (aka the section type). + /// * The size of the section contents, encoded as ULEB128. + /// * If the section is a custom section (type 0) it also has a name, which is + /// encoded as a length-prefixed string. The encoded section size *includes* + /// this string. + /// See https://webassembly.github.io/spec/core/binary/modules.html#sections + /// Return the header and store the total size in SectionSize. + static SectionHeader createSectionHeader(const Section &S, + size_t &SectionSize); + size_t finalize(); +}; + +} // end namespace wasm +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_LLVM_OBJCOPY_WASM_WRITER_H diff --git a/llvm/tools/llvm-objdump/COFFDump.cpp b/llvm/tools/llvm-objdump/COFFDump.cpp index 60b0f5a3cbd15..b9d69d62e4e71 100644 --- a/llvm/tools/llvm-objdump/COFFDump.cpp +++ b/llvm/tools/llvm-objdump/COFFDump.cpp @@ -14,6 +14,8 @@ /// //===----------------------------------------------------------------------===// +#include "COFFDump.h" + #include "llvm-objdump.h" #include "llvm/Demangle/Demangle.h" #include "llvm/Object/COFF.h" @@ -24,10 +26,11 @@ #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" +using namespace llvm; +using namespace llvm::objdump; 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) { @@ -235,8 +238,8 @@ printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) { return; uintptr_t IntPtr = 0; - if (std::error_code EC = Obj->getVaPtr(TableVA, IntPtr)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = Obj->getVaPtr(TableVA, IntPtr)) + reportError(std::move(E), Obj->getFileName()); const support::ulittle32_t *P = (const support::ulittle32_t *)IntPtr; outs() << "SEH Table:"; @@ -274,17 +277,17 @@ static void printTLSDirectory(const COFFObjectFile *Obj) { if (!PE32Header && !PE32PlusHeader) return; - const data_directory *DataDir; - if (std::error_code EC = Obj->getDataDirectory(COFF::TLS_TABLE, DataDir)) - reportError(errorCodeToError(EC), Obj->getFileName()); + const data_directory *DataDir = Obj->getDataDirectory(COFF::TLS_TABLE); + if (!DataDir) + reportError("missing data dir for TLS table", Obj->getFileName()); if (DataDir->RelativeVirtualAddress == 0) return; uintptr_t IntPtr = 0; - if (std::error_code EC = + if (Error E = Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr)) - reportError(errorCodeToError(EC), Obj->getFileName()); + reportError(std::move(E), Obj->getFileName()); if (PE32Header) { auto *TLSDir = reinterpret_cast<const coff_tls_directory32 *>(IntPtr); @@ -306,19 +309,17 @@ static void printLoadConfiguration(const COFFObjectFile *Obj) { if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_I386) return; - const data_directory *DataDir; - - if (std::error_code EC = - Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE, DataDir)) - reportError(errorCodeToError(EC), Obj->getFileName()); + const data_directory *DataDir = Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE); + if (!DataDir) + reportError("no load config data dir", Obj->getFileName()); uintptr_t IntPtr = 0; if (DataDir->RelativeVirtualAddress == 0) return; - if (std::error_code EC = + if (Error E = Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr)) - reportError(errorCodeToError(EC), Obj->getFileName()); + reportError(std::move(E), Obj->getFileName()); auto *LoadConf = reinterpret_cast<const coff_load_configuration32 *>(IntPtr); outs() << "Load configuration:" @@ -473,9 +474,9 @@ static bool getPDataSection(const COFFObjectFile *Obj, return false; } -Error getCOFFRelocationValueString(const COFFObjectFile *Obj, - const RelocationRef &Rel, - SmallVectorImpl<char> &Result) { +Error objdump::getCOFFRelocationValueString(const COFFObjectFile *Obj, + const RelocationRef &Rel, + SmallVectorImpl<char> &Result) { symbol_iterator SymI = Rel.getSymbol(); Expected<StringRef> SymNameOrErr = SymI->getName(); if (!SymNameOrErr) @@ -596,7 +597,7 @@ static void printRuntimeFunctionRels(const COFFObjectFile *Obj, printWin64EHUnwindInfo(UI); } -void printCOFFUnwindInfo(const COFFObjectFile *Obj) { +void objdump::printCOFFUnwindInfo(const COFFObjectFile *Obj) { if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_AMD64) { WithColor::error(errs(), "llvm-objdump") << "unsupported image machine type " @@ -625,7 +626,7 @@ void printCOFFUnwindInfo(const COFFObjectFile *Obj) { } } -void printCOFFFileHeader(const object::ObjectFile *Obj) { +void objdump::printCOFFFileHeader(const object::ObjectFile *Obj) { const COFFObjectFile *file = dyn_cast<const COFFObjectFile>(Obj); printTLSDirectory(file); printLoadConfiguration(file); @@ -633,7 +634,7 @@ void printCOFFFileHeader(const object::ObjectFile *Obj) { printExportTable(file); } -void printCOFFSymbolTable(const object::COFFImportFile *i) { +void objdump::printCOFFSymbolTable(const object::COFFImportFile *i) { unsigned Index = 0; bool IsCode = i->getCOFFImportHeader()->getType() == COFF::IMPORT_CODE; @@ -656,15 +657,16 @@ void printCOFFSymbolTable(const object::COFFImportFile *i) { } } -void printCOFFSymbolTable(const COFFObjectFile *coff) { +void objdump::printCOFFSymbolTable(const COFFObjectFile *coff) { for (unsigned SI = 0, SE = coff->getNumberOfSymbols(); SI != SE; ++SI) { Expected<COFFSymbolRef> Symbol = coff->getSymbol(SI); if (!Symbol) reportError(Symbol.takeError(), coff->getFileName()); - StringRef Name; - if (std::error_code EC = coff->getSymbolName(*Symbol, Name)) - reportError(errorCodeToError(EC), coff->getFileName()); + Expected<StringRef> NameOrErr = coff->getSymbolName(*Symbol); + if (!NameOrErr) + reportError(NameOrErr.takeError(), coff->getFileName()); + StringRef Name = *NameOrErr; outs() << "[" << format("%2d", SI) << "]" << "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")" @@ -676,11 +678,9 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) { << "0x" << format("%08x", unsigned(Symbol->getValue())) << " " << Name; if (Demangle && Name.startswith("?")) { - char *DemangledSymbol = nullptr; - size_t Size = 0; int Status = -1; - DemangledSymbol = - microsoftDemangle(Name.data(), DemangledSymbol, &Size, &Status); + char *DemangledSymbol = + microsoftDemangle(Name.data(), nullptr, nullptr, nullptr, &Status); if (Status == 0 && DemangledSymbol) { outs() << " (" << StringRef(DemangledSymbol) << ")"; @@ -694,9 +694,9 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) { for (unsigned AI = 0, AE = Symbol->getNumberOfAuxSymbols(); AI < AE; ++AI, ++SI) { if (Symbol->isSectionDefinition()) { const coff_aux_section_definition *asd; - if (std::error_code EC = + if (Error E = coff->getAuxSymbol<coff_aux_section_definition>(SI + 1, asd)) - reportError(errorCodeToError(EC), coff->getFileName()); + reportError(std::move(E), coff->getFileName()); int32_t AuxNumber = asd->getNumber(Symbol->isBigObj()); @@ -711,8 +711,8 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) { , unsigned(asd->Selection)); } else if (Symbol->isFileRecord()) { const char *FileName; - if (std::error_code EC = coff->getAuxSymbol<char>(SI + 1, FileName)) - reportError(errorCodeToError(EC), coff->getFileName()); + if (Error E = coff->getAuxSymbol<char>(SI + 1, FileName)) + reportError(std::move(E), coff->getFileName()); StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() * coff->getSymbolTableEntrySize()); @@ -722,9 +722,8 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) { break; } else if (Symbol->isWeakExternal()) { const coff_aux_weak_external *awe; - if (std::error_code EC = - coff->getAuxSymbol<coff_aux_weak_external>(SI + 1, awe)) - reportError(errorCodeToError(EC), coff->getFileName()); + if (Error E = coff->getAuxSymbol<coff_aux_weak_external>(SI + 1, awe)) + reportError(std::move(E), coff->getFileName()); outs() << "AUX " << format("indx %d srch %d\n", static_cast<uint32_t>(awe->TagIndex), @@ -735,4 +734,3 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) { } } } -} // namespace llvm diff --git a/llvm/tools/llvm-objdump/COFFDump.h b/llvm/tools/llvm-objdump/COFFDump.h new file mode 100644 index 0000000000000..21f97bdeb83cd --- /dev/null +++ b/llvm/tools/llvm-objdump/COFFDump.h @@ -0,0 +1,37 @@ +//===-- COFFDump.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_COFFDUMP_H +#define LLVM_TOOLS_LLVM_OBJDUMP_COFFDUMP_H + +#include "llvm/ADT/SmallVector.h" + +namespace llvm { + +class Error; + +namespace object { +class COFFObjectFile; +class COFFImportFile; +class ObjectFile; +class RelocationRef; +} // namespace object + +namespace objdump { +Error getCOFFRelocationValueString(const object::COFFObjectFile *Obj, + const object::RelocationRef &Rel, + llvm::SmallVectorImpl<char> &Result); + +void printCOFFUnwindInfo(const object::COFFObjectFile *O); +void printCOFFFileHeader(const object::ObjectFile *O); +void printCOFFSymbolTable(const object::COFFImportFile *I); +void printCOFFSymbolTable(const object::COFFObjectFile *O); +} // namespace objdump +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-objdump/ELFDump.cpp b/llvm/tools/llvm-objdump/ELFDump.cpp index abfe08346bbd7..602bc63882527 100644 --- a/llvm/tools/llvm-objdump/ELFDump.cpp +++ b/llvm/tools/llvm-objdump/ELFDump.cpp @@ -11,6 +11,8 @@ /// //===----------------------------------------------------------------------===// +#include "ELFDump.h" + #include "llvm-objdump.h" #include "llvm/Demangle/Demangle.h" #include "llvm/Object/ELFObjectFile.h" @@ -18,9 +20,10 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" +using namespace llvm; using namespace llvm::object; +using namespace llvm::objdump; -namespace llvm { template <class ELFT> static Expected<StringRef> getDynamicStrTab(const ELFFile<ELFT> *Elf) { auto DynamicEntriesOrError = Elf->dynamicEntries(); @@ -98,7 +101,7 @@ static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj, if (!SymName) return SymName.takeError(); if (Demangle) - Fmt << demangle(*SymName); + Fmt << demangle(std::string(*SymName)); else Fmt << *SymName; } @@ -116,9 +119,9 @@ static Error getRelocationValueString(const ELFObjectFile<ELFT> *Obj, return Error::success(); } -Error getELFRelocationValueString(const ELFObjectFileBase *Obj, - const RelocationRef &Rel, - SmallVectorImpl<char> &Result) { +Error objdump::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)) @@ -147,7 +150,7 @@ static uint64_t getSectionLMA(const ELFFile<ELFT> *Obj, return Sec.getAddress(); } -uint64_t getELFSectionLMA(const object::ELFSectionRef &Sec) { +uint64_t objdump::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())) @@ -159,16 +162,23 @@ uint64_t getELFSectionLMA(const object::ELFSectionRef &Sec) { } template <class ELFT> -void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) { +static void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) { ArrayRef<typename ELFT::Dyn> DynamicEntries = unwrapOrError(Elf->dynamicEntries(), Filename); + + // Find the maximum tag name length to format the value column properly. + size_t MaxLen = 0; + for (const typename ELFT::Dyn &Dyn : DynamicEntries) + MaxLen = std::max(MaxLen, Elf->getDynamicTagAsString(Dyn.d_tag).size()); + std::string TagFmt = " %-" + std::to_string(MaxLen) + "s "; + outs() << "Dynamic Section:\n"; for (const typename ELFT::Dyn &Dyn : DynamicEntries) { if (Dyn.d_tag == ELF::DT_NULL) continue; std::string Str = Elf->getDynamicTagAsString(Dyn.d_tag); - outs() << format(" %-21s", Str.c_str()); + outs() << format(TagFmt.c_str(), Str.c_str()); const char *Fmt = ELFT::Is64Bits ? "0x%016" PRIx64 "\n" : "0x%08" PRIx64 "\n"; @@ -188,11 +198,17 @@ void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) { } } -template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) { +template <class ELFT> +static void printProgramHeaders(const ELFFile<ELFT> *Obj, StringRef FileName) { outs() << "Program Header:\n"; - auto ProgramHeaderOrError = o->program_headers(); - if (!ProgramHeaderOrError) - report_fatal_error(toString(ProgramHeaderOrError.takeError())); + auto ProgramHeaderOrError = Obj->program_headers(); + if (!ProgramHeaderOrError) { + reportWarning("unable to read program headers: " + + toString(ProgramHeaderOrError.takeError()), + FileName); + return; + } + for (const typename ELFT::Phdr &Phdr : *ProgramHeaderOrError) { switch (Phdr.p_type) { case ELF::PT_DYNAMIC: @@ -255,8 +271,8 @@ template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) { } template <class ELFT> -void printSymbolVersionDependency(ArrayRef<uint8_t> Contents, - StringRef StrTab) { +static void printSymbolVersionDependency(ArrayRef<uint8_t> Contents, + StringRef StrTab) { outs() << "Version References:\n"; const uint8_t *Buf = Contents.data(); @@ -280,9 +296,9 @@ void printSymbolVersionDependency(ArrayRef<uint8_t> Contents, } template <class ELFT> -void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr, - ArrayRef<uint8_t> Contents, - StringRef StrTab) { +static void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr, + ArrayRef<uint8_t> Contents, + StringRef StrTab) { outs() << "Version definitions:\n"; const uint8_t *Buf = Contents.data(); @@ -312,7 +328,8 @@ void printSymbolVersionDefinition(const typename ELFT::Shdr &Shdr, } template <class ELFT> -void printSymbolVersionInfo(const ELFFile<ELFT> *Elf, StringRef FileName) { +static void printSymbolVersionInfo(const ELFFile<ELFT> *Elf, + StringRef FileName) { ArrayRef<typename ELFT::Shdr> Sections = unwrapOrError(Elf->sections(), FileName); for (const typename ELFT::Shdr &Shdr : Sections) { @@ -333,18 +350,18 @@ void printSymbolVersionInfo(const ELFFile<ELFT> *Elf, StringRef FileName) { } } -void printELFFileHeader(const object::ObjectFile *Obj) { +void objdump::printELFFileHeader(const object::ObjectFile *Obj) { if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) - printProgramHeaders(ELFObj->getELFFile()); + printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName()); else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) - printProgramHeaders(ELFObj->getELFFile()); + printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName()); else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj)) - printProgramHeaders(ELFObj->getELFFile()); + printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName()); else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj)) - printProgramHeaders(ELFObj->getELFFile()); + printProgramHeaders(ELFObj->getELFFile(), Obj->getFileName()); } -void printELFDynamicSection(const object::ObjectFile *Obj) { +void objdump::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)) @@ -355,7 +372,7 @@ void printELFDynamicSection(const object::ObjectFile *Obj) { printDynamicSection(ELFObj->getELFFile(), Obj->getFileName()); } -void printELFSymbolVersionInfo(const object::ObjectFile *Obj) { +void objdump::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)) @@ -365,4 +382,3 @@ void printELFSymbolVersionInfo(const object::ObjectFile *Obj) { else if (const auto *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj)) printSymbolVersionInfo(ELFObj->getELFFile(), Obj->getFileName()); } -} // namespace llvm diff --git a/llvm/tools/llvm-objdump/ELFDump.h b/llvm/tools/llvm-objdump/ELFDump.h new file mode 100644 index 0000000000000..9b6b1f341cf3a --- /dev/null +++ b/llvm/tools/llvm-objdump/ELFDump.h @@ -0,0 +1,39 @@ +//===-- ELFDump.h - ELF-specific dumper -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_ELFDUMP_H +#define LLVM_TOOLS_LLVM_OBJDUMP_ELFDUMP_H + +#include "llvm/ADT/SmallVector.h" + +namespace llvm { + +class Error; + +namespace object { +class ELFObjectFileBase; +class ELFSectionRef; +class ObjectFile; +class RelocationRef; +} // namespace object + +namespace objdump { + +Error getELFRelocationValueString(const object::ELFObjectFileBase *Obj, + const object::RelocationRef &Rel, + llvm::SmallVectorImpl<char> &Result); +uint64_t getELFSectionLMA(const object::ELFSectionRef &Sec); + +void printELFFileHeader(const object::ObjectFile *O); +void printELFDynamicSection(const object::ObjectFile *Obj); +void printELFSymbolVersionInfo(const object::ObjectFile *Obj); + +} // namespace objdump +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp index 87c7a92933f1d..6d46496ecd4ea 100644 --- a/llvm/tools/llvm-objdump/MachODump.cpp +++ b/llvm/tools/llvm-objdump/MachODump.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "MachODump.h" + #include "llvm-objdump.h" #include "llvm-c/Disassembler.h" #include "llvm/ADT/STLExtras.h" @@ -56,53 +58,35 @@ extern "C" { } #endif +using namespace llvm; using namespace llvm::object; +using namespace llvm::objdump; -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::OptionCategory objdump::MachOCat("llvm-objdump MachO Specific Options"); -cl::opt<bool> - FirstPrivateHeader("private-header", - cl::desc("Display only the first format specific file " - "header"), - cl::cat(MachOCat)); +cl::opt<bool> objdump::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> objdump::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> objdump::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> objdump::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> objdump::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)); +cl::opt<bool> objdump::WeakBind("weak-bind", + cl::desc("Display mach-o weak binding info"), + cl::cat(MachOCat)); static cl::opt<bool> UseDbg("g", cl::Grouping, @@ -121,63 +105,65 @@ static cl::opt<bool> NoLeadingHeaders("no-leading-headers", cl::desc("Print no leading headers"), cl::cat(MachOCat)); -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::cat(MachOCat)); - -cl::opt<bool> IndirectSymbols("indirect-symbols", - cl::desc("Print indirect symbol table for Mach-O " - "objects (requires -macho)"), - cl::cat(MachOCat)); +cl::opt<bool> objdump::UniversalHeaders( + "universal-headers", + cl::desc("Print Mach-O universal headers (requires -macho)"), + cl::cat(MachOCat)); -cl::opt<bool> - DataInCode("data-in-code", - cl::desc("Print the data in code table for Mach-O objects " - "(requires -macho)"), - cl::cat(MachOCat)); +static cl::opt<bool> ArchiveMemberOffsets( + "archive-member-offsets", + cl::desc("Print the offset to each archive member for Mach-O archives " + "(requires -macho and -archive-headers)"), + cl::cat(MachOCat)); -cl::opt<bool> LinkOptHints("link-opt-hints", - cl::desc("Print the linker optimization hints for " - "Mach-O objects (requires -macho)"), - cl::cat(MachOCat)); +cl::opt<bool> objdump::IndirectSymbols( + "indirect-symbols", + cl::desc( + "Print indirect symbol table for Mach-O objects (requires -macho)"), + cl::cat(MachOCat)); -cl::opt<bool> InfoPlist("info-plist", - cl::desc("Print the info plist section as strings for " - "Mach-O objects (requires -macho)"), - cl::cat(MachOCat)); +cl::opt<bool> objdump::DataInCode( + "data-in-code", + cl::desc( + "Print the data in code table 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> + objdump::LinkOptHints("link-opt-hints", + cl::desc("Print the linker optimization hints for " + "Mach-O objects (requires -macho)"), + cl::cat(MachOCat)); cl::opt<bool> - DylibId("dylib-id", - cl::desc("Print the shared library's id for the dylib Mach-O " - "file (requires -macho)"), - cl::cat(MachOCat)); + objdump::InfoPlist("info-plist", + cl::desc("Print the info plist section as strings for " + "Mach-O objects (requires -macho)"), + cl::cat(MachOCat)); cl::opt<bool> + objdump::DylibsUsed("dylibs-used", + cl::desc("Print the shared libraries used for linked " + "Mach-O files (requires -macho)"), + cl::cat(MachOCat)); + +cl::opt<bool> objdump::DylibId("dylib-id", + cl::desc("Print the shared library's id for the " + "dylib Mach-O file (requires -macho)"), + cl::cat(MachOCat)); + +static cl::opt<bool> NonVerbose("non-verbose", - cl::desc("Print the info for Mach-O objects in " - "non-verbose or numeric form (requires -macho)"), + cl::desc("Print the info for Mach-O objects in non-verbose or " + "numeric form (requires -macho)"), cl::cat(MachOCat)); cl::opt<bool> - ObjcMetaData("objc-meta-data", - cl::desc("Print the Objective-C runtime meta data for " - "Mach-O files (requires -macho)"), - cl::cat(MachOCat)); + objdump::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> DisSymName( +static cl::opt<std::string> DisSymName( "dis-symname", cl::desc("disassemble just this symbol's instructions (requires -macho)"), cl::cat(MachOCat)); @@ -191,7 +177,7 @@ static cl::list<std::string> ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), cl::ZeroOrMore, cl::cat(MachOCat)); -bool ArchAll = false; +static bool ArchAll = false; static std::string ThumbTripleName; @@ -233,6 +219,7 @@ static const Target *GetTarget(const MachOObjectFile *MachOObj, return nullptr; } +namespace { struct SymbolSorter { bool operator()(const SymbolRef &A, const SymbolRef &B) { Expected<SymbolRef::Type> ATypeOrErr = A.getType(); @@ -243,11 +230,14 @@ struct SymbolSorter { if (!BTypeOrErr) reportError(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(); + uint64_t AAddr = + (AType != SymbolRef::ST_Function) ? 0 : cantFail(A.getValue()); + uint64_t BAddr = + (BType != SymbolRef::ST_Function) ? 0 : cantFail(B.getValue()); return AAddr < BAddr; } }; +} // namespace // Types for the storted data in code table that is built before disassembly // and the predicate function to sort them. @@ -497,9 +487,9 @@ static void printRelocationTargetName(const MachOObjectFile *O, Fmt << S; } -Error getMachORelocationValueString(const MachOObjectFile *Obj, - const RelocationRef &RelRef, - SmallVectorImpl<char> &Result) { +Error objdump::getMachORelocationValueString(const MachOObjectFile *Obj, + const RelocationRef &RelRef, + SmallVectorImpl<char> &Result) { DataRefImpl Rel = RelRef.getRawDataRefImpl(); MachO::any_relocation_info RE = Obj->getRelocation(Rel); @@ -1279,7 +1269,7 @@ static void CreateSymbolAddressMap(MachOObjectFile *O, 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(); + uint64_t Address = cantFail(Symbol.getValue()); StringRef SymName = unwrapOrError(Symbol.getName(), FileName); if (!SymName.startswith(".objc")) (*AddrMap)[Address] = SymName; @@ -1754,6 +1744,9 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, else consumeError(SecNameOrErr.takeError()); + if (!DumpSection.empty()) + FoundSectionSet.insert(DumpSection); + DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); if ((DumpSegName.empty() || SegName == DumpSegName) && @@ -1776,8 +1769,9 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, uint32_t sect_size = BytesStr.size(); uint64_t sect_addr = Section.getAddress(); - outs() << "Contents of (" << SegName << "," << SectName - << ") section\n"; + if (!NoLeadingHeaders) + outs() << "Contents of (" << SegName << "," << SectName + << ") section\n"; if (verbose) { if ((section_flags & MachO::S_ATTR_PURE_INSTRUCTIONS) || @@ -2318,7 +2312,7 @@ 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 parseInputMachO(StringRef Filename) { +void objdump::parseInputMachO(StringRef Filename) { if (!ValidateArchFlags()) return; @@ -2376,7 +2370,7 @@ void parseInputMachO(StringRef Filename) { llvm_unreachable("Input object can't be invalid at this point"); } -void parseInputMachO(MachOUniversalBinary *UB) { +void objdump::parseInputMachO(MachOUniversalBinary *UB) { if (!ValidateArchFlags()) return; @@ -2562,6 +2556,7 @@ void parseInputMachO(MachOUniversalBinary *UB) { } } +namespace { // The block of info used by the Symbolizer call backs. struct DisassembleInfo { DisassembleInfo(MachOObjectFile *O, SymbolAddressMap *AddrMap, @@ -2581,6 +2576,7 @@ struct DisassembleInfo { std::unique_ptr<SymbolAddressMap> bindtable; uint32_t depth = 0; }; +} // namespace // SymbolizerGetOpInfo() is the operand information call back function. // This is called to get the symbolic information for operand(s) of an @@ -3358,7 +3354,7 @@ static const char *get_symbol_64(uint32_t sect_offset, SectionRef S, // and return its name. const char *SymbolName = nullptr; if (reloc_found && isExtern) { - n_value = Symbol.getValue(); + n_value = cantFail(Symbol.getValue()); StringRef Name = unwrapOrError(Symbol.getName(), info->O->getFileName()); if (!Name.empty()) { SymbolName = Name.data(); @@ -3389,6 +3385,8 @@ static const char *get_symbol_32(uint32_t sect_offset, SectionRef S, return get_symbol_64(sect_offset, S, info, n_value64, ReferenceValue); } +namespace { + // These are structs in the Objective-C meta data and read to produce the // comments for disassembly. While these are part of the ABI they are no // public defintions. So the are here not in include/llvm/BinaryFormat/MachO.h @@ -3974,6 +3972,8 @@ inline void swapStruct(struct objc_method_description_t &md) { sys::swapByteOrder(md.types); } +} // namespace + static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, struct DisassembleInfo *info); @@ -6473,7 +6473,7 @@ static void DumpProtocolSection(MachOObjectFile *O, const char *sect, } #ifdef HAVE_LIBXAR -inline void swapStruct(struct xar_header &xar) { +static inline void swapStruct(struct xar_header &xar) { sys::swapByteOrder(xar.magic); sys::swapByteOrder(xar.size); sys::swapByteOrder(xar.version); @@ -6910,7 +6910,7 @@ static const char *GuessLiteralPointer(uint64_t ReferenceValue, if (info->O->getAnyRelocationPCRel(RE)) { unsigned Type = info->O->getAnyRelocationType(RE); if (Type == MachO::X86_64_RELOC_SIGNED) { - ReferenceValue = Symbol.getValue(); + ReferenceValue = cantFail(Symbol.getValue()); } } } @@ -7346,7 +7346,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, SmallString<1024> FullPath(DSYMPath); llvm::sys::path::append(FullPath, "Contents", "Resources", "DWARF", ShortName); - DSYMPath = FullPath.str(); + DSYMPath = std::string(FullPath.str()); } // Load the file. @@ -7451,7 +7451,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, unwrapOrError(Symbol.getType(), MachOOF->getFileName()); if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data || ST == SymbolRef::ST_Other) { - uint64_t Address = Symbol.getValue(); + uint64_t Address = cantFail(Symbol.getValue()); StringRef SymName = unwrapOrError(Symbol.getName(), MachOOF->getFileName()); AddrMap[Address] = SymName; @@ -7530,7 +7530,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, // Start at the address of the symbol relative to the section's address. uint64_t SectSize = Sections[SectIdx].getSize(); - uint64_t Start = Symbols[SymIdx].getValue(); + uint64_t Start = cantFail(Symbols[SymIdx].getValue()); uint64_t SectionAddress = Sections[SectIdx].getAddress(); Start -= SectionAddress; @@ -7551,7 +7551,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, if (NextSymType == SymbolRef::ST_Function) { containsNextSym = Sections[SectIdx].containsSymbol(Symbols[NextSymIdx]); - NextSym = Symbols[NextSymIdx].getValue(); + NextSym = cantFail(Symbols[NextSymIdx].getValue()); NextSym -= SectionAddress; break; } @@ -7564,7 +7564,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, symbolTableWorked = true; DataRefImpl Symb = Symbols[SymIdx].getRawDataRefImpl(); - bool IsThumb = MachOOF->getSymbolFlags(Symb) & SymbolRef::SF_Thumb; + uint32_t SymbolFlags = cantFail(MachOOF->getSymbolFlags(Symb)); + bool IsThumb = SymbolFlags & SymbolRef::SF_Thumb; // We only need the dedicated Thumb target if there's a real choice // (i.e. we're not targeting M-class) and the function is Thumb. @@ -8194,7 +8195,7 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, } } -void printMachOUnwindInfo(const MachOObjectFile *Obj) { +void objdump::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 @@ -8209,7 +8210,7 @@ void printMachOUnwindInfo(const MachOObjectFile *Obj) { if (Section == Obj->section_end()) continue; - uint64_t Addr = SymRef.getValue(); + uint64_t Addr = cantFail(SymRef.getValue()); Symbols.insert(std::make_pair(Addr, SymRef)); } @@ -10202,12 +10203,12 @@ static void PrintMachHeader(const MachOObjectFile *Obj, bool verbose) { } } -void printMachOFileHeader(const object::ObjectFile *Obj) { +void objdump::printMachOFileHeader(const object::ObjectFile *Obj) { const MachOObjectFile *file = dyn_cast<const MachOObjectFile>(Obj); PrintMachHeader(file, !NonVerbose); } -void printMachOLoadCommands(const object::ObjectFile *Obj) { +void objdump::printMachOLoadCommands(const object::ObjectFile *Obj) { const MachOObjectFile *file = dyn_cast<const MachOObjectFile>(Obj); uint32_t filetype = 0; uint32_t cputype = 0; @@ -10229,7 +10230,7 @@ void printMachOLoadCommands(const object::ObjectFile *Obj) { // export trie dumping //===----------------------------------------------------------------------===// -void printMachOExportsTrie(const object::MachOObjectFile *Obj) { +static void printMachOExportsTrie(const object::MachOObjectFile *Obj) { uint64_t BaseSegmentAddress = 0; for (const auto &Command : Obj->load_commands()) { if (Command.C.cmd == MachO::LC_SEGMENT) { @@ -10308,7 +10309,7 @@ void printMachOExportsTrie(const object::MachOObjectFile *Obj) { // rebase table dumping //===----------------------------------------------------------------------===// -void printMachORebaseTable(object::MachOObjectFile *Obj) { +static void printMachORebaseTable(object::MachOObjectFile *Obj) { outs() << "segment section address type\n"; Error Err = Error::success(); for (const object::MachORebaseEntry &Entry : Obj->rebaseTable(Err)) { @@ -10350,7 +10351,7 @@ static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { // bind table dumping //===----------------------------------------------------------------------===// -void printMachOBindTable(object::MachOObjectFile *Obj) { +static 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"; @@ -10381,7 +10382,7 @@ void printMachOBindTable(object::MachOObjectFile *Obj) { // lazy bind table dumping //===----------------------------------------------------------------------===// -void printMachOLazyBindTable(object::MachOObjectFile *Obj) { +static void printMachOLazyBindTable(object::MachOObjectFile *Obj) { outs() << "segment section address " "dylib symbol\n"; Error Err = Error::success(); @@ -10406,7 +10407,7 @@ void printMachOLazyBindTable(object::MachOObjectFile *Obj) { // weak bind table dumping //===----------------------------------------------------------------------===// -void printMachOWeakBindTable(object::MachOObjectFile *Obj) { +static void printMachOWeakBindTable(object::MachOObjectFile *Obj) { outs() << "segment section address " "type addend symbol\n"; Error Err = Error::success(); @@ -10456,7 +10457,7 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, return !name.empty() ? name.data() : nullptr; } -void printLazyBindTable(ObjectFile *o) { +void objdump::printLazyBindTable(ObjectFile *o) { outs() << "Lazy bind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOLazyBindTable(MachO); @@ -10466,7 +10467,7 @@ void printLazyBindTable(ObjectFile *o) { "for Mach-O executable files.\n"; } -void printWeakBindTable(ObjectFile *o) { +void objdump::printWeakBindTable(ObjectFile *o) { outs() << "Weak bind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOWeakBindTable(MachO); @@ -10476,7 +10477,7 @@ void printWeakBindTable(ObjectFile *o) { "for Mach-O executable files.\n"; } -void printExportsTrie(const ObjectFile *o) { +void objdump::printExportsTrie(const ObjectFile *o) { outs() << "Exports trie:\n"; if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOExportsTrie(MachO); @@ -10486,7 +10487,7 @@ void printExportsTrie(const ObjectFile *o) { "for Mach-O executable files.\n"; } -void printRebaseTable(ObjectFile *o) { +void objdump::printRebaseTable(ObjectFile *o) { outs() << "Rebase table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachORebaseTable(MachO); @@ -10496,7 +10497,7 @@ void printRebaseTable(ObjectFile *o) { "for Mach-O executable files.\n"; } -void printBindTable(ObjectFile *o) { +void objdump::printBindTable(ObjectFile *o) { outs() << "Bind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOBindTable(MachO); @@ -10505,4 +10506,3 @@ void printBindTable(ObjectFile *o) { << "This operation is only currently supported " "for Mach-O executable files.\n"; } -} // namespace llvm diff --git a/llvm/tools/llvm-objdump/MachODump.h b/llvm/tools/llvm-objdump/MachODump.h new file mode 100644 index 0000000000000..adf6c3404f790 --- /dev/null +++ b/llvm/tools/llvm-objdump/MachODump.h @@ -0,0 +1,66 @@ +//===-- MachODump.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_MACHODUMP_H +#define LLVM_TOOLS_LLVM_OBJDUMP_MACHODUMP_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/CommandLine.h" + +namespace llvm { + +class Error; +class StringRef; + +namespace object { +class MachOObjectFile; +class MachOUniversalBinary; +class ObjectFile; +class RelocationRef; +} // namespace object + +namespace objdump { + +// MachO specific options +extern cl::OptionCategory MachOCat; +extern cl::opt<bool> Bind; +extern cl::opt<bool> DataInCode; +extern cl::opt<bool> DylibsUsed; +extern cl::opt<bool> DylibId; +extern cl::opt<bool> ExportsTrie; +extern cl::opt<bool> FirstPrivateHeader; +extern cl::opt<bool> IndirectSymbols; +extern cl::opt<bool> InfoPlist; +extern cl::opt<bool> LazyBind; +extern cl::opt<bool> LinkOptHints; +extern cl::opt<bool> ObjcMetaData; +extern cl::opt<bool> Rebase; +extern cl::opt<bool> UniversalHeaders; +extern cl::opt<bool> WeakBind; + +Error getMachORelocationValueString(const object::MachOObjectFile *Obj, + const object::RelocationRef &RelRef, + llvm::SmallVectorImpl<char> &Result); + +void parseInputMachO(StringRef Filename); +void parseInputMachO(object::MachOUniversalBinary *UB); + +void printMachOUnwindInfo(const object::MachOObjectFile *O); +void printMachOFileHeader(const object::ObjectFile *O); +void printMachOLoadCommands(const object::ObjectFile *O); + +void printExportsTrie(const object::ObjectFile *O); +void printRebaseTable(object::ObjectFile *O); +void printBindTable(object::ObjectFile *O); +void printLazyBindTable(object::ObjectFile *O); +void printWeakBindTable(object::ObjectFile *O); + +} // namespace objdump +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-objdump/WasmDump.cpp b/llvm/tools/llvm-objdump/WasmDump.cpp index da27a4acbb5fb..28311361d97e9 100644 --- a/llvm/tools/llvm-objdump/WasmDump.cpp +++ b/llvm/tools/llvm-objdump/WasmDump.cpp @@ -11,13 +11,15 @@ /// //===----------------------------------------------------------------------===// +#include "WasmDump.h" + #include "llvm-objdump.h" #include "llvm/Object/Wasm.h" +using namespace llvm; using namespace llvm::object; -namespace llvm { -void printWasmFileHeader(const object::ObjectFile *Obj) { +void objdump::printWasmFileHeader(const object::ObjectFile *Obj) { const auto *File = dyn_cast<const WasmObjectFile>(Obj); outs() << "Program Header:\n"; @@ -26,9 +28,9 @@ void printWasmFileHeader(const object::ObjectFile *Obj) { outs() << "\n"; } -Error getWasmRelocationValueString(const WasmObjectFile *Obj, - const RelocationRef &RelRef, - SmallVectorImpl<char> &Result) { +Error objdump::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; @@ -49,4 +51,3 @@ Error getWasmRelocationValueString(const WasmObjectFile *Obj, Result.append(FmtBuf.begin(), FmtBuf.end()); return Error::success(); } -} // namespace llvm diff --git a/llvm/tools/llvm-objdump/WasmDump.h b/llvm/tools/llvm-objdump/WasmDump.h new file mode 100644 index 0000000000000..03ff9aed8e07b --- /dev/null +++ b/llvm/tools/llvm-objdump/WasmDump.h @@ -0,0 +1,35 @@ +//===-- WasmDump.h - wasm-specific dumper -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_WASMDUMP_H +#define LLVM_TOOLS_LLVM_OBJDUMP_WASMDUMP_H + +#include "llvm/ADT/SmallVector.h" + +namespace llvm { + +class Error; + +namespace object { +class WasmObjectFile; +class ObjectFile; +class RelocationRef; +} // namespace object + +namespace objdump { + +Error getWasmRelocationValueString(const object::WasmObjectFile *Obj, + const object::RelocationRef &RelRef, + llvm::SmallVectorImpl<char> &Result); + +void printWasmFileHeader(const object::ObjectFile *O); + +} // namespace objdump +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-objdump/XCOFFDump.cpp b/llvm/tools/llvm-objdump/XCOFFDump.cpp new file mode 100644 index 0000000000000..df37abbd38813 --- /dev/null +++ b/llvm/tools/llvm-objdump/XCOFFDump.cpp @@ -0,0 +1,88 @@ +//===-- XCOFFDump.cpp - XCOFF-specific dumper -----------------------------===// +// +// 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 XCOFF-specific dumper for llvm-objdump. +/// +//===----------------------------------------------------------------------===// + +#include "XCOFFDump.h" + +#include "llvm-objdump.h" +#include "llvm/Demangle/Demangle.h" + +using namespace llvm; +using namespace llvm::object; + +Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile *Obj, + const RelocationRef &Rel, + SmallVectorImpl<char> &Result) { + symbol_iterator SymI = Rel.getSymbol(); + if (SymI == Obj->symbol_end()) + return make_error<GenericBinaryError>( + "invalid symbol reference in relocation entry", + object_error::parse_failed); + + Expected<StringRef> SymNameOrErr = SymI->getName(); + if (!SymNameOrErr) + return SymNameOrErr.takeError(); + + std::string SymName = (*SymNameOrErr).str(); + if (Demangle) + SymName = demangle(SymName); + + if (SymbolDescription) + SymName = getXCOFFSymbolDescription(createSymbolInfo(Obj, *SymI), SymName); + + Result.append(SymName.begin(), SymName.end()); + return Error::success(); +} + +Optional<XCOFF::StorageMappingClass> +objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile *Obj, + const SymbolRef &Sym) { + XCOFFSymbolRef SymRef(Sym.getRawDataRefImpl(), Obj); + + if (SymRef.hasCsectAuxEnt()) + return SymRef.getXCOFFCsectAuxEnt32()->StorageMappingClass; + + return None; +} + +bool objdump::isLabel(const XCOFFObjectFile *Obj, const SymbolRef &Sym) { + + XCOFFSymbolRef SymRef(Sym.getRawDataRefImpl(), Obj); + + if (SymRef.hasCsectAuxEnt()) + return SymRef.getXCOFFCsectAuxEnt32()->isLabel(); + + return false; +} + +std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo, + StringRef SymbolName) { + assert(SymbolInfo.isXCOFF() && "Must be a XCOFFSymInfo."); + + std::string Result; + // Dummy symbols have no symbol index. + if (SymbolInfo.XCOFFSymInfo.Index) + Result = ("(idx: " + Twine(SymbolInfo.XCOFFSymInfo.Index.getValue()) + + ") " + SymbolName) + .str(); + else + Result.append(SymbolName.begin(), SymbolName.end()); + + if (SymbolInfo.XCOFFSymInfo.StorageMappingClass && + !SymbolInfo.XCOFFSymInfo.IsLabel) { + const XCOFF::StorageMappingClass Smc = + SymbolInfo.XCOFFSymInfo.StorageMappingClass.getValue(); + Result.append(("[" + XCOFF::getMappingClassString(Smc) + "]").str()); + } + + return Result; +} diff --git a/llvm/tools/llvm-objdump/XCOFFDump.h b/llvm/tools/llvm-objdump/XCOFFDump.h new file mode 100644 index 0000000000000..dbf520021594b --- /dev/null +++ b/llvm/tools/llvm-objdump/XCOFFDump.h @@ -0,0 +1,33 @@ +//===-- XCOFFDump.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_XCOFFDUMP_H +#define LLVM_TOOLS_LLVM_OBJDUMP_XCOFFDUMP_H + +#include "llvm/Object/XCOFFObjectFile.h" + +namespace llvm { + +struct SymbolInfoTy; + +namespace objdump { +Optional<XCOFF::StorageMappingClass> +getXCOFFSymbolCsectSMC(const object::XCOFFObjectFile *Obj, + const object::SymbolRef &Sym); + +bool isLabel(const object::XCOFFObjectFile *Obj, const object::SymbolRef &Sym); + +std::string getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo, + StringRef SymbolName); + +Error getXCOFFRelocationValueString(const object::XCOFFObjectFile *Obj, + const object::RelocationRef &RelRef, + llvm::SmallVectorImpl<char> &Result); +} // namespace objdump +} // namespace llvm +#endif diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index 6bd37a1fb86c9..320bbb5d358b9 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -16,7 +16,14 @@ //===----------------------------------------------------------------------===// #include "llvm-objdump.h" +#include "COFFDump.h" +#include "ELFDump.h" +#include "MachODump.h" +#include "WasmDump.h" +#include "XCOFFDump.h" +#include "llvm/ADT/IndexedMap.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/StringExtras.h" @@ -70,28 +77,13 @@ #include <unordered_map> #include <utility> +using namespace llvm; using namespace llvm::object; +using namespace llvm::objdump; -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; +#define DEBUG_TYPE "objdump" + +static cl::OptionCategory ObjdumpCat("llvm-objdump Options"); static cl::opt<uint64_t> AdjustVMA( "adjust-vma", @@ -112,21 +104,22 @@ static cl::opt<std::string> "see -version for available targets"), cl::cat(ObjdumpCat)); -cl::opt<bool> ArchiveHeaders("archive-headers", - cl::desc("Display archive header information"), - cl::cat(ObjdumpCat)); +cl::opt<bool> + objdump::ArchiveHeaders("archive-headers", + cl::desc("Display archive header information"), + cl::cat(ObjdumpCat)); static cl::alias ArchiveHeadersShort("a", cl::desc("Alias for --archive-headers"), cl::NotHidden, cl::Grouping, cl::aliasopt(ArchiveHeaders)); -cl::opt<bool> Demangle("demangle", cl::desc("Demangle symbols names"), - cl::init(false), cl::cat(ObjdumpCat)); +cl::opt<bool> objdump::Demangle("demangle", cl::desc("Demangle symbols names"), + cl::init(false), cl::cat(ObjdumpCat)); static cl::alias DemangleShort("C", cl::desc("Alias for --demangle"), cl::NotHidden, cl::Grouping, cl::aliasopt(Demangle)); -cl::opt<bool> Disassemble( +cl::opt<bool> objdump::Disassemble( "disassemble", cl::desc("Display assembler mnemonics for the machine instructions"), cl::cat(ObjdumpCat)); @@ -134,7 +127,7 @@ static cl::alias DisassembleShort("d", cl::desc("Alias for --disassemble"), cl::NotHidden, cl::Grouping, cl::aliasopt(Disassemble)); -cl::opt<bool> DisassembleAll( +cl::opt<bool> objdump::DisassembleAll( "disassemble-all", cl::desc("Display assembler mnemonics for the machine instructions"), cl::cat(ObjdumpCat)); @@ -143,12 +136,18 @@ static cl::alias DisassembleAllShort("D", cl::NotHidden, cl::Grouping, cl::aliasopt(DisassembleAll)); +cl::opt<bool> objdump::SymbolDescription( + "symbol-description", + cl::desc("Add symbol description for disassembly. This " + "option is for XCOFF files only"), + cl::init(false), cl::cat(ObjdumpCat)); + static cl::list<std::string> - 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)); + DisassembleSymbols("disassemble-symbols", cl::CommaSeparated, + cl::desc("List of symbols to disassemble. " + "Accept demangled names when --demangle is " + "specified, otherwise accept mangled names"), + cl::cat(ObjdumpCat)); static cl::opt<bool> DisassembleZeroes( "disassemble-zeroes", @@ -170,7 +169,7 @@ static cl::alias cl::CommaSeparated, cl::aliasopt(DisassemblerOptions)); -cl::opt<DIDumpType> DwarfDumpType( +cl::opt<DIDumpType> objdump::DwarfDumpType( "dwarf", cl::init(DIDT_Null), cl::desc("Dump of dwarf debug sections:"), cl::values(clEnumValN(DIDT_DebugFrame, "frames", ".debug_frame")), cl::cat(ObjdumpCat)); @@ -197,9 +196,10 @@ static cl::alias FileHeadersShort("f", cl::desc("Alias for --file-headers"), cl::NotHidden, cl::Grouping, cl::aliasopt(FileHeaders)); -cl::opt<bool> SectionContents("full-contents", - cl::desc("Display the content of each section"), - cl::cat(ObjdumpCat)); +cl::opt<bool> + objdump::SectionContents("full-contents", + cl::desc("Display the content of each section"), + cl::cat(ObjdumpCat)); static cl::alias SectionContentsShort("s", cl::desc("Alias for --full-contents"), cl::NotHidden, cl::Grouping, @@ -225,24 +225,24 @@ static cl::opt<bool> MachOOpt("macho", static cl::alias MachOm("m", cl::desc("Alias for --macho"), cl::NotHidden, cl::Grouping, cl::aliasopt(MachOOpt)); -cl::opt<std::string> - MCPU("mcpu", - cl::desc("Target a specific cpu type (-mcpu=help for details)"), - cl::value_desc("cpu-name"), cl::init(""), cl::cat(ObjdumpCat)); +cl::opt<std::string> objdump::MCPU( + "mcpu", cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), cl::init(""), cl::cat(ObjdumpCat)); -cl::list<std::string> MAttrs("mattr", cl::CommaSeparated, - cl::desc("Target specific attributes"), - cl::value_desc("a1,+a2,-a3,..."), - cl::cat(ObjdumpCat)); +cl::list<std::string> objdump::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)); +cl::opt<bool> objdump::NoShowRawInsn( + "no-show-raw-insn", + cl::desc( + "When disassembling instructions, do not print the instruction bytes."), + cl::cat(ObjdumpCat)); + +cl::opt<bool> objdump::NoLeadingAddr("no-leading-addr", + cl::desc("Print no leading address"), + cl::cat(ObjdumpCat)); static cl::opt<bool> RawClangAST( "raw-clang-ast", @@ -250,37 +250,40 @@ static cl::opt<bool> RawClangAST( cl::cat(ObjdumpCat)); cl::opt<bool> - Relocations("reloc", cl::desc("Display the relocation entries in the file"), - cl::cat(ObjdumpCat)); + objdump::Relocations("reloc", + cl::desc("Display the relocation entries in the file"), + cl::cat(ObjdumpCat)); static cl::alias RelocationsShort("r", cl::desc("Alias for --reloc"), cl::NotHidden, cl::Grouping, cl::aliasopt(Relocations)); -cl::opt<bool> PrintImmHex("print-imm-hex", - cl::desc("Use hex format for immediate values"), - cl::cat(ObjdumpCat)); +cl::opt<bool> + objdump::PrintImmHex("print-imm-hex", + cl::desc("Use hex format for immediate values"), + cl::cat(ObjdumpCat)); -cl::opt<bool> PrivateHeaders("private-headers", - cl::desc("Display format specific file headers"), - cl::cat(ObjdumpCat)); +cl::opt<bool> + objdump::PrivateHeaders("private-headers", + cl::desc("Display format specific file headers"), + cl::cat(ObjdumpCat)); static cl::alias PrivateHeadersShort("p", cl::desc("Alias for --private-headers"), cl::NotHidden, cl::Grouping, cl::aliasopt(PrivateHeaders)); cl::list<std::string> - FilterSections("section", - cl::desc("Operate on the specified sections only. " - "With -macho dump segment,section"), - cl::cat(ObjdumpCat)); + objdump::FilterSections("section", + cl::desc("Operate on the specified sections only. " + "With -macho dump segment,section"), + cl::cat(ObjdumpCat)); static cl::alias FilterSectionsj("j", cl::desc("Alias for --section"), cl::NotHidden, cl::Grouping, cl::Prefix, cl::aliasopt(FilterSections)); -cl::opt<bool> SectionHeaders("section-headers", - cl::desc("Display summaries of the " - "headers for each section."), - cl::cat(ObjdumpCat)); +cl::opt<bool> objdump::SectionHeaders( + "section-headers", + cl::desc("Display summaries of the headers for each section."), + cl::cat(ObjdumpCat)); static cl::alias SectionHeadersShort("headers", cl::desc("Alias for --section-headers"), cl::NotHidden, @@ -312,19 +315,30 @@ static cl::opt<uint64_t> StopAddress("stop-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)); +cl::opt<bool> objdump::SymbolTable("syms", cl::desc("Display the symbol table"), + cl::cat(ObjdumpCat)); static cl::alias SymbolTableShort("t", cl::desc("Alias for --syms"), cl::NotHidden, cl::Grouping, cl::aliasopt(SymbolTable)); -cl::opt<std::string> TripleName("triple", - cl::desc("Target triple to disassemble for, " - "see -version for available targets"), - cl::cat(ObjdumpCat)); +static cl::opt<bool> DynamicSymbolTable( + "dynamic-syms", + cl::desc("Display the contents of the dynamic symbol table"), + cl::cat(ObjdumpCat)); +static cl::alias DynamicSymbolTableShort("T", + cl::desc("Alias for --dynamic-syms"), + cl::NotHidden, cl::Grouping, + cl::aliasopt(DynamicSymbolTable)); -cl::opt<bool> UnwindInfo("unwind-info", cl::desc("Display unwind information"), - cl::cat(ObjdumpCat)); +cl::opt<std::string> objdump::TripleName( + "triple", + cl::desc( + "Target triple to disassemble for, see -version for available targets"), + cl::cat(ObjdumpCat)); + +cl::opt<bool> objdump::UnwindInfo("unwind-info", + cl::desc("Display unwind information"), + cl::cat(ObjdumpCat)); static cl::alias UnwindInfoShort("u", cl::desc("Alias for --unwind-info"), cl::NotHidden, cl::Grouping, cl::aliasopt(UnwindInfo)); @@ -334,15 +348,35 @@ static cl::opt<bool> cl::cat(ObjdumpCat)); static cl::alias WideShort("w", cl::Grouping, cl::aliasopt(Wide)); +enum DebugVarsFormat { + DVDisabled, + DVUnicode, + DVASCII, +}; + +static cl::opt<DebugVarsFormat> DbgVariables( + "debug-vars", cl::init(DVDisabled), + cl::desc("Print the locations (in registers or memory) of " + "source-level variables alongside disassembly"), + cl::ValueOptional, + cl::values(clEnumValN(DVUnicode, "", "unicode"), + clEnumValN(DVUnicode, "unicode", "unicode"), + clEnumValN(DVASCII, "ascii", "unicode")), + cl::cat(ObjdumpCat)); + +static cl::opt<int> + DbgIndent("debug-vars-indent", cl::init(40), + cl::desc("Distance to indent the source-level variable display, " + "relative to the start of the disassembly"), + cl::cat(ObjdumpCat)); + static cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); -static StringSet<> DisasmFuncsSet; -static StringSet<> FoundSectionSet; +static StringSet<> DisasmSymbolSet; +StringSet<> objdump::FoundSectionSet; static StringRef ToolName; -typedef std::vector<std::tuple<uint64_t, StringRef, uint8_t>> SectionSymbolsTy; - namespace { struct FilterResult { // True if the section should not be skipped. @@ -378,7 +412,8 @@ static FilterResult checkSectionFilter(object::SectionRef S) { /*IncrementIndex=*/true}; } -SectionFilter ToolSectionFilter(object::ObjectFile const &O, uint64_t *Idx) { +SectionFilter objdump::ToolSectionFilter(object::ObjectFile const &O, + uint64_t *Idx) { // Start at UINT64_MAX so that the first index returned after an increment is // zero (after the unsigned wrap). if (Idx) @@ -393,35 +428,37 @@ SectionFilter ToolSectionFilter(object::ObjectFile const &O, uint64_t *Idx) { O); } -std::string getFileNameForError(const object::Archive::Child &C, - unsigned Index) { +std::string objdump::getFileNameForError(const object::Archive::Child &C, + unsigned Index) { Expected<StringRef> NameOrErr = C.getName(); if (NameOrErr) - return NameOrErr.get(); + return std::string(NameOrErr.get()); // If we have an error getting the name then we print the index of the archive // member. Since we are already in an error state, we just ignore this error. consumeError(NameOrErr.takeError()); return "<file index: " + std::to_string(Index) + ">"; } -void reportWarning(Twine Message, StringRef File) { +void objdump::reportWarning(Twine Message, StringRef File) { // Output order between errs() and outs() matters especially for archive // files where the output is per member object. outs().flush(); WithColor::warning(errs(), ToolName) << "'" << File << "': " << Message << "\n"; - errs().flush(); } -LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Twine Message) { +LLVM_ATTRIBUTE_NORETURN void objdump::reportError(StringRef File, + Twine Message) { + outs().flush(); WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n"; exit(1); } -LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName, - StringRef ArchiveName, - StringRef ArchitectureName) { +LLVM_ATTRIBUTE_NORETURN void objdump::reportError(Error E, StringRef FileName, + StringRef ArchiveName, + StringRef ArchitectureName) { assert(E); + outs().flush(); WithColor::error(errs(), ToolName); if (ArchiveName != "") errs() << ArchiveName << "(" << FileName << ")"; @@ -429,11 +466,8 @@ LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName, errs() << "'" << FileName << "'"; if (!ArchitectureName.empty()) errs() << " (for architecture " << ArchitectureName << ")"; - std::string Buf; - raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS); - OS.flush(); - errs() << ": " << Buf; + errs() << ": "; + logAllUnhandledErrors(std::move(E), errs()); exit(1); } @@ -487,7 +521,7 @@ static const Target *getTarget(const ObjectFile *Obj) { return TheTarget; } -bool isRelocAddressLess(RelocationRef A, RelocationRef B) { +bool objdump::isRelocAddressLess(RelocationRef A, RelocationRef B) { return A.getOffset() < B.getOffset(); } @@ -502,6 +536,8 @@ static Error getRelocationValueString(const RelocationRef &Rel, return getWasmRelocationValueString(Wasm, Rel, Result); if (auto *MachO = dyn_cast<MachOObjectFile>(Obj)) return getMachORelocationValueString(MachO, Rel, Result); + if (auto *XCOFF = dyn_cast<XCOFFObjectFile>(Obj)) + return getXCOFFRelocationValueString(XCOFF, Rel, Result); llvm_unreachable("unknown object file format"); } @@ -538,6 +574,358 @@ static bool getHidden(RelocationRef RelRef) { } namespace { + +/// Get the column at which we want to start printing the instruction +/// disassembly, taking into account anything which appears to the left of it. +unsigned getInstStartColumn(const MCSubtargetInfo &STI) { + return NoShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; +} + +/// Stores a single expression representing the location of a source-level +/// variable, along with the PC range for which that expression is valid. +struct LiveVariable { + DWARFLocationExpression LocExpr; + const char *VarName; + DWARFUnit *Unit; + const DWARFDie FuncDie; + + LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName, + DWARFUnit *Unit, const DWARFDie FuncDie) + : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {} + + bool liveAtAddress(object::SectionedAddress Addr) { + if (LocExpr.Range == None) + return false; + return LocExpr.Range->SectionIndex == Addr.SectionIndex && + LocExpr.Range->LowPC <= Addr.Address && + LocExpr.Range->HighPC > Addr.Address; + } + + void print(raw_ostream &OS, const MCRegisterInfo &MRI) const { + DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()}, + Unit->getContext().isLittleEndian(), 0); + DWARFExpression Expression(Data, Unit->getAddressByteSize()); + Expression.printCompact(OS, MRI); + } +}; + +/// Helper class for printing source variable locations alongside disassembly. +class LiveVariablePrinter { + // Information we want to track about one column in which we are printing a + // variable live range. + struct Column { + unsigned VarIdx = NullVarIdx; + bool LiveIn = false; + bool LiveOut = false; + bool MustDrawLabel = false; + + bool isActive() const { return VarIdx != NullVarIdx; } + + static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max(); + }; + + // All live variables we know about in the object/image file. + std::vector<LiveVariable> LiveVariables; + + // The columns we are currently drawing. + IndexedMap<Column> ActiveCols; + + const MCRegisterInfo &MRI; + const MCSubtargetInfo &STI; + + void addVariable(DWARFDie FuncDie, DWARFDie VarDie) { + uint64_t FuncLowPC, FuncHighPC, SectionIndex; + FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex); + const char *VarName = VarDie.getName(DINameKind::ShortName); + DWARFUnit *U = VarDie.getDwarfUnit(); + + Expected<DWARFLocationExpressionsVector> Locs = + VarDie.getLocations(dwarf::DW_AT_location); + if (!Locs) { + // If the variable doesn't have any locations, just ignore it. We don't + // report an error or warning here as that could be noisy on optimised + // code. + consumeError(Locs.takeError()); + return; + } + + for (const DWARFLocationExpression &LocExpr : *Locs) { + if (LocExpr.Range) { + LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie); + } else { + // If the LocExpr does not have an associated range, it is valid for + // the whole of the function. + // TODO: technically it is not valid for any range covered by another + // LocExpr, does that happen in reality? + DWARFLocationExpression WholeFuncExpr{ + DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), + LocExpr.Expr}; + LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie); + } + } + } + + void addFunction(DWARFDie D) { + for (const DWARFDie &Child : D.children()) { + if (Child.getTag() == dwarf::DW_TAG_variable || + Child.getTag() == dwarf::DW_TAG_formal_parameter) + addVariable(D, Child); + else + addFunction(Child); + } + } + + // Get the column number (in characters) at which the first live variable + // line should be printed. + unsigned getIndentLevel() const { + return DbgIndent + getInstStartColumn(STI); + } + + // Indent to the first live-range column to the right of the currently + // printed line, and return the index of that column. + // TODO: formatted_raw_ostream uses "column" to mean a number of characters + // since the last \n, and we use it to mean the number of slots in which we + // put live variable lines. Pick a less overloaded word. + unsigned moveToFirstVarColumn(formatted_raw_ostream &OS) { + // Logical column number: column zero is the first column we print in, each + // logical column is 2 physical columns wide. + unsigned FirstUnprintedLogicalColumn = + std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0); + // Physical column number: the actual column number in characters, with + // zero being the left-most side of the screen. + unsigned FirstUnprintedPhysicalColumn = + getIndentLevel() + FirstUnprintedLogicalColumn * 2; + + if (FirstUnprintedPhysicalColumn > OS.getColumn()) + OS.PadToColumn(FirstUnprintedPhysicalColumn); + + return FirstUnprintedLogicalColumn; + } + + unsigned findFreeColumn() { + for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx) + if (!ActiveCols[ColIdx].isActive()) + return ColIdx; + + size_t OldSize = ActiveCols.size(); + ActiveCols.grow(std::max<size_t>(OldSize * 2, 1)); + return OldSize; + } + +public: + LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) + : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {} + + void dump() const { + for (const LiveVariable &LV : LiveVariables) { + dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": "; + LV.print(dbgs(), MRI); + dbgs() << "\n"; + } + } + + void addCompileUnit(DWARFDie D) { + if (D.getTag() == dwarf::DW_TAG_subprogram) + addFunction(D); + else + for (const DWARFDie &Child : D.children()) + addFunction(Child); + } + + /// Update to match the state of the instruction between ThisAddr and + /// NextAddr. In the common case, any live range active at ThisAddr is + /// live-in to the instruction, and any live range active at NextAddr is + /// live-out of the instruction. If IncludeDefinedVars is false, then live + /// ranges starting at NextAddr will be ignored. + void update(object::SectionedAddress ThisAddr, + object::SectionedAddress NextAddr, bool IncludeDefinedVars) { + // First, check variables which have already been assigned a column, so + // that we don't change their order. + SmallSet<unsigned, 8> CheckedVarIdxs; + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { + if (!ActiveCols[ColIdx].isActive()) + continue; + CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx); + LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx]; + ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr); + ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr); + LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-" + << NextAddr.Address << ", " << LV.VarName << ", Col " + << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn + << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n"); + + if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) + ActiveCols[ColIdx].VarIdx = Column::NullVarIdx; + } + + // Next, look for variables which don't already have a column, but which + // are now live. + if (IncludeDefinedVars) { + for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End; + ++VarIdx) { + if (CheckedVarIdxs.count(VarIdx)) + continue; + LiveVariable &LV = LiveVariables[VarIdx]; + bool LiveIn = LV.liveAtAddress(ThisAddr); + bool LiveOut = LV.liveAtAddress(NextAddr); + if (!LiveIn && !LiveOut) + continue; + + unsigned ColIdx = findFreeColumn(); + LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-" + << NextAddr.Address << ", " << LV.VarName << ", Col " + << ColIdx << ": LiveIn=" << LiveIn + << ", LiveOut=" << LiveOut << "\n"); + ActiveCols[ColIdx].VarIdx = VarIdx; + ActiveCols[ColIdx].LiveIn = LiveIn; + ActiveCols[ColIdx].LiveOut = LiveOut; + ActiveCols[ColIdx].MustDrawLabel = true; + } + } + } + + enum class LineChar { + RangeStart, + RangeMid, + RangeEnd, + LabelVert, + LabelCornerNew, + LabelCornerActive, + LabelHoriz, + }; + const char *getLineChar(LineChar C) const { + bool IsASCII = DbgVariables == DVASCII; + switch (C) { + case LineChar::RangeStart: + return IsASCII ? "^" : u8"\u2548"; + case LineChar::RangeMid: + return IsASCII ? "|" : u8"\u2503"; + case LineChar::RangeEnd: + return IsASCII ? "v" : u8"\u253b"; + case LineChar::LabelVert: + return IsASCII ? "|" : u8"\u2502"; + case LineChar::LabelCornerNew: + return IsASCII ? "/" : u8"\u250c"; + case LineChar::LabelCornerActive: + return IsASCII ? "|" : u8"\u2520"; + case LineChar::LabelHoriz: + return IsASCII ? "-" : u8"\u2500"; + } + llvm_unreachable("Unhandled LineChar enum"); + } + + /// Print live ranges to the right of an existing line. This assumes the + /// line is not an instruction, so doesn't start or end any live ranges, so + /// we only need to print active ranges or empty columns. If AfterInst is + /// true, this is being printed after the last instruction fed to update(), + /// otherwise this is being printed before it. + void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst) { + if (ActiveCols.size()) { + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx < End; ++ColIdx) { + if (ActiveCols[ColIdx].isActive()) { + if ((AfterInst && ActiveCols[ColIdx].LiveOut) || + (!AfterInst && ActiveCols[ColIdx].LiveIn)) + OS << getLineChar(LineChar::RangeMid); + else if (!AfterInst && ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::LabelVert); + else + OS << " "; + } + OS << " "; + } + } + OS << "\n"; + } + + /// Print any live variable range info needed to the right of a + /// non-instruction line of disassembly. This is where we print the variable + /// names and expressions, with thin line-drawing characters connecting them + /// to the live range which starts at the next instruction. If MustPrint is + /// true, we have to print at least one line (with the continuation of any + /// already-active live ranges) because something has already been printed + /// earlier on this line. + void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint) { + bool PrintedSomething = false; + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { + if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) { + // First we need to print the live range markers for any active + // columns to the left of this one. + OS.PadToColumn(getIndentLevel()); + for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) { + if (ActiveCols[ColIdx2].isActive()) { + if (ActiveCols[ColIdx2].MustDrawLabel && + !ActiveCols[ColIdx2].LiveIn) + OS << getLineChar(LineChar::LabelVert) << " "; + else + OS << getLineChar(LineChar::RangeMid) << " "; + } else + OS << " "; + } + + // Then print the variable name and location of the new live range, + // with box drawing characters joining it to the live range line. + OS << getLineChar(ActiveCols[ColIdx].LiveIn + ? LineChar::LabelCornerActive + : LineChar::LabelCornerNew) + << getLineChar(LineChar::LabelHoriz) << " "; + WithColor(OS, raw_ostream::GREEN) + << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName; + OS << " = "; + { + WithColor ExprColor(OS, raw_ostream::CYAN); + LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI); + } + + // If there are any columns to the right of the expression we just + // printed, then continue their live range lines. + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx2 < End; ++ColIdx2) { + if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn) + OS << getLineChar(LineChar::RangeMid) << " "; + else + OS << " "; + } + + OS << "\n"; + PrintedSomething = true; + } + } + + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) + if (ActiveCols[ColIdx].isActive()) + ActiveCols[ColIdx].MustDrawLabel = false; + + // If we must print something (because we printed a line/column number), + // but don't have any new variables to print, then print a line which + // just continues any existing live ranges. + if (MustPrint && !PrintedSomething) + printAfterOtherLine(OS, false); + } + + /// Print the live variable ranges to the right of a disassembled instruction. + void printAfterInst(formatted_raw_ostream &OS) { + if (!ActiveCols.size()) + return; + unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS); + for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size(); + ColIdx < End; ++ColIdx) { + if (!ActiveCols[ColIdx].isActive()) + OS << " "; + else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::RangeMid) << " "; + else if (ActiveCols[ColIdx].LiveOut) + OS << getLineChar(LineChar::RangeStart) << " "; + else if (ActiveCols[ColIdx].LiveIn) + OS << getLineChar(LineChar::RangeEnd) << " "; + else + llvm_unreachable("var must be live in or out!"); + } + } +}; + class SourcePrinter { protected: DILineInfo OldLineInfo; @@ -555,20 +943,29 @@ protected: private: bool cacheSource(const DILineInfo& LineInfoFile); + void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo, + StringRef Delimiter, LiveVariablePrinter &LVP); + + void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter, + LiveVariablePrinter &LVP); + public: SourcePrinter() = default; SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) : Obj(Obj), WarnedNoDebugInfo(false) { symbolize::LLVMSymbolizer::Options SymbolizerOpts; - SymbolizerOpts.PrintFunctions = DILineInfoSpecifier::FunctionNameKind::None; - SymbolizerOpts.Demangle = false; - SymbolizerOpts.DefaultArch = DefaultArch; + SymbolizerOpts.PrintFunctions = + DILineInfoSpecifier::FunctionNameKind::LinkageName; + SymbolizerOpts.Demangle = Demangle; + SymbolizerOpts.DefaultArch = std::string(DefaultArch); Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts)); } virtual ~SourcePrinter() = default; - virtual void printSourceLine(raw_ostream &OS, + virtual void printSourceLine(formatted_raw_ostream &OS, object::SectionedAddress Address, StringRef ObjectFilename, + LiveVariablePrinter &LVP, StringRef Delimiter = "; "); }; @@ -602,9 +999,10 @@ bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { return true; } -void SourcePrinter::printSourceLine(raw_ostream &OS, +void SourcePrinter::printSourceLine(formatted_raw_ostream &OS, object::SectionedAddress Address, StringRef ObjectFilename, + LiveVariablePrinter &LVP, StringRef Delimiter) { if (!Symbolizer) return; @@ -626,34 +1024,62 @@ void SourcePrinter::printSourceLine(raw_ostream &OS, reportWarning(Warning, ObjectFilename); WarnedNoDebugInfo = true; } - return; } - if (LineInfo.Line == 0 || ((OldLineInfo.Line == LineInfo.Line) && - (OldLineInfo.FileName == LineInfo.FileName))) + if (PrintLines) + printLines(OS, LineInfo, Delimiter, LVP); + if (PrintSource) + printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP); + OldLineInfo = LineInfo; +} + +void SourcePrinter::printLines(formatted_raw_ostream &OS, + const DILineInfo &LineInfo, StringRef Delimiter, + LiveVariablePrinter &LVP) { + bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString && + LineInfo.FunctionName != OldLineInfo.FunctionName; + if (PrintFunctionName) { + OS << Delimiter << LineInfo.FunctionName; + // If demangling is successful, FunctionName will end with "()". Print it + // only if demangling did not run or was unsuccessful. + if (!StringRef(LineInfo.FunctionName).endswith("()")) + OS << "()"; + OS << ":\n"; + } + if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 && + (OldLineInfo.Line != LineInfo.Line || + OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) { + OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line; + LVP.printBetweenInsts(OS, true); + } +} + +void SourcePrinter::printSources(formatted_raw_ostream &OS, + const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter, + LiveVariablePrinter &LVP) { + if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 || + (OldLineInfo.Line == LineInfo.Line && + OldLineInfo.FileName == LineInfo.FileName)) return; - if (PrintLines) - OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n"; - if (PrintSource) { - if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) - if (!cacheSource(LineInfo)) - return; - auto LineBuffer = LineCache.find(LineInfo.FileName); - if (LineBuffer != LineCache.end()) { - if (LineInfo.Line > LineBuffer->second.size()) { - reportWarning( - formatv( - "debug info line number {0} exceeds the number of lines in {1}", - LineInfo.Line, LineInfo.FileName), - ObjectFilename); - return; - } - // Vector begins at 0, line numbers are non-zero - OS << Delimiter << LineBuffer->second[LineInfo.Line - 1] << '\n'; + if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) + if (!cacheSource(LineInfo)) + return; + auto LineBuffer = LineCache.find(LineInfo.FileName); + if (LineBuffer != LineCache.end()) { + if (LineInfo.Line > LineBuffer->second.size()) { + reportWarning( + formatv( + "debug info line number {0} exceeds the number of lines in {1}", + LineInfo.Line, LineInfo.FileName), + ObjectFilename); + return; } + // Vector begins at 0, line numbers are non-zero + OS << Delimiter << LineBuffer->second[LineInfo.Line - 1]; + LVP.printBetweenInsts(OS, true); } - OldLineInfo = LineInfo; } static bool isAArch64Elf(const ObjectFile *Obj) { @@ -670,28 +1096,30 @@ static bool hasMappingSymbols(const ObjectFile *Obj) { return isArmElf(Obj) || isAArch64Elf(Obj); } -static void printRelocation(StringRef FileName, const RelocationRef &Rel, - uint64_t Address, bool Is64Bits) { +static void printRelocation(formatted_raw_ostream &OS, StringRef FileName, + 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); if (Error E = getRelocationValueString(Rel, Val)) reportError(std::move(E), FileName); - outs() << format(Fmt.data(), Address) << Name << "\t" << Val << "\n"; + OS << format(Fmt.data(), Address) << Name << "\t" << Val; } class PrettyPrinter { public: virtual ~PrettyPrinter() = default; - virtual void printInst(MCInstPrinter &IP, const MCInst *MI, - ArrayRef<uint8_t> Bytes, - object::SectionedAddress Address, raw_ostream &OS, - StringRef Annot, MCSubtargetInfo const &STI, - SourcePrinter *SP, StringRef ObjectFilename, - std::vector<RelocationRef> *Rels = nullptr) { + virtual void + printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes, + object::SectionedAddress Address, formatted_raw_ostream &OS, + StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, + StringRef ObjectFilename, std::vector<RelocationRef> *Rels, + LiveVariablePrinter &LVP) { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename); + SP->printSourceLine(OS, Address, ObjectFilename, LVP); + LVP.printBetweenInsts(OS, false); size_t Start = OS.tell(); if (!NoLeadingAddr) @@ -703,13 +1131,19 @@ public: // 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 TabStop = getInstStartColumn(STI); unsigned Column = OS.tell() - Start; OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8); - if (MI) - IP.printInst(MI, Address.Address, "", STI, OS); - else + if (MI) { + // See MCInstPrinter::printInst. On targets where a PC relative immediate + // is relative to the next instruction and the length of a MCInst is + // difficult to measure (x86), this is the address of the next + // instruction. + uint64_t Addr = + Address.Address + (STI.getTargetTriple().isX86() ? Bytes.size() : 0); + IP.printInst(MI, Addr, "", STI, OS); + } else OS << "\t<unknown>"; } }; @@ -718,7 +1152,7 @@ PrettyPrinter PrettyPrinterInst; class HexagonPrettyPrinter : public PrettyPrinter { public: void printLead(ArrayRef<uint8_t> Bytes, uint64_t Address, - raw_ostream &OS) { + formatted_raw_ostream &OS) { uint32_t opcode = (Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0]; if (!NoLeadingAddr) @@ -730,12 +1164,12 @@ public: } } void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes, - object::SectionedAddress Address, raw_ostream &OS, + object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, - StringRef ObjectFilename, - std::vector<RelocationRef> *Rels) override { + StringRef ObjectFilename, std::vector<RelocationRef> *Rels, + LiveVariablePrinter &LVP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, ""); + SP->printSourceLine(OS, Address, ObjectFilename, LVP, ""); if (!MI) { printLead(Bytes, Address.Address, OS); OS << " <unknown>"; @@ -761,7 +1195,7 @@ public: auto PrintReloc = [&]() -> void { while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address.Address)) { if (RelCur->getOffset() == Address.Address) { - printRelocation(ObjectFilename, *RelCur, Address.Address, false); + printRelocation(OS, ObjectFilename, *RelCur, Address.Address, false); return; } ++RelCur; @@ -772,7 +1206,7 @@ public: OS << Separator; Separator = "\n"; if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename, ""); + SP->printSourceLine(OS, Address, ObjectFilename, LVP, ""); printLead(Bytes, Address.Address, OS); OS << Preamble; Preamble = " "; @@ -800,12 +1234,12 @@ HexagonPrettyPrinter HexagonPrettyPrinterInst; class AMDGCNPrettyPrinter : public PrettyPrinter { public: void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes, - object::SectionedAddress Address, raw_ostream &OS, + object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, - StringRef ObjectFilename, - std::vector<RelocationRef> *Rels) override { + StringRef ObjectFilename, std::vector<RelocationRef> *Rels, + LiveVariablePrinter &LVP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename); + SP->printSourceLine(OS, Address, ObjectFilename, LVP); if (MI) { SmallString<40> InstStr; @@ -852,12 +1286,12 @@ AMDGCNPrettyPrinter AMDGCNPrettyPrinterInst; class BPFPrettyPrinter : public PrettyPrinter { public: void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes, - object::SectionedAddress Address, raw_ostream &OS, + object::SectionedAddress Address, formatted_raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, - StringRef ObjectFilename, - std::vector<RelocationRef> *Rels) override { + StringRef ObjectFilename, std::vector<RelocationRef> *Rels, + LiveVariablePrinter &LVP) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ObjectFilename); + SP->printSourceLine(OS, Address, ObjectFilename, LVP); if (!NoLeadingAddr) OS << format("%8" PRId64 ":", Address.Address / 8); if (!NoShowRawInsn) { @@ -1023,7 +1457,7 @@ getRelocsMap(object::ObjectFile const &Obj) { // TODO: implement for other file formats. static bool shouldAdjustVA(const SectionRef &Section) { const ObjectFile *Obj = Section.getObject(); - if (isa<object::ELFObjectFileBase>(Obj)) + if (Obj->isELF()) return ELFSectionRef(Section).getFlags() & ELF::SHF_ALLOC; return false; } @@ -1043,37 +1477,31 @@ static char getMappingSymbolKind(ArrayRef<MappingSymbolPair> MappingSymbols, return (It - 1)->second; } -static uint64_t -dumpARMELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End, - const ObjectFile *Obj, ArrayRef<uint8_t> Bytes, - ArrayRef<MappingSymbolPair> MappingSymbols) { +static uint64_t dumpARMELFData(uint64_t SectionAddr, uint64_t Index, + uint64_t End, const ObjectFile *Obj, + ArrayRef<uint8_t> Bytes, + ArrayRef<MappingSymbolPair> MappingSymbols, + raw_ostream &OS) { 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; + OS << format("%8" PRIx64 ":\t", SectionAddr + Index); + if (Index + 4 <= End) { + dumpBytes(Bytes.slice(Index, 4), OS); + OS << "\t.word\t" + << format_hex(support::endian::read32(Bytes.data() + Index, Endian), + 10); + return 4; + } + if (Index + 2 <= End) { + dumpBytes(Bytes.slice(Index, 2), OS); + OS << "\t\t.short\t" + << format_hex(support::endian::read16(Bytes.data() + Index, Endian), + 6); + return 2; } - return Index; + dumpBytes(Bytes.slice(Index, 1), OS); + OS << "\t\t.byte\t" << format_hex(Bytes[0], 4); + return 1; } static void dumpELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End, @@ -1110,6 +1538,36 @@ static void dumpELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End, } } +SymbolInfoTy objdump::createSymbolInfo(const ObjectFile *Obj, + const SymbolRef &Symbol) { + const StringRef FileName = Obj->getFileName(); + const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName); + const StringRef Name = unwrapOrError(Symbol.getName(), FileName); + + if (Obj->isXCOFF() && SymbolDescription) { + const auto *XCOFFObj = cast<XCOFFObjectFile>(Obj); + DataRefImpl SymbolDRI = Symbol.getRawDataRefImpl(); + + const uint32_t SymbolIndex = XCOFFObj->getSymbolIndex(SymbolDRI.p); + Optional<XCOFF::StorageMappingClass> Smc = + getXCOFFSymbolCsectSMC(XCOFFObj, Symbol); + return SymbolInfoTy(Addr, Name, Smc, SymbolIndex, + isLabel(XCOFFObj, Symbol)); + } else + return SymbolInfoTy(Addr, Name, + Obj->isELF() ? getElfSymbolType(Obj, Symbol) + : (uint8_t)ELF::STT_NOTYPE); +} + +static SymbolInfoTy createDummySymbolInfo(const ObjectFile *Obj, + const uint64_t Addr, StringRef &Name, + uint8_t Type) { + if (Obj->isXCOFF() && SymbolDescription) + return SymbolInfoTy(Addr, Name, None, None, false); + else + return SymbolInfoTy(Addr, Name, Type); +} + static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, MCContext &Ctx, MCDisassembler *PrimaryDisAsm, MCDisassembler *SecondaryDisAsm, @@ -1136,20 +1594,14 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, const StringRef FileName = Obj->getFileName(); const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(Obj); for (const SymbolRef &Symbol : Obj->symbols()) { - uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName); - StringRef Name = unwrapOrError(Symbol.getName(), FileName); - if (Name.empty()) + if (Name.empty() && !(Obj->isXCOFF() && SymbolDescription)) continue; - uint8_t SymbolType = ELF::STT_NOTYPE; - if (Obj->isELF()) { - SymbolType = getElfSymbolType(Obj, Symbol); - if (SymbolType == ELF::STT_SECTION) - continue; - } + if (Obj->isELF() && getElfSymbolType(Obj, Symbol) == ELF::STT_SECTION) + continue; - // Don't ask a Mach-O STAB symbol for its section unless you know that + // Don't ask a Mach-O STAB symbol for its section unless you know that // STAB symbol's section field refers to a valid section index. Otherwise // the symbol may error trying to load a section that does not exist. if (MachO) { @@ -1163,10 +1615,11 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName); if (SecI != Obj->section_end()) - AllSymbols[*SecI].emplace_back(Address, Name, SymbolType); + AllSymbols[*SecI].push_back(createSymbolInfo(Obj, Symbol)); else - AbsoluteSymbols.emplace_back(Address, Name, SymbolType); + AbsoluteSymbols.push_back(createSymbolInfo(Obj, Symbol)); } + if (AllSymbols.empty() && Obj->isELF()) addDynamicElfSymbols(Obj, AllSymbols); @@ -1174,25 +1627,32 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, StringSaver Saver(A); addPltEntries(Obj, AllSymbols, Saver); - // Create a mapping from virtual address to section. + // Create a mapping from virtual address to section. An empty section can + // cause more than one section at the same address. Sort such sections to be + // before same-addressed non-empty sections so that symbol lookups prefer the + // non-empty section. std::vector<std::pair<uint64_t, SectionRef>> SectionAddresses; for (SectionRef Sec : Obj->sections()) SectionAddresses.emplace_back(Sec.getAddress(), Sec); - array_pod_sort(SectionAddresses.begin(), SectionAddresses.end()); + llvm::stable_sort(SectionAddresses, [](const auto &LHS, const auto &RHS) { + if (LHS.first != RHS.first) + return LHS.first < RHS.first; + return LHS.second.getSize() < RHS.second.getSize(); + }); // Linked executables (.exe and .dll files) typically don't include a real // symbol table but they might contain an export table. if (const auto *COFFObj = dyn_cast<COFFObjectFile>(Obj)) { for (const auto &ExportEntry : COFFObj->export_directories()) { StringRef Name; - if (std::error_code EC = ExportEntry.getSymbolName(Name)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = ExportEntry.getSymbolName(Name)) + reportError(std::move(E), Obj->getFileName()); if (Name.empty()) continue; uint32_t RVA; - if (std::error_code EC = ExportEntry.getExportRVA(RVA)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = ExportEntry.getExportRVA(RVA)) + reportError(std::move(E), Obj->getFileName()); uint64_t VA = COFFObj->getImageBase() + RVA; auto Sec = partition_point( @@ -1208,11 +1668,23 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, } // Sort all the symbols, this allows us to use a simple binary search to find - // a symbol near an address. - StringSet<> FoundDisasmFuncsSet; + // Multiple symbols can have the same address. Use a stable sort to stabilize + // the output. + StringSet<> FoundDisasmSymbolSet; for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols) - array_pod_sort(SecSyms.second.begin(), SecSyms.second.end()); - array_pod_sort(AbsoluteSymbols.begin(), AbsoluteSymbols.end()); + stable_sort(SecSyms.second); + stable_sort(AbsoluteSymbols); + + std::unique_ptr<DWARFContext> DICtx; + LiveVariablePrinter LVP(*Ctx.getRegisterInfo(), *STI); + + if (DbgVariables != DVDisabled) { + DICtx = DWARFContext::create(*Obj); + for (const std::unique_ptr<DWARFUnit> &CU : DICtx->compile_units()) + LVP.addCompileUnit(CU->getUnitDIE(false)); + } + + LLVM_DEBUG(LVP.dump()); for (const SectionRef &Section : ToolSectionFilter(*Obj)) { if (FilterSections.empty() && !DisassembleAll && @@ -1229,8 +1701,8 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *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); + uint64_t Address = Symb.Addr; + StringRef Name = Symb.Name; if (Name.startswith("$d")) MappingSymbols.emplace_back(Address - SectionAddr, 'd'); if (Name.startswith("$x")) @@ -1264,11 +1736,11 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, StringRef SectionName = unwrapOrError(Section.getName(), Obj->getFileName()); // If the section has no symbol at the start, just insert a dummy one. - if (Symbols.empty() || std::get<0>(Symbols[0]) != 0) { - Symbols.insert( - Symbols.begin(), - std::make_tuple(SectionAddr, SectionName, - Section.isText() ? ELF::STT_FUNC : ELF::STT_OBJECT)); + if (Symbols.empty() || Symbols[0].Addr != 0) { + Symbols.insert(Symbols.begin(), + createDummySymbolInfo(Obj, SectionAddr, SectionName, + Section.isText() ? ELF::STT_FUNC + : ELF::STT_OBJECT)); } SmallString<40> Comments; @@ -1289,26 +1761,26 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, std::vector<RelocationRef>::const_iterator RelEnd = Rels.end(); // Disassemble symbol by symbol. for (unsigned SI = 0, SE = Symbols.size(); SI != SE; ++SI) { - std::string SymbolName = std::get<1>(Symbols[SI]).str(); + std::string SymbolName = Symbols[SI].Name.str(); if (Demangle) SymbolName = demangle(SymbolName); - // Skip if --disassemble-functions is not empty and the symbol is not in + // Skip if --disassemble-symbols is not empty and the symbol is not in // the list. - if (!DisasmFuncsSet.empty() && !DisasmFuncsSet.count(SymbolName)) + if (!DisasmSymbolSet.empty() && !DisasmSymbolSet.count(SymbolName)) continue; - uint64_t Start = std::get<0>(Symbols[SI]); + uint64_t Start = Symbols[SI].Addr; if (Start < SectionAddr || StopAddress <= Start) continue; else - FoundDisasmFuncsSet.insert(SymbolName); + FoundDisasmSymbolSet.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])); + End = std::min(End, Symbols[SI + 1].Addr); if (Start >= End || End <= StartAddress) continue; Start -= SectionAddr; @@ -1323,12 +1795,12 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, } if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) { - if (std::get<2>(Symbols[SI]) == ELF::STT_AMDGPU_HSA_KERNEL) { + if (Symbols[SI].Type == ELF::STT_AMDGPU_HSA_KERNEL) { // skip amd_kernel_code_t at the begining of kernel symbol (256 bytes) Start += 256; } if (SI == SE - 1 || - std::get<2>(Symbols[SI + 1]) == ELF::STT_AMDGPU_HSA_KERNEL) { + Symbols[SI + 1].Type == ELF::STT_AMDGPU_HSA_KERNEL) { // cut trailing zeroes at the end of kernel // cut up to 256 bytes const uint64_t EndAlign = 256; @@ -1343,8 +1815,10 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, if (!NoLeadingAddr) outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ", SectionAddr + Start + VMAAdjustment); - - outs() << SymbolName << ":\n"; + if (Obj->isXCOFF() && SymbolDescription) { + outs() << getXCOFFSymbolDescription(Symbols[SI], SymbolName) << ":\n"; + } else + outs() << '<' << SymbolName << ">:\n"; // Don't print raw contents of a virtual section. A virtual section // doesn't have any contents in the file. @@ -1353,10 +1827,37 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, continue; } - // 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, CommentStream); + auto Status = DisAsm->onSymbolStart(Symbols[SI], Size, + Bytes.slice(Start, End - Start), + SectionAddr + Start, CommentStream); + // To have round trippable disassembly, we fall back to decoding the + // remaining bytes as instructions. + // + // If there is a failure, we disassemble the failed region as bytes before + // falling back. The target is expected to print nothing in this case. + // + // If there is Success or SoftFail i.e no 'real' failure, we go ahead by + // Size bytes before falling back. + // So if the entire symbol is 'eaten' by the target: + // Start += Size // Now Start = End and we will never decode as + // // instructions + // + // Right now, most targets return None i.e ignore to treat a symbol + // separately. But WebAssembly decodes preludes for some symbols. + // + if (Status.hasValue()) { + if (Status.getValue() == MCDisassembler::Fail) { + outs() << "// Error in decoding " << SymbolName + << " : Decoding failed region as bytes.\n"; + for (uint64_t I = 0; I < Size; ++I) { + outs() << "\t.byte\t " << format_hex(Bytes[I], 1, /*Upper=*/true) + << "\n"; + } + } + } else { + Size = 0; + } + Start += Size; Index = Start; @@ -1367,7 +1868,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, // 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]); + uint8_t SymTy = Symbols[SI].Type; if (SymTy == ELF::STT_OBJECT || SymTy == ELF::STT_COMMON) { dumpELFData(SectionAddr, Index, End, Bytes); Index = End; @@ -1375,123 +1876,155 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, } bool CheckARMELFData = hasMappingSymbols(Obj) && - std::get<2>(Symbols[SI]) != ELF::STT_OBJECT && + Symbols[SI].Type != ELF::STT_OBJECT && !DisassembleAll; + bool DumpARMELFData = false; + formatted_raw_ostream FOS(outs()); 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; - } - - // 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 (CheckARMELFData) { + char Kind = getMappingSymbolKind(MappingSymbols, Index); + DumpARMELFData = Kind == 'd'; + if (SecondarySTI) { + if (Kind == 'a') { + STI = PrimaryIsThumb ? SecondarySTI : PrimarySTI; + DisAsm = PrimaryIsThumb ? SecondaryDisAsm : PrimaryDisAsm; + } else if (Kind == 't') { + STI = PrimaryIsThumb ? PrimarySTI : SecondarySTI; + DisAsm = PrimaryIsThumb ? PrimaryDisAsm : SecondaryDisAsm; + } } } - 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 (DumpARMELFData) { + Size = dumpARMELFData(SectionAddr, Index, End, Obj, Bytes, + MappingSymbols, FOS); + } else { + // 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))) { + FOS << "\t\t..." << '\n'; + Index += N; + continue; + } } - } - // Disassemble a real instruction or a data when disassemble all is - // provided - MCInst Inst; - bool Disassembled = DisAsm->getInstruction( - Inst, Size, Bytes.slice(Index), SectionAddr + Index, CommentStream); - if (Size == 0) - Size = 1; - - PIP.printInst(*IP, Disassembled ? &Inst : nullptr, - Bytes.slice(Index, Size), - {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, - outs(), "", *STI, &SP, Obj->getFileName(), &Rels); - outs() << CommentStream.str(); - Comments.clear(); - - // Try to resolve the target of a call, tail call, etc. to a specific - // symbol. - if (MIA && (MIA->isCall(Inst) || MIA->isUnconditionalBranch(Inst) || - MIA->isConditionalBranch(Inst))) { - uint64_t Target; - if (MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target)) { - // In a relocatable object, the target's section must reside in - // the same section as the call instruction or it is accessed - // through a relocation. - // - // In a non-relocatable object, the target may be in any section. - // - // N.B. We don't walk the relocations in the relocatable case yet. - auto *TargetSectionSymbols = &Symbols; - if (!Obj->isRelocatableObject()) { - auto It = partition_point( - SectionAddresses, - [=](const std::pair<uint64_t, SectionRef> &O) { - return O.first <= Target; - }); - if (It != SectionAddresses.begin()) { - --It; - TargetSectionSymbols = &AllSymbols[It->second]; + // Disassemble a real instruction or a data when disassemble all is + // provided + MCInst Inst; + bool Disassembled = + DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), + SectionAddr + Index, CommentStream); + if (Size == 0) + Size = 1; + + LVP.update({Index, Section.getIndex()}, + {Index + Size, Section.getIndex()}, Index + Size != End); + + PIP.printInst( + *IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size), + {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, FOS, + "", *STI, &SP, Obj->getFileName(), &Rels, LVP); + FOS << CommentStream.str(); + Comments.clear(); + + // If disassembly has failed, avoid analysing invalid/incomplete + // instruction information. Otherwise, try to resolve the target + // address (jump target or memory operand address) and print it on the + // right of the instruction. + if (Disassembled && MIA) { + uint64_t Target; + bool PrintTarget = + MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target); + if (!PrintTarget) + if (Optional<uint64_t> MaybeTarget = + MIA->evaluateMemoryOperandAddress( + Inst, SectionAddr + Index, Size)) { + Target = *MaybeTarget; + PrintTarget = true; + FOS << " # " << Twine::utohexstr(Target); + } + if (PrintTarget) { + // In a relocatable object, the target's section must reside in + // the same section as the call instruction or it is accessed + // through a relocation. + // + // In a non-relocatable object, the target may be in any section. + // In that case, locate the section(s) containing the target + // address and find the symbol in one of those, if possible. + // + // N.B. We don't walk the relocations in the relocatable case yet. + std::vector<const SectionSymbolsTy *> TargetSectionSymbols; + if (!Obj->isRelocatableObject()) { + auto It = llvm::partition_point( + SectionAddresses, + [=](const std::pair<uint64_t, SectionRef> &O) { + return O.first <= Target; + }); + uint64_t TargetSecAddr = 0; + while (It != SectionAddresses.begin()) { + --It; + if (TargetSecAddr == 0) + TargetSecAddr = It->first; + if (It->first != TargetSecAddr) + break; + TargetSectionSymbols.push_back(&AllSymbols[It->second]); + } } else { - TargetSectionSymbols = &AbsoluteSymbols; + TargetSectionSymbols.push_back(&Symbols); + } + TargetSectionSymbols.push_back(&AbsoluteSymbols); + + // Find the last symbol in the first candidate section whose + // offset is less than or equal to the target. If there are no + // such symbols, try in the next section and so on, before finally + // using the nearest preceding absolute symbol (if any), if there + // are no other valid symbols. + const SymbolInfoTy *TargetSym = nullptr; + for (const SectionSymbolsTy *TargetSymbols : + TargetSectionSymbols) { + auto It = llvm::partition_point( + *TargetSymbols, + [=](const SymbolInfoTy &O) { return O.Addr <= Target; }); + if (It != TargetSymbols->begin()) { + TargetSym = &*(It - 1); + break; + } } - } - // 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 = 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 = partition_point( - AbsoluteSymbols, - [=](const std::tuple<uint64_t, StringRef, uint8_t> &O) { - return std::get<0>(O) <= Target; - }); - } - if (TargetSym != TargetSectionSymbols->begin()) { - --TargetSym; - uint64_t TargetAddress = std::get<0>(*TargetSym); - StringRef TargetName = std::get<1>(*TargetSym); - outs() << " <" << TargetName; - uint64_t Disp = Target - TargetAddress; - if (Disp) - outs() << "+0x" << Twine::utohexstr(Disp); - outs() << '>'; + if (TargetSym != nullptr) { + uint64_t TargetAddress = TargetSym->Addr; + std::string TargetName = TargetSym->Name.str(); + if (Demangle) + TargetName = demangle(TargetName); + + FOS << " <" << TargetName; + uint64_t Disp = Target - TargetAddress; + if (Disp) + FOS << "+0x" << Twine::utohexstr(Disp); + FOS << '>'; + } } } } - outs() << "\n"; + + LVP.printAfterInst(FOS); + FOS << "\n"; // Hexagon does this in pretty printer if (Obj->getArch() != Triple::hexagon) { - // Print relocation for instruction. + // Print relocation for instruction and data. while (RelCur != RelEnd) { uint64_t Offset = RelCur->getOffset(); // If this relocation is hidden, skip it. @@ -1500,7 +2033,11 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, continue; } - // Stop when RelCur's offset is past the current instruction. + // Stop when RelCur's offset is past the disassembled + // instruction/data. Note that it's possible the disassembled data + // is not the complete data: we might see the relocation printed in + // the middle of the data, but this matches the binutils objdump + // output. if (Offset >= Index + Size) break; @@ -1513,8 +2050,9 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, Offset += AdjustVMA; } - printRelocation(Obj->getFileName(), *RelCur, SectionAddr + Offset, - Is64Bits); + printRelocation(FOS, Obj->getFileName(), *RelCur, + SectionAddr + Offset, Is64Bits); + LVP.printAfterOtherLine(FOS, true); ++RelCur; } } @@ -1523,11 +2061,10 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, } } } - StringSet<> MissingDisasmFuncsSet = - set_difference(DisasmFuncsSet, FoundDisasmFuncsSet); - for (StringRef MissingDisasmFunc : MissingDisasmFuncsSet.keys()) - reportWarning("failed to disassemble missing function " + MissingDisasmFunc, - FileName); + StringSet<> MissingDisasmSymbolSet = + set_difference(DisasmSymbolSet, FoundDisasmSymbolSet); + for (StringRef Sym : MissingDisasmSymbolSet.keys()) + reportWarning("failed to disassemble missing symbol " + Sym, FileName); } static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { @@ -1597,6 +2134,7 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { reportError(Obj->getFileName(), "no instruction printer for target " + TripleName); IP->setPrintImmHex(PrintImmHex); + IP->setPrintBranchImmAsAddress(true); PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName)); SourcePrinter SP(Obj, TheTarget->getName()); @@ -1611,7 +2149,7 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { SP, InlineRelocs); } -void printRelocations(const ObjectFile *Obj) { +void objdump::printRelocations(const ObjectFile *Obj) { StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; // Regular objdump doesn't print relocations in non-relocatable object @@ -1639,6 +2177,11 @@ void printRelocations(const ObjectFile *Obj) { for (std::pair<SectionRef, std::vector<SectionRef>> &P : SecToRelSec) { StringRef SecName = unwrapOrError(P.first.getName(), Obj->getFileName()); outs() << "RELOCATION RECORDS FOR [" << SecName << "]:\n"; + uint32_t OffsetPadding = (Obj->getBytesInAddress() > 4 ? 16 : 8); + uint32_t TypePadding = 24; + outs() << left_justify("OFFSET", OffsetPadding) << " " + << left_justify("TYPE", TypePadding) << " " + << "VALUE\n"; for (SectionRef Section : P.second) { for (const RelocationRef &Reloc : Section.relocations()) { @@ -1651,15 +2194,16 @@ void printRelocations(const ObjectFile *Obj) { if (Error E = getRelocationValueString(Reloc, ValueStr)) reportError(std::move(E), Obj->getFileName()); - outs() << format(Fmt.data(), Address) << " " << RelocName << " " - << ValueStr << "\n"; + outs() << format(Fmt.data(), Address) << " " + << left_justify(RelocName, TypePadding) << " " << ValueStr + << "\n"; } } outs() << "\n"; } } -void printDynamicRelocations(const ObjectFile *Obj) { +void objdump::printDynamicRelocations(const ObjectFile *Obj) { // For the moment, this option is for ELF only if (!Obj->isELF()) return; @@ -1711,7 +2255,7 @@ static size_t getMaxSectionNameWidth(const ObjectFile *Obj) { return MaxWidth; } -void printSectionHeaders(const ObjectFile *Obj) { +void objdump::printSectionHeaders(const ObjectFile *Obj) { size_t NameWidth = getMaxSectionNameWidth(Obj); size_t AddressWidth = 2 * Obj->getBytesInAddress(); bool HasLMAColumn = shouldDisplayLMA(Obj); @@ -1756,7 +2300,7 @@ void printSectionHeaders(const ObjectFile *Obj) { outs() << "\n"; } -void printSectionContents(const ObjectFile *Obj) { +void objdump::printSectionContents(const ObjectFile *Obj) { for (const SectionRef &Section : ToolSectionFilter(*Obj)) { StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName()); uint64_t BaseAddr = Section.getAddress(); @@ -1800,137 +2344,169 @@ void printSectionContents(const ObjectFile *Obj) { } } -void printSymbolTable(const ObjectFile *O, StringRef ArchiveName, - StringRef ArchitectureName) { - outs() << "SYMBOL TABLE:\n"; - - if (const COFFObjectFile *Coff = dyn_cast<const COFFObjectFile>(O)) { - printCOFFSymbolTable(Coff); +void objdump::printSymbolTable(const ObjectFile *O, StringRef ArchiveName, + StringRef ArchitectureName, bool DumpDynamic) { + if (O->isCOFF() && !DumpDynamic) { + outs() << "SYMBOL TABLE:\n"; + printCOFFSymbolTable(cast<const COFFObjectFile>(O)); return; } const StringRef FileName = O->getFileName(); + + if (!DumpDynamic) { + outs() << "SYMBOL TABLE:\n"; + for (auto I = O->symbol_begin(); I != O->symbol_end(); ++I) + printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic); + return; + } + + outs() << "DYNAMIC SYMBOL TABLE:\n"; + if (!O->isELF()) { + reportWarning( + "this operation is not currently supported for this file format", + FileName); + return; + } + + const ELFObjectFileBase *ELF = cast<const ELFObjectFileBase>(O); + for (auto I = ELF->getDynamicSymbolIterators().begin(); + I != ELF->getDynamicSymbolIterators().end(); ++I) + printSymbol(O, *I, FileName, ArchiveName, ArchitectureName, DumpDynamic); +} + +void objdump::printSymbol(const ObjectFile *O, const SymbolRef &Symbol, + StringRef FileName, StringRef ArchiveName, + StringRef ArchitectureName, bool DumpDynamic) { const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(O); - for (auto I = O->symbol_begin(), E = O->symbol_end(); I != E; ++I) { - const SymbolRef &Symbol = *I; - uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName, - ArchitectureName); - if ((Address < StartAddress) || (Address > StopAddress)) - continue; - SymbolRef::Type Type = unwrapOrError(Symbol.getType(), FileName, - ArchiveName, ArchitectureName); - uint32_t Flags = Symbol.getFlags(); + uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName, + ArchitectureName); + if ((Address < StartAddress) || (Address > StopAddress)) + return; + SymbolRef::Type Type = + unwrapOrError(Symbol.getType(), FileName, ArchiveName, ArchitectureName); + uint32_t Flags = + unwrapOrError(Symbol.getFlags(), FileName, ArchiveName, ArchitectureName); + + // Don't ask a Mach-O STAB symbol for its section unless you know that + // STAB symbol's section field refers to a valid section index. Otherwise + // the symbol may error trying to load a section that does not exist. + bool IsSTAB = false; + if (MachO) { + DataRefImpl SymDRI = Symbol.getRawDataRefImpl(); + uint8_t NType = + (MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type + : MachO->getSymbolTableEntry(SymDRI).n_type); + if (NType & MachO::N_STAB) + IsSTAB = true; + } + section_iterator Section = IsSTAB + ? O->section_end() + : unwrapOrError(Symbol.getSection(), FileName, + ArchiveName, ArchitectureName); + + StringRef Name; + if (Type == SymbolRef::ST_Debug && Section != O->section_end()) { + if (Expected<StringRef> NameOrErr = Section->getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); - // Don't ask a Mach-O STAB symbol for its section unless you know that - // STAB symbol's section field refers to a valid section index. Otherwise - // the symbol may error trying to load a section that does not exist. - bool isSTAB = false; - if (MachO) { - DataRefImpl SymDRI = Symbol.getRawDataRefImpl(); - uint8_t NType = (MachO->is64Bit() ? - MachO->getSymbol64TableEntry(SymDRI).n_type: - MachO->getSymbolTableEntry(SymDRI).n_type); - if (NType & MachO::N_STAB) - isSTAB = true; - } - section_iterator Section = isSTAB ? O->section_end() : - unwrapOrError(Symbol.getSection(), FileName, - ArchiveName, ArchitectureName); + } else { + Name = unwrapOrError(Symbol.getName(), FileName, ArchiveName, + ArchitectureName); + } - StringRef Name; - if (Type == SymbolRef::ST_Debug && Section != O->section_end()) { - if (Expected<StringRef> NameOrErr = Section->getName()) - Name = *NameOrErr; - else - consumeError(NameOrErr.takeError()); + bool Global = Flags & SymbolRef::SF_Global; + bool Weak = Flags & SymbolRef::SF_Weak; + bool Absolute = Flags & SymbolRef::SF_Absolute; + bool Common = Flags & SymbolRef::SF_Common; + bool Hidden = Flags & SymbolRef::SF_Hidden; - } else { - Name = unwrapOrError(Symbol.getName(), FileName, ArchiveName, - ArchitectureName); - } + char GlobLoc = ' '; + if ((Section != O->section_end() || Absolute) && !Weak) + GlobLoc = Global ? 'g' : 'l'; + char IFunc = ' '; + if (O->isELF()) { + if (ELFSymbolRef(Symbol).getELFType() == ELF::STT_GNU_IFUNC) + IFunc = 'i'; + if (ELFSymbolRef(Symbol).getBinding() == ELF::STB_GNU_UNIQUE) + GlobLoc = 'u'; + } - bool Global = Flags & SymbolRef::SF_Global; - bool Weak = Flags & SymbolRef::SF_Weak; - bool Absolute = Flags & SymbolRef::SF_Absolute; - bool Common = Flags & SymbolRef::SF_Common; - bool Hidden = Flags & SymbolRef::SF_Hidden; - - char GlobLoc = ' '; - if (Type != SymbolRef::ST_Unknown) - GlobLoc = Global ? 'g' : 'l'; - char Debug = (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File) - ? 'd' : ' '; - char FileFunc = ' '; - if (Type == SymbolRef::ST_File) - FileFunc = 'f'; - else if (Type == SymbolRef::ST_Function) - FileFunc = 'F'; - else if (Type == SymbolRef::ST_Data) - FileFunc = 'O'; - - const char *Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : - "%08" PRIx64; - - outs() << format(Fmt, Address) << " " - << GlobLoc // Local -> 'l', Global -> 'g', Neither -> ' ' - << (Weak ? 'w' : ' ') // Weak? - << ' ' // Constructor. Not supported yet. - << ' ' // Warning. Not supported yet. - << ' ' // Indirect reference to another symbol. - << Debug // Debugging (d) or dynamic (D) symbol. - << FileFunc // Name of function (F), file (f) or object (O). - << ' '; - if (Absolute) { - outs() << "*ABS*"; - } else if (Common) { - outs() << "*COM*"; - } else if (Section == O->section_end()) { - outs() << "*UND*"; - } else { - if (const MachOObjectFile *MachO = - dyn_cast<const MachOObjectFile>(O)) { - DataRefImpl DR = Section->getRawDataRefImpl(); - StringRef SegmentName = MachO->getSectionFinalSegmentName(DR); - outs() << SegmentName << ","; - } - StringRef SectionName = - unwrapOrError(Section->getName(), O->getFileName()); - outs() << SectionName; + char Debug = ' '; + if (DumpDynamic) + Debug = 'D'; + else if (Type == SymbolRef::ST_Debug || Type == SymbolRef::ST_File) + Debug = 'd'; + + char FileFunc = ' '; + if (Type == SymbolRef::ST_File) + FileFunc = 'f'; + else if (Type == SymbolRef::ST_Function) + FileFunc = 'F'; + else if (Type == SymbolRef::ST_Data) + FileFunc = 'O'; + + const char *Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; + + outs() << format(Fmt, Address) << " " + << GlobLoc // Local -> 'l', Global -> 'g', Neither -> ' ' + << (Weak ? 'w' : ' ') // Weak? + << ' ' // Constructor. Not supported yet. + << ' ' // Warning. Not supported yet. + << IFunc // Indirect reference to another symbol. + << Debug // Debugging (d) or dynamic (D) symbol. + << FileFunc // Name of function (F), file (f) or object (O). + << ' '; + if (Absolute) { + outs() << "*ABS*"; + } else if (Common) { + outs() << "*COM*"; + } else if (Section == O->section_end()) { + outs() << "*UND*"; + } else { + if (MachO) { + DataRefImpl DR = Section->getRawDataRefImpl(); + StringRef SegmentName = MachO->getSectionFinalSegmentName(DR); + outs() << SegmentName << ","; } + StringRef SectionName = unwrapOrError(Section->getName(), FileName); + outs() << SectionName; + } - if (Common || isa<ELFObjectFileBase>(O)) { - uint64_t Val = - Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize(); - outs() << format("\t%08" PRIx64, Val); - } + if (Common || O->isELF()) { + uint64_t Val = + Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize(); + outs() << '\t' << format(Fmt, Val); + } - if (isa<ELFObjectFileBase>(O)) { - uint8_t Other = ELFSymbolRef(Symbol).getOther(); - switch (Other) { - case ELF::STV_DEFAULT: - break; - case ELF::STV_INTERNAL: - outs() << " .internal"; - break; - case ELF::STV_HIDDEN: - outs() << " .hidden"; - break; - case ELF::STV_PROTECTED: - outs() << " .protected"; - break; - default: - outs() << format(" 0x%02x", Other); - break; - } - } else if (Hidden) { + if (O->isELF()) { + uint8_t Other = ELFSymbolRef(Symbol).getOther(); + switch (Other) { + case ELF::STV_DEFAULT: + break; + case ELF::STV_INTERNAL: + outs() << " .internal"; + break; + case ELF::STV_HIDDEN: outs() << " .hidden"; + break; + case ELF::STV_PROTECTED: + outs() << " .protected"; + break; + default: + outs() << format(" 0x%02x", Other); + break; } - - if (Demangle) - outs() << ' ' << demangle(Name) << '\n'; - else - outs() << ' ' << Name << '\n'; + } else if (Hidden) { + outs() << " .hidden"; } + + if (Demangle) + outs() << ' ' << demangle(std::string(Name)) << '\n'; + else + outs() << ' ' << Name << '\n'; } static void printUnwindInfo(const ObjectFile *O) { @@ -1949,7 +2525,7 @@ static void printUnwindInfo(const ObjectFile *O) { /// Dump the raw contents of the __clangast section so the output can be piped /// into llvm-bcanalyzer. -void printRawClangAST(const ObjectFile *Obj) { +static 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 " @@ -1960,7 +2536,7 @@ void printRawClangAST(const ObjectFile *Obj) { } StringRef ClangASTSectionName("__clangast"); - if (isa<COFFObjectFile>(Obj)) { + if (Obj->isCOFF()) { ClangASTSectionName = "clangast"; } @@ -1988,9 +2564,9 @@ void printRawClangAST(const ObjectFile *Obj) { static void printFaultMaps(const ObjectFile *Obj) { StringRef FaultMapSectionName; - if (isa<ELFObjectFileBase>(Obj)) { + if (Obj->isELF()) { FaultMapSectionName = ".llvm_faultmaps"; - } else if (isa<MachOObjectFile>(Obj)) { + } else if (Obj->isMachO()) { FaultMapSectionName = "__llvm_faultmaps"; } else { WithColor::error(errs(), ToolName) @@ -2156,7 +2732,7 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr, outs() << A->getFileName() << "(" << O->getFileName() << ")"; else outs() << O->getFileName(); - outs() << ":\tfile format " << O->getFileFormatName() << "\n\n"; + outs() << ":\tfile format " << O->getFileFormatName().lower() << "\n\n"; } if (StartAddress.getNumOccurrences() || StopAddress.getNumOccurrences()) @@ -2174,6 +2750,9 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr, printSectionHeaders(O); if (SymbolTable) printSymbolTable(O, ArchiveName); + if (DynamicSymbolTable) + printSymbolTable(O, ArchiveName, /*ArchitectureName=*/"", + /*DumpDynamic=*/true); if (DwarfDumpType != DIDT_Null) { std::unique_ptr<DIContext> DICtx = DWARFContext::create(*O); // Dump the complete DWARF structure. @@ -2275,7 +2854,6 @@ static void dumpInput(StringRef file) { else reportError(errorCodeToError(object_error::invalid_file_type), file); } -} // namespace llvm int main(int argc, char **argv) { using namespace llvm; @@ -2291,7 +2869,9 @@ int main(int argc, char **argv) { // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); - cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n"); + cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n", nullptr, + /*EnvVar=*/nullptr, + /*LongOptionsUseDoubleDash=*/true); if (StartAddress >= StopAddress) reportCmdLineError("start address should be less than stop address"); @@ -2307,13 +2887,13 @@ int main(int argc, char **argv) { SectionHeaders = SymbolTable = true; if (DisassembleAll || PrintSource || PrintLines || - (!DisassembleFunctions.empty())) + !DisassembleSymbols.empty()) Disassemble = true; if (!ArchiveHeaders && !Disassemble && DwarfDumpType == DIDT_Null && !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST && !Relocations && !SectionHeaders && !SectionContents && !SymbolTable && - !UnwindInfo && !FaultMapSection && + !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && !(MachOOpt && (Bind || DataInCode || DylibId || DylibsUsed || ExportsTrie || FirstPrivateHeader || IndirectSymbols || InfoPlist || LazyBind || @@ -2323,8 +2903,7 @@ int main(int argc, char **argv) { return 2; } - DisasmFuncsSet.insert(DisassembleFunctions.begin(), - DisassembleFunctions.end()); + DisasmSymbolSet.insert(DisassembleSymbols.begin(), DisassembleSymbols.end()); llvm::for_each(InputFilenames, dumpInput); diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h index 43ce02ae0bc27..390fc62d09f81 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -8,26 +8,48 @@ #ifndef LLVM_TOOLS_LLVM_OBJDUMP_LLVM_OBJDUMP_H #define LLVM_TOOLS_LLVM_OBJDUMP_LLVM_OBJDUMP_H +#include "llvm/ADT/StringSet.h" #include "llvm/DebugInfo/DIContext.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/Object/Archive.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DataTypes.h" -#include "llvm/Object/Archive.h" namespace llvm { class StringRef; namespace object { -class COFFObjectFile; -class COFFImportFile; class ELFObjectFileBase; class ELFSectionRef; class MachOObjectFile; class MachOUniversalBinary; class RelocationRef; -} +} // namespace object +namespace objdump { + +extern cl::opt<bool> ArchiveHeaders; extern cl::opt<bool> Demangle; +extern cl::opt<bool> Disassemble; +extern cl::opt<bool> DisassembleAll; +extern cl::opt<DIDumpType> DwarfDumpType; +extern cl::list<std::string> FilterSections; +extern cl::list<std::string> MAttrs; +extern cl::opt<std::string> MCPU; +extern cl::opt<bool> NoShowRawInsn; +extern cl::opt<bool> NoLeadingAddr; +extern cl::opt<bool> PrintImmHex; +extern cl::opt<bool> PrivateHeaders; +extern cl::opt<bool> Relocations; +extern cl::opt<bool> SectionHeaders; +extern cl::opt<bool> SectionContents; +extern cl::opt<bool> SymbolDescription; +extern cl::opt<bool> SymbolTable; +extern cl::opt<std::string> TripleName; +extern cl::opt<bool> UnwindInfo; + +extern StringSet<> FoundSectionSet; typedef std::function<bool(llvm::object::SectionRef const &)> FilterPredicate; @@ -93,52 +115,17 @@ private: SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O, uint64_t *Idx = nullptr); -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); - bool isRelocAddressLess(object::RelocationRef A, object::RelocationRef B); -void parseInputMachO(StringRef Filename); -void parseInputMachO(object::MachOUniversalBinary *UB); -void printCOFFUnwindInfo(const object::COFFObjectFile *O); -void printMachOUnwindInfo(const object::MachOObjectFile *O); -void printMachOExportsTrie(const object::MachOObjectFile *O); -void printMachORebaseTable(object::MachOObjectFile *O); -void printMachOBindTable(object::MachOObjectFile *O); -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); -void printMachOFileHeader(const object::ObjectFile *O); -void printMachOLoadCommands(const object::ObjectFile *O); -void printWasmFileHeader(const object::ObjectFile *O); -void printExportsTrie(const object::ObjectFile *O); -void printRebaseTable(object::ObjectFile *O); -void printBindTable(object::ObjectFile *O); -void printLazyBindTable(object::ObjectFile *O); -void printWeakBindTable(object::ObjectFile *O); -void printRawClangAST(const object::ObjectFile *O); void printRelocations(const object::ObjectFile *O); void printDynamicRelocations(const object::ObjectFile *O); void printSectionHeaders(const object::ObjectFile *O); void printSectionContents(const object::ObjectFile *O); void printSymbolTable(const object::ObjectFile *O, StringRef ArchiveName, - StringRef ArchitectureName = StringRef()); + StringRef ArchitectureName = StringRef(), + bool DumpDynamic = false); +void printSymbol(const object::ObjectFile *O, const object::SymbolRef &Symbol, + StringRef FileName, StringRef ArchiveName, + StringRef ArchitectureName, bool DumpDynamic); LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Twine Message); LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName, StringRef ArchiveName = "", @@ -154,7 +141,10 @@ T unwrapOrError(Expected<T> EO, Ts &&... Args) { std::string getFileNameForError(const object::Archive::Child &C, unsigned Index); +SymbolInfoTy createSymbolInfo(const object::ObjectFile *Obj, + const object::SymbolRef &Symbol); +} // namespace objdump } // end namespace llvm #endif diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp index bf725ad8d6066..aa185e8a2f228 100644 --- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -110,7 +110,7 @@ Error DumpOutputStyle::dump() { P.NewLine(); } - if (opts::dump::DumpTypeStats) { + if (opts::dump::DumpTypeStats || opts::dump::DumpIDStats) { if (auto EC = dumpTypeStats()) return EC; P.NewLine(); @@ -701,7 +701,8 @@ Error DumpOutputStyle::dumpTypeStats() { // Iterate the types, categorize by kind, accumulate size stats. StatCollection TypeStats; - LazyRandomTypeCollection &Types = File.types(); + LazyRandomTypeCollection &Types = + opts::dump::DumpTypeStats ? File.types() : File.ids(); for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) { CVType Type = Types.getType(*TI); TypeStats.update(uint32_t(Type.kind()), Type.length()); @@ -710,18 +711,16 @@ Error DumpOutputStyle::dumpTypeStats() { P.NewLine(); P.formatLine(" Types"); AutoIndent Indent(P); - P.formatLine("{0,14}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total", + P.formatLine("{0,16}: {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)", + P.formatLine("{0,16}: {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(); } @@ -896,7 +895,7 @@ Error DumpOutputStyle::dumpUdtStats() { return L.Stat.Size > R.Stat.Size; }); for (const auto &Stat : NamespacedStatsSorted) { - std::string Label = formatv("namespace '{0}'", Stat.Key); + std::string Label = std::string(formatv("namespace '{0}'", Stat.Key)); P.formatLine("{0} | {1:N} {2:N}", fmt_align(Label, AlignStyle::Right, FieldWidth), fmt_align(Stat.Stat.Count, AlignStyle::Right, CD), @@ -1039,7 +1038,7 @@ Error DumpOutputStyle::dumpXmi() { } std::vector<std::string> TIs; for (const auto I : Xmi.Imports) - TIs.push_back(formatv("{0,+10:X+}", fmtle(I))); + TIs.push_back(std::string(formatv("{0,+10:X+}", fmtle(I)))); std::string Result = typesetItemList(TIs, P.getIndentLevel() + 35, 12, " "); P.formatLine("{0,+32} | {1}", Module, Result); diff --git a/llvm/tools/llvm-pdbutil/FormatUtil.cpp b/llvm/tools/llvm-pdbutil/FormatUtil.cpp index 1a13f383e53c8..c9ef196094964 100644 --- a/llvm/tools/llvm-pdbutil/FormatUtil.cpp +++ b/llvm/tools/llvm-pdbutil/FormatUtil.cpp @@ -20,7 +20,7 @@ using namespace llvm::pdb; std::string llvm::pdb::truncateStringBack(StringRef S, uint32_t MaxLen) { if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) - return S; + return std::string(S); assert(MaxLen >= 3); uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3); @@ -30,7 +30,7 @@ std::string llvm::pdb::truncateStringBack(StringRef S, uint32_t MaxLen) { std::string llvm::pdb::truncateStringMiddle(StringRef S, uint32_t MaxLen) { if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) - return S; + return std::string(S); assert(MaxLen >= 3); uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3); @@ -41,7 +41,7 @@ std::string llvm::pdb::truncateStringMiddle(StringRef S, uint32_t MaxLen) { std::string llvm::pdb::truncateStringFront(StringRef S, uint32_t MaxLen) { if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) - return S; + return std::string(S); assert(MaxLen >= 3); S = S.take_back(MaxLen - 3); @@ -82,7 +82,7 @@ std::string llvm::pdb::typesetItemList(ArrayRef<std::string> Opts, if (!Opts.empty()) { Result += Sep; Result += "\n"; - Result += formatv("{0}", fmt_repeat(' ', IndentLevel)); + Result += std::string(formatv("{0}", fmt_repeat(' ', IndentLevel))); } } return Result; @@ -92,7 +92,7 @@ std::string llvm::pdb::typesetStringList(uint32_t IndentLevel, ArrayRef<StringRef> Strings) { std::string Result = "["; for (const auto &S : Strings) { - Result += formatv("\n{0}{1}", fmt_repeat(' ', IndentLevel), S); + Result += std::string(formatv("\n{0}{1}", fmt_repeat(' ', IndentLevel), S)); } Result += "]"; return Result; @@ -169,7 +169,7 @@ StringRef llvm::pdb::formatTypeLeafKind(TypeLeafKind K) { } std::string llvm::pdb::formatSegmentOffset(uint16_t Segment, uint32_t Offset) { - return formatv("{0:4}:{1:4}", Segment, Offset); + return std::string(formatv("{0:4}:{1:4}", Segment, Offset)); } #define PUSH_CHARACTERISTIC_FLAG(Enum, TheOpt, Value, Style, Descriptive) \ diff --git a/llvm/tools/llvm-pdbutil/FormatUtil.h b/llvm/tools/llvm-pdbutil/FormatUtil.h index 19ce248f9a6f0..1a006844e011a 100644 --- a/llvm/tools/llvm-pdbutil/FormatUtil.h +++ b/llvm/tools/llvm-pdbutil/FormatUtil.h @@ -42,8 +42,7 @@ std::string truncateQuotedNameBack(StringRef Label, StringRef Name, return Ret; template <typename T> std::string formatUnknownEnum(T Value) { - return formatv("unknown ({0})", - static_cast<typename std::underlying_type<T>::type>(Value)) + return formatv("unknown ({0})", static_cast<std::underlying_type_t<T>>(Value)) .str(); } diff --git a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp index ebfa50625e767..7a06140855f80 100644 --- a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -371,9 +371,9 @@ std::string MinimalSymbolDumper::typeOrIdIndex(codeview::TypeIndex TI, StringRef Name = Container.getTypeName(TI); if (Name.size() > 32) { Name = Name.take_front(32); - return formatv("{0} ({1}...)", TI, Name); + return std::string(formatv("{0} ({1}...)", TI, Name)); } else - return formatv("{0} ({1})", TI, Name); + return std::string(formatv("{0} ({1})", TI, Name)); } std::string MinimalSymbolDumper::idIndex(codeview::TypeIndex TI) const { diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp index 3fdef085f19e0..8e46a97272d58 100644 --- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -201,8 +201,9 @@ static std::string formatPointerAttrs(const PointerRecord &Record) { PointerMode Mode = Record.getMode(); PointerOptions Opts = Record.getOptions(); PointerKind Kind = Record.getPointerKind(); - return formatv("mode = {0}, opts = {1}, kind = {2}", formatPointerMode(Mode), - pointerOptions(Opts), pointerKind(Kind)); + return std::string(formatv("mode = {0}, opts = {1}, kind = {2}", + formatPointerMode(Mode), pointerOptions(Opts), + pointerKind(Kind))); } static std::string formatFunctionOptions(FunctionOptions Options) { diff --git a/llvm/tools/llvm-pdbutil/StreamUtil.cpp b/llvm/tools/llvm-pdbutil/StreamUtil.cpp index 7dfc2beefe78d..d0d0a9fbe9275 100644 --- a/llvm/tools/llvm-pdbutil/StreamUtil.cpp +++ b/llvm/tools/llvm-pdbutil/StreamUtil.cpp @@ -32,7 +32,7 @@ std::string StreamInfo::getLongName() const { StreamInfo StreamInfo::createStream(StreamPurpose Purpose, StringRef Name, uint32_t StreamIndex) { StreamInfo Result; - Result.Name = Name; + Result.Name = std::string(Name); Result.StreamIndex = StreamIndex; Result.Purpose = Purpose; return Result; @@ -41,7 +41,7 @@ StreamInfo StreamInfo::createStream(StreamPurpose Purpose, StringRef Name, StreamInfo StreamInfo::createModuleStream(StringRef Module, uint32_t StreamIndex, uint32_t Modi) { StreamInfo Result; - Result.Name = Module; + Result.Name = std::string(Module); Result.StreamIndex = StreamIndex; Result.ModuleIndex = Modi; Result.Purpose = StreamPurpose::ModuleStream; @@ -90,7 +90,7 @@ void llvm::pdb::discoverStreamPurposes(PDBFile &File, if (Info) { for (auto &NSE : Info->named_streams()) { if (NSE.second != kInvalidStreamIndex) - NamedStreams[NSE.second] = NSE.first(); + NamedStreams[NSE.second] = std::string(NSE.first()); } } diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp index 9307300861d43..00092e71c6b49 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -466,6 +466,10 @@ 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> DumpIDStats( + "id-stats", + cl::desc("Dump a detailed breakdown of IPI types 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"), @@ -1507,7 +1511,7 @@ int main(int Argc, const char **Argv) { if (opts::yaml2pdb::YamlPdbOutputFile.empty()) { SmallString<16> OutputFilename(opts::yaml2pdb::InputFilename.getValue()); sys::path::replace_extension(OutputFilename, ".pdb"); - opts::yaml2pdb::YamlPdbOutputFile = OutputFilename.str(); + opts::yaml2pdb::YamlPdbOutputFile = std::string(OutputFilename.str()); } yamlToPdb(opts::yaml2pdb::InputFilename); } else if (opts::DiaDumpSubcommand) { diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h index 321f41bba7f17..9fe92c2c9d75e 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h @@ -141,6 +141,7 @@ 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> DumpIDStats; extern llvm::cl::opt<bool> DumpUdtStats; extern llvm::cl::opt<bool> DumpStreamBlocks; diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index 41e9abb82b1fc..843f072a61c32 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -23,11 +23,12 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormattedStream.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" -#include "llvm/Support/Threading.h" #include "llvm/Support/ThreadPool.h" +#include "llvm/Support/Threading.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -70,18 +71,18 @@ static void exitWithError(Error E, StringRef Whence = "") { instrprof_error instrError = IPE.get(); StringRef Hint = ""; if (instrError == instrprof_error::unrecognized_format) { - // Hint for common error of forgetting -sample for sample profiles. - Hint = "Perhaps you forgot to use the -sample option?"; + // Hint for common error of forgetting --sample for sample profiles. + Hint = "Perhaps you forgot to use the --sample option?"; } - exitWithError(IPE.message(), Whence, Hint); + exitWithError(IPE.message(), std::string(Whence), std::string(Hint)); }); } - exitWithError(toString(std::move(E)), Whence); + exitWithError(toString(std::move(E)), std::string(Whence)); } static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { - exitWithError(EC.message(), Whence); + exitWithError(EC.message(), std::string(Whence)); } namespace { @@ -94,7 +95,7 @@ static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, if (FailMode == failIfAnyAreInvalid) exitWithErrorCode(EC, Whence); else - warn(EC.message(), Whence); + warn(EC.message(), std::string(Whence)); } static void handleMergeWriterError(Error E, StringRef WhenceFile = "", @@ -290,6 +291,22 @@ static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { }); } +static void writeInstrProfile(StringRef OutputFilename, + ProfileFormat OutputFormat, + InstrProfWriter &Writer) { + std::error_code EC; + raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None); + if (EC) + exitWithErrorCode(EC, OutputFilename); + + if (OutputFormat == PF_Text) { + if (Error E = Writer.writeText(Output)) + exitWithError(std::move(E)); + } else { + Writer.write(Output); + } +} + static void mergeInstrProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, StringRef OutputFilename, @@ -307,8 +324,11 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, // If NumThreads is not specified, auto-detect a good default. if (NumThreads == 0) - NumThreads = - std::min(hardware_concurrency(), unsigned((Inputs.size() + 1) / 2)); + NumThreads = std::min(hardware_concurrency().compute_thread_count(), + unsigned((Inputs.size() + 1) / 2)); + // FIXME: There's a bug here, where setting NumThreads = Inputs.size() fails + // the merge_empty_profile.test because the InstrProfWriter.ProfileKind isn't + // merged, thus the emitted file ends up with a PF_Unknown kind. // Initialize the writer contexts. SmallVector<std::unique_ptr<WriterContext>, 4> Contexts; @@ -320,7 +340,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, for (const auto &Input : Inputs) loadInput(Input, Remapper, Contexts[0].get()); } else { - ThreadPool Pool(NumThreads); + ThreadPool Pool(hardware_concurrency(NumThreads)); // Load the inputs in parallel (N/NumThreads serial steps). unsigned Ctx = 0; @@ -362,18 +382,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) exitWithError("No profiles could be merged."); - std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - InstrProfWriter &Writer = Contexts[0]->Writer; - if (OutputFormat == PF_Text) { - if (Error E = Writer.writeText(Output)) - exitWithError(std::move(E)); - } else { - Writer.write(Output); - } + writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer); } /// Make a copy of the given function samples with all symbol names remapped @@ -401,7 +410,8 @@ remapSamples(const sampleprof::FunctionSamples &Samples, for (const auto &Callsite : CallsiteSamples.second) { sampleprof::FunctionSamples Remapped = remapSamples(Callsite.second, Remapper, Error); - MergeResult(Error, Target[Remapped.getName()].merge(Remapped)); + MergeResult(Error, + Target[std::string(Remapped.getName())].merge(Remapped)); } } return Result; @@ -444,7 +454,8 @@ static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, ProfileFormat OutputFormat, MemoryBuffer *Buffer, sampleprof::ProfileSymbolList &WriterList, - bool CompressAllSections) { + bool CompressAllSections, bool UseMD5, + bool GenPartialProfile) { populateProfileSymbolList(Buffer, WriterList); if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary) warn("Profile Symbol list is not empty but the output format is not " @@ -453,22 +464,30 @@ static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, Writer.setProfileSymbolList(&WriterList); if (CompressAllSections) { - if (OutputFormat != PF_Ext_Binary) { + if (OutputFormat != PF_Ext_Binary) warn("-compress-all-section is ignored. Specify -extbinary to enable it"); - } else { - auto ExtBinaryWriter = - static_cast<sampleprof::SampleProfileWriterExtBinary *>(&Writer); - ExtBinaryWriter->setToCompressAllSections(); - } + else + Writer.setToCompressAllSections(); + } + if (UseMD5) { + if (OutputFormat != PF_Ext_Binary) + warn("-use-md5 is ignored. Specify -extbinary to enable it"); + else + Writer.setUseMD5(); + } + if (GenPartialProfile) { + if (OutputFormat != PF_Ext_Binary) + warn("-gen-partial-profile is ignored. Specify -extbinary to enable it"); + else + Writer.setPartialProfile(); } } -static void mergeSampleProfile(const WeightedFileVector &Inputs, - SymbolRemapper *Remapper, - StringRef OutputFilename, - ProfileFormat OutputFormat, - StringRef ProfileSymbolListFile, - bool CompressAllSections, FailureMode FailMode) { +static void +mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, + StringRef OutputFilename, ProfileFormat OutputFormat, + StringRef ProfileSymbolListFile, bool CompressAllSections, + bool UseMD5, bool GenPartialProfile, FailureMode FailMode) { using namespace sampleprof; StringMap<FunctionSamples> ProfileMap; SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; @@ -525,7 +544,7 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs, // Make sure Buffer lives as long as WriterList. auto Buffer = getInputFileBuf(ProfileSymbolListFile); handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, - CompressAllSections); + CompressAllSections, UseMD5, GenPartialProfile); Writer->write(ProfileMap); } @@ -537,7 +556,7 @@ static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { if (WeightStr.getAsInteger(10, Weight) || Weight < 1) exitWithError("Input weight must be a positive integer."); - return {FileName, Weight}; + return {std::string(FileName), Weight}; } static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { @@ -546,7 +565,7 @@ static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { // If it's STDIN just pass it on. if (Filename == "-") { - WNI.push_back({Filename, Weight}); + WNI.push_back({std::string(Filename), Weight}); return; } @@ -557,7 +576,7 @@ static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { Filename); // If it's a source file, collect it. if (llvm::sys::fs::is_regular_file(Status)) { - WNI.push_back({Filename, Weight}); + WNI.push_back({std::string(Filename), Weight}); return; } @@ -589,7 +608,7 @@ static void parseInputFilenamesFile(MemoryBuffer *Buffer, continue; // If there's no comma, it's an unweighted profile. else if (SanitizedEntry.find(',') == StringRef::npos) - addWeightedInput(WFV, {SanitizedEntry, 1}); + addWeightedInput(WFV, {std::string(SanitizedEntry), 1}); else addWeightedInput(WFV, parseWeightedFile(SanitizedEntry)); } @@ -653,12 +672,19 @@ static int merge_main(int argc, const char *argv[]) { "compress-all-sections", cl::init(false), cl::Hidden, cl::desc("Compress all sections when writing the profile (only " "meaningful for -extbinary)")); + cl::opt<bool> UseMD5( + "use-md5", cl::init(false), cl::Hidden, + cl::desc("Choose to use MD5 to represent string in name table (only " + "meaningful for -extbinary)")); + cl::opt<bool> GenPartialProfile( + "gen-partial-profile", cl::init(false), cl::Hidden, + cl::desc("Generate a partial profile (only meaningful for -extbinary)")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); WeightedFileVector WeightedInputs; for (StringRef Filename : InputFilenames) - addWeightedInput(WeightedInputs, {Filename, 1}); + addWeightedInput(WeightedInputs, {std::string(Filename), 1}); for (StringRef WeightedFilename : WeightedInputFilenames) addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename)); @@ -687,7 +713,7 @@ static int merge_main(int argc, const char *argv[]) { else mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, OutputFormat, ProfileSymbolListFile, CompressAllSections, - FailureMode); + UseMD5, GenPartialProfile, FailureMode); return 0; } @@ -989,15 +1015,9 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, } if (ShowDetailedSummary) { - OS << "Detailed summary:\n"; OS << "Total number of blocks: " << PS->getNumCounts() << "\n"; OS << "Total count: " << PS->getTotalCount() << "\n"; - for (auto Entry : PS->getDetailedSummary()) { - OS << Entry.NumCounts << " blocks with count >= " << Entry.MinCount - << " account for " - << format("%0.6g", (float)Entry.Cutoff / ProfileSummary::Scale * 100) - << " percentage of the total counts.\n"; - } + PS->printDetailedSummary(OS); } return 0; } @@ -1012,11 +1032,144 @@ static void showSectionInfo(sampleprof::SampleProfileReader *Reader, } } +namespace { +struct HotFuncInfo { + StringRef FuncName; + uint64_t TotalCount; + double TotalCountPercent; + uint64_t MaxCount; + uint64_t EntryCount; + + HotFuncInfo() + : FuncName(), TotalCount(0), TotalCountPercent(0.0f), MaxCount(0), + EntryCount(0) {} + + HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES) + : FuncName(FN), TotalCount(TS), TotalCountPercent(TSP), MaxCount(MS), + EntryCount(ES) {} +}; +} // namespace + +// Print out detailed information about hot functions in PrintValues vector. +// Users specify titles and offset of every columns through ColumnTitle and +// ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same +// and at least 4. Besides, users can optionally give a HotFuncMetric string to +// print out or let it be an empty string. +static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle, + const std::vector<int> &ColumnOffset, + const std::vector<HotFuncInfo> &PrintValues, + uint64_t HotFuncCount, uint64_t TotalFuncCount, + uint64_t HotProfCount, uint64_t TotalProfCount, + const std::string &HotFuncMetric, + raw_fd_ostream &OS) { + assert(ColumnOffset.size() == ColumnTitle.size()); + assert(ColumnTitle.size() >= 4); + assert(TotalFuncCount > 0); + double TotalProfPercent = 0; + if (TotalProfCount > 0) + TotalProfPercent = ((double)HotProfCount) / TotalProfCount * 100; + + formatted_raw_ostream FOS(OS); + FOS << HotFuncCount << " out of " << TotalFuncCount + << " functions with profile (" + << format("%.2f%%", (((double)HotFuncCount) / TotalFuncCount * 100)) + << ") are considered hot functions"; + if (!HotFuncMetric.empty()) + FOS << " (" << HotFuncMetric << ")"; + FOS << ".\n"; + FOS << HotProfCount << " out of " << TotalProfCount << " profile counts (" + << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n"; + + for (size_t I = 0; I < ColumnTitle.size(); ++I) { + FOS.PadToColumn(ColumnOffset[I]); + FOS << ColumnTitle[I]; + } + FOS << "\n"; + + for (const HotFuncInfo &R : PrintValues) { + FOS.PadToColumn(ColumnOffset[0]); + FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")"; + FOS.PadToColumn(ColumnOffset[1]); + FOS << R.MaxCount; + FOS.PadToColumn(ColumnOffset[2]); + FOS << R.EntryCount; + FOS.PadToColumn(ColumnOffset[3]); + FOS << R.FuncName << "\n"; + } + return; +} + +static int +showHotFunctionList(const StringMap<sampleprof::FunctionSamples> &Profiles, + ProfileSummary &PS, raw_fd_ostream &OS) { + using namespace sampleprof; + + const uint32_t HotFuncCutoff = 990000; + auto &SummaryVector = PS.getDetailedSummary(); + uint64_t MinCountThreshold = 0; + for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) { + if (SummaryEntry.Cutoff == HotFuncCutoff) { + MinCountThreshold = SummaryEntry.MinCount; + break; + } + } + assert(MinCountThreshold != 0); + + // Traverse all functions in the profile and keep only hot functions. + // The following loop also calculates the sum of total samples of all + // functions. + std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>, + std::greater<uint64_t>> + HotFunc; + uint64_t ProfileTotalSample = 0; + uint64_t HotFuncSample = 0; + uint64_t HotFuncCount = 0; + uint64_t MaxCount = 0; + for (const auto &I : Profiles) { + const FunctionSamples &FuncProf = I.second; + ProfileTotalSample += FuncProf.getTotalSamples(); + MaxCount = FuncProf.getMaxCountInside(); + + // MinCountThreshold is a block/line threshold computed for a given cutoff. + // We intentionally compare the maximum sample count in a function with this + // threshold to get an approximate threshold for hot functions. + if (MaxCount >= MinCountThreshold) { + HotFunc.emplace(FuncProf.getTotalSamples(), + std::make_pair(&(I.second), MaxCount)); + HotFuncSample += FuncProf.getTotalSamples(); + ++HotFuncCount; + } + } + + std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample", + "Entry sample", "Function name"}; + std::vector<int> ColumnOffset{0, 24, 42, 58}; + std::string Metric = + std::string("max sample >= ") + std::to_string(MinCountThreshold); + std::vector<HotFuncInfo> PrintValues; + for (const auto &FuncPair : HotFunc) { + const FunctionSamples &Func = *FuncPair.second.first; + double TotalSamplePercent = + (ProfileTotalSample > 0) + ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample + : 0; + PrintValues.emplace_back(HotFuncInfo( + Func.getFuncName(), Func.getTotalSamples(), TotalSamplePercent, + FuncPair.second.second, Func.getEntrySamples())); + } + dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount, + Profiles.size(), HotFuncSample, ProfileTotalSample, + Metric, OS); + + return 0; +} + static int showSampleProfile(const std::string &Filename, bool ShowCounts, - bool ShowAllFunctions, + bool ShowAllFunctions, bool ShowDetailedSummary, const std::string &ShowFunction, bool ShowProfileSymbolList, - bool ShowSectionInfoOnly, raw_fd_ostream &OS) { + bool ShowSectionInfoOnly, bool ShowHotFuncList, + raw_fd_ostream &OS) { using namespace sampleprof; LLVMContext Context; auto ReaderOrErr = SampleProfileReader::create(Filename, Context); @@ -1044,6 +1197,15 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, ReaderList->dump(OS); } + if (ShowDetailedSummary) { + auto &PS = Reader->getSummary(); + PS.printSummary(OS); + PS.printDetailedSummary(OS); + } + + if (ShowHotFuncList) + showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), OS); + return 0; } @@ -1070,6 +1232,9 @@ static int show_main(int argc, const char *argv[]) { cl::desc( "Cutoff percentages (times 10000) for generating detailed summary"), cl::value_desc("800000,901000,999999")); + cl::opt<bool> ShowHotFuncList( + "hot-func-list", cl::init(false), + cl::desc("Show profile summary of a list of hot functions")); cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false), cl::desc("Details for every function")); cl::opt<bool> ShowCS("showcs", cl::init(false), @@ -1132,8 +1297,9 @@ static int show_main(int argc, const char *argv[]) { OnlyListBelow, ShowFunction, TextFormat, OS); else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, - ShowFunction, ShowProfileSymbolList, - ShowSectionInfoOnly, OS); + ShowDetailedSummary, ShowFunction, + ShowProfileSymbolList, ShowSectionInfoOnly, + ShowHotFuncList, OS); } int main(int argc, const char *argv[]) { diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp index 9b2c6adb9d930..89a904f53ae7d 100644 --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -104,7 +104,10 @@ public: bool GHash) override; void printStackMap() const override; void printAddrsig() override; + void printCGProfile() override; + private: + StringRef getSymbolName(uint32_t Index); void printSymbols() override; void printDynamicSymbols() override; void printSymbol(const SymbolRef &Sym); @@ -409,6 +412,11 @@ static const EnumEntry<COFF::DLLCharacteristics> PEDLLCharacteristics[] = { LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE), }; +static const EnumEntry<COFF::ExtendedDLLCharacteristics> + PEExtendedDLLCharacteristics[] = { + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT), +}; + static const EnumEntry<COFF::SectionCharacteristics> ImageSectionCharacteristics[] = { LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NOLOAD ), @@ -516,23 +524,25 @@ static const EnumEntry<COFF::COMDATType> ImageCOMDATSelect[] = { }; static const EnumEntry<COFF::DebugType> ImageDebugType[] = { - { "Unknown" , COFF::IMAGE_DEBUG_TYPE_UNKNOWN }, - { "COFF" , COFF::IMAGE_DEBUG_TYPE_COFF }, - { "CodeView" , COFF::IMAGE_DEBUG_TYPE_CODEVIEW }, - { "FPO" , COFF::IMAGE_DEBUG_TYPE_FPO }, - { "Misc" , COFF::IMAGE_DEBUG_TYPE_MISC }, - { "Exception" , COFF::IMAGE_DEBUG_TYPE_EXCEPTION }, - { "Fixup" , COFF::IMAGE_DEBUG_TYPE_FIXUP }, - { "OmapToSrc" , COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC }, - { "OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC }, - { "Borland" , COFF::IMAGE_DEBUG_TYPE_BORLAND }, - { "Reserved10" , COFF::IMAGE_DEBUG_TYPE_RESERVED10 }, - { "CLSID" , COFF::IMAGE_DEBUG_TYPE_CLSID }, - { "VCFeature" , COFF::IMAGE_DEBUG_TYPE_VC_FEATURE }, - { "POGO" , COFF::IMAGE_DEBUG_TYPE_POGO }, - { "ILTCG" , COFF::IMAGE_DEBUG_TYPE_ILTCG }, - { "MPX" , COFF::IMAGE_DEBUG_TYPE_MPX }, - { "Repro" , COFF::IMAGE_DEBUG_TYPE_REPRO }, + {"Unknown", COFF::IMAGE_DEBUG_TYPE_UNKNOWN}, + {"COFF", COFF::IMAGE_DEBUG_TYPE_COFF}, + {"CodeView", COFF::IMAGE_DEBUG_TYPE_CODEVIEW}, + {"FPO", COFF::IMAGE_DEBUG_TYPE_FPO}, + {"Misc", COFF::IMAGE_DEBUG_TYPE_MISC}, + {"Exception", COFF::IMAGE_DEBUG_TYPE_EXCEPTION}, + {"Fixup", COFF::IMAGE_DEBUG_TYPE_FIXUP}, + {"OmapToSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC}, + {"OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC}, + {"Borland", COFF::IMAGE_DEBUG_TYPE_BORLAND}, + {"Reserved10", COFF::IMAGE_DEBUG_TYPE_RESERVED10}, + {"CLSID", COFF::IMAGE_DEBUG_TYPE_CLSID}, + {"VCFeature", COFF::IMAGE_DEBUG_TYPE_VC_FEATURE}, + {"POGO", COFF::IMAGE_DEBUG_TYPE_POGO}, + {"ILTCG", COFF::IMAGE_DEBUG_TYPE_ILTCG}, + {"MPX", COFF::IMAGE_DEBUG_TYPE_MPX}, + {"Repro", COFF::IMAGE_DEBUG_TYPE_REPRO}, + {"ExtendedDLLCharacteristics", + COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS}, }; static const EnumEntry<COFF::WeakExternalCharacteristics> @@ -601,8 +611,8 @@ void COFFDumper::cacheRelocations() { void COFFDumper::printDataDirectory(uint32_t Index, const std::string &FieldName) { - const data_directory *Data; - if (Obj->getDataDirectory(Index, Data)) + const data_directory *Data = Obj->getDataDirectory(Index); + if (!Data) return; W.printHex(FieldName + "RVA", Data->RelativeVirtualAddress); W.printHex(FieldName + "Size", Data->Size); @@ -621,6 +631,7 @@ void COFFDumper::printFileHeaders() { W.printHex ("TimeDateStamp", FormattedTime, Obj->getTimeDateStamp()); W.printHex ("PointerToSymbolTable", Obj->getPointerToSymbolTable()); W.printNumber("SymbolCount", Obj->getNumberOfSymbols()); + W.printNumber("StringTableSize", Obj->getStringTableSize()); W.printNumber("OptionalHeaderSize", Obj->getSizeOfOptionalHeader()); W.printFlags ("Characteristics", Obj->getCharacteristics(), makeArrayRef(ImageFileCharacteristics)); @@ -722,11 +733,15 @@ void COFFDumper::printCOFFDebugDirectory() { W.printHex("SizeOfData", D.SizeOfData); W.printHex("AddressOfRawData", D.AddressOfRawData); W.printHex("PointerToRawData", D.PointerToRawData); + // Ideally, if D.AddressOfRawData == 0, we should try to load the payload + // using D.PointerToRawData instead. + if (D.AddressOfRawData == 0) + continue; if (D.Type == COFF::IMAGE_DEBUG_TYPE_CODEVIEW) { const codeview::DebugInfo *DebugInfo; StringRef PDBFileName; - if (std::error_code EC = Obj->getDebugPDBInfo(&D, DebugInfo, PDBFileName)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = Obj->getDebugPDBInfo(&D, DebugInfo, PDBFileName)) + reportError(std::move(E), Obj->getFileName()); DictScope PDBScope(W, "PDBInfo"); W.printHex("PDBSignature", DebugInfo->Signature.CVSignature); @@ -736,12 +751,19 @@ void COFFDumper::printCOFFDebugDirectory() { W.printString("PDBFileName", PDBFileName); } } else if (D.SizeOfData != 0) { - // FIXME: Type values of 12 and 13 are commonly observed but are not in - // the documented type enum. Figure out what they mean. + // FIXME: Data visualization for IMAGE_DEBUG_TYPE_VC_FEATURE and + // IMAGE_DEBUG_TYPE_POGO? ArrayRef<uint8_t> RawData; - if (std::error_code EC = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData, + if (Error E = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData, D.SizeOfData, RawData)) - reportError(errorCodeToError(EC), Obj->getFileName()); + reportError(std::move(E), Obj->getFileName()); + if (D.Type == COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS) { + // FIXME right now the only possible value would fit in 8 bits, + // but that might change in the future + uint16_t Characteristics = RawData[0]; + W.printFlags("ExtendedCharacteristics", Characteristics, + makeArrayRef(PEExtendedDLLCharacteristics)); + } W.printBinaryBlock("RawData", RawData); } } @@ -750,11 +772,11 @@ void COFFDumper::printCOFFDebugDirectory() { void COFFDumper::printRVATable(uint64_t TableVA, uint64_t Count, uint64_t EntrySize, PrintExtraCB PrintExtra) { uintptr_t TableStart, TableEnd; - if (std::error_code EC = Obj->getVaPtr(TableVA, TableStart)) - reportError(errorCodeToError(EC), Obj->getFileName()); - if (std::error_code EC = + if (Error E = Obj->getVaPtr(TableVA, TableStart)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = Obj->getVaPtr(TableVA + Count * EntrySize - 1, TableEnd)) - reportError(errorCodeToError(EC), Obj->getFileName()); + reportError(std::move(E), Obj->getFileName()); TableEnd++; for (uintptr_t I = TableStart; I < TableEnd; I += EntrySize) { uint32_t RVA = *reinterpret_cast<const ulittle32_t *>(I); @@ -1135,7 +1157,7 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, return; } - std::string PC = formatv("+{0:X}", uint32_t(Line.Offset)); + std::string PC = std::string(formatv("+{0:X}", uint32_t(Line.Offset))); ListScope PCScope(W, PC); codeview::LineInfo LI(Line.Flags); @@ -1449,21 +1471,25 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { DictScope D(W, "Symbol"); COFFSymbolRef Symbol = Obj->getCOFFSymbol(Sym); - const coff_section *Section; - if (std::error_code EC = Obj->getSection(Symbol.getSectionNumber(), Section)) { - W.startLine() << "Invalid section number: " << EC.message() << "\n"; + Expected<const coff_section *> SecOrErr = + Obj->getSection(Symbol.getSectionNumber()); + if (!SecOrErr) { + W.startLine() << "Invalid section number: " << Symbol.getSectionNumber() + << "\n"; W.flush(); + consumeError(SecOrErr.takeError()); return; } + const coff_section *Section = *SecOrErr; StringRef SymbolName; - if (Obj->getSymbolName(Symbol, SymbolName)) - SymbolName = ""; + if (Expected<StringRef> SymNameOrErr = Obj->getSymbolName(Symbol)) + SymbolName = *SymNameOrErr; StringRef SectionName; - if (Expected<StringRef> NameOrErr = + if (Expected<StringRef> SecNameOrErr = getSectionName(Obj, Symbol.getSectionNumber(), Section)) - SectionName = *NameOrErr; + SectionName = *SecNameOrErr; W.printString("Name", SymbolName); W.printNumber("Value", Symbol.getValue()); @@ -1492,16 +1518,8 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux)) reportError(errorCodeToError(EC), Obj->getFileName()); - Expected<COFFSymbolRef> Linked = Obj->getSymbol(Aux->TagIndex); - if (!Linked) - reportError(Linked.takeError(), Obj->getFileName()); - - StringRef LinkedName; - if (std::error_code EC = Obj->getSymbolName(*Linked, LinkedName)) - reportError(errorCodeToError(EC), Obj->getFileName()); - DictScope AS(W, "AuxWeakExternal"); - W.printNumber("Linked", LinkedName, Aux->TagIndex); + W.printNumber("Linked", getSymbolName(Aux->TagIndex), Aux->TagIndex); W.printEnum ("Search", Aux->Characteristics, makeArrayRef(WeakExternalCharacteristics)); @@ -1532,35 +1550,25 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { if (Section && Section->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT && Aux->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { - const coff_section *Assoc; - StringRef AssocName = ""; - if (std::error_code EC = Obj->getSection(AuxNumber, Assoc)) - reportError(errorCodeToError(EC), Obj->getFileName()); - Expected<StringRef> Res = getSectionName(Obj, AuxNumber, Assoc); - if (!Res) - reportError(Res.takeError(), Obj->getFileName()); - AssocName = *Res; - - W.printNumber("AssocSection", AssocName, AuxNumber); + Expected<const coff_section *> Assoc = Obj->getSection(AuxNumber); + if (!Assoc) + reportError(Assoc.takeError(), Obj->getFileName()); + Expected<StringRef> AssocName = getSectionName(Obj, AuxNumber, *Assoc); + if (!AssocName) + reportError(AssocName.takeError(), Obj->getFileName()); + + W.printNumber("AssocSection", *AssocName, AuxNumber); } } else if (Symbol.isCLRToken()) { const coff_aux_clr_token *Aux; if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux)) reportError(errorCodeToError(EC), Obj->getFileName()); - Expected<COFFSymbolRef> ReferredSym = - Obj->getSymbol(Aux->SymbolTableIndex); - if (!ReferredSym) - reportError(ReferredSym.takeError(), Obj->getFileName()); - - StringRef ReferredName; - if (std::error_code EC = Obj->getSymbolName(*ReferredSym, ReferredName)) - reportError(errorCodeToError(EC), Obj->getFileName()); - DictScope AS(W, "AuxCLRToken"); W.printNumber("AuxType", Aux->AuxType); W.printNumber("Reserved", Aux->Reserved); - W.printNumber("SymbolTableIndex", ReferredName, Aux->SymbolTableIndex); + W.printNumber("SymbolTableIndex", getSymbolName(Aux->SymbolTableIndex), + Aux->SymbolTableIndex); } else { W.startLine() << "<unhandled auxiliary record>\n"; @@ -1621,11 +1629,11 @@ void COFFDumper::printImportedSymbols( iterator_range<imported_symbol_iterator> Range) { for (const ImportedSymbolRef &I : Range) { StringRef Sym; - if (std::error_code EC = I.getSymbolName(Sym)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = I.getSymbolName(Sym)) + reportError(std::move(E), Obj->getFileName()); uint16_t Ordinal; - if (std::error_code EC = I.getOrdinal(Ordinal)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = I.getOrdinal(Ordinal)) + reportError(std::move(E), Obj->getFileName()); W.printNumber("Symbol", Sym, Ordinal); } } @@ -1637,17 +1645,17 @@ void COFFDumper::printDelayImportedSymbols( for (const ImportedSymbolRef &S : Range) { DictScope Import(W, "Import"); StringRef Sym; - if (std::error_code EC = S.getSymbolName(Sym)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = S.getSymbolName(Sym)) + reportError(std::move(E), Obj->getFileName()); uint16_t Ordinal; - if (std::error_code EC = S.getOrdinal(Ordinal)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = S.getOrdinal(Ordinal)) + reportError(std::move(E), Obj->getFileName()); W.printNumber("Symbol", Sym, Ordinal); uint64_t Addr; - if (std::error_code EC = I.getImportAddress(Index++, Addr)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = I.getImportAddress(Index++, Addr)) + reportError(std::move(E), Obj->getFileName()); W.printHex("Address", Addr); } } @@ -1657,16 +1665,16 @@ void COFFDumper::printCOFFImports() { for (const ImportDirectoryEntryRef &I : Obj->import_directories()) { DictScope Import(W, "Import"); StringRef Name; - if (std::error_code EC = I.getName(Name)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = I.getName(Name)) + reportError(std::move(E), Obj->getFileName()); W.printString("Name", Name); uint32_t ILTAddr; - if (std::error_code EC = I.getImportLookupTableRVA(ILTAddr)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = I.getImportLookupTableRVA(ILTAddr)) + reportError(std::move(E), Obj->getFileName()); W.printHex("ImportLookupTableRVA", ILTAddr); uint32_t IATAddr; - if (std::error_code EC = I.getImportAddressTableRVA(IATAddr)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = I.getImportAddressTableRVA(IATAddr)) + reportError(std::move(E), Obj->getFileName()); W.printHex("ImportAddressTableRVA", IATAddr); // The import lookup table can be missing with certain older linkers, so // fall back to the import address table in that case. @@ -1680,12 +1688,12 @@ void COFFDumper::printCOFFImports() { for (const DelayImportDirectoryEntryRef &I : Obj->delay_import_directories()) { DictScope Import(W, "DelayImport"); StringRef Name; - if (std::error_code EC = I.getName(Name)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = I.getName(Name)) + reportError(std::move(E), Obj->getFileName()); W.printString("Name", Name); const delay_import_directory_table_entry *Table; - if (std::error_code EC = I.getDelayImportTable(Table)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = I.getDelayImportTable(Table)) + reportError(std::move(E), Obj->getFileName()); W.printHex("Attributes", Table->Attributes); W.printHex("ModuleHandle", Table->ModuleHandle); W.printHex("ImportAddressTable", Table->DelayImportAddressTable); @@ -1697,18 +1705,18 @@ void COFFDumper::printCOFFImports() { } void COFFDumper::printCOFFExports() { - for (const ExportDirectoryEntryRef &E : Obj->export_directories()) { + for (const ExportDirectoryEntryRef &Exp : Obj->export_directories()) { DictScope Export(W, "Export"); StringRef Name; uint32_t Ordinal, RVA; - if (std::error_code EC = E.getSymbolName(Name)) - reportError(errorCodeToError(EC), Obj->getFileName()); - if (std::error_code EC = E.getOrdinal(Ordinal)) - reportError(errorCodeToError(EC), Obj->getFileName()); - if (std::error_code EC = E.getExportRVA(RVA)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = Exp.getSymbolName(Name)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = Exp.getOrdinal(Ordinal)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = Exp.getExportRVA(RVA)) + reportError(std::move(E), Obj->getFileName()); W.printNumber("Ordinal", Ordinal); W.printString("Name", Name); @@ -1746,10 +1754,10 @@ void COFFDumper::printCOFFBaseReloc() { for (const BaseRelocRef &I : Obj->base_relocs()) { uint8_t Type; uint32_t RVA; - if (std::error_code EC = I.getRVA(RVA)) - reportError(errorCodeToError(EC), Obj->getFileName()); - if (std::error_code EC = I.getType(Type)) - reportError(errorCodeToError(EC), Obj->getFileName()); + if (Error E = I.getRVA(RVA)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = I.getType(Type)) + reportError(std::move(E), Obj->getFileName()); DictScope Import(W, "Entry"); W.printString("Type", getBaseRelocTypeName(Type)); W.printHex("Address", RVA); @@ -1882,7 +1890,7 @@ void COFFDumper::printResourceDirectoryTable( } void COFFDumper::printStackMap() const { - object::SectionRef StackMapSection; + SectionRef StackMapSection; for (auto Sec : Obj->sections()) { StringRef Name; if (Expected<StringRef> NameOrErr = Sec.getName()) @@ -1896,7 +1904,7 @@ void COFFDumper::printStackMap() const { } } - if (StackMapSection == object::SectionRef()) + if (StackMapSection == SectionRef()) return; StringRef StackMapContents = @@ -1913,7 +1921,7 @@ void COFFDumper::printStackMap() const { } void COFFDumper::printAddrsig() { - object::SectionRef AddrsigSection; + SectionRef AddrsigSection; for (auto Sec : Obj->sections()) { StringRef Name; if (Expected<StringRef> NameOrErr = Sec.getName()) @@ -1927,7 +1935,7 @@ void COFFDumper::printAddrsig() { } } - if (AddrsigSection == object::SectionRef()) + if (AddrsigSection == SectionRef()) return; StringRef AddrsigContents = @@ -1945,19 +1953,58 @@ void COFFDumper::printAddrsig() { if (Err) reportError(createError(Err), Obj->getFileName()); - Expected<COFFSymbolRef> Sym = Obj->getSymbol(SymIndex); - if (!Sym) - reportError(Sym.takeError(), Obj->getFileName()); + W.printNumber("Sym", getSymbolName(SymIndex), SymIndex); + Cur += Size; + } +} - StringRef SymName; - if (std::error_code EC = Obj->getSymbolName(*Sym, SymName)) - reportError(errorCodeToError(EC), Obj->getFileName()); +void COFFDumper::printCGProfile() { + SectionRef CGProfileSection; + for (SectionRef Sec : Obj->sections()) { + StringRef Name = unwrapOrError(Obj->getFileName(), Sec.getName()); + if (Name == ".llvm.call-graph-profile") { + CGProfileSection = Sec; + break; + } + } - W.printNumber("Sym", SymName, SymIndex); - Cur += Size; + if (CGProfileSection == SectionRef()) + return; + + StringRef CGProfileContents = + unwrapOrError(Obj->getFileName(), CGProfileSection.getContents()); + BinaryStreamReader Reader(CGProfileContents, llvm::support::little); + + ListScope L(W, "CGProfile"); + while (!Reader.empty()) { + uint32_t FromIndex, ToIndex; + uint64_t Count; + if (Error Err = Reader.readInteger(FromIndex)) + reportError(std::move(Err), Obj->getFileName()); + if (Error Err = Reader.readInteger(ToIndex)) + reportError(std::move(Err), Obj->getFileName()); + if (Error Err = Reader.readInteger(Count)) + reportError(std::move(Err), Obj->getFileName()); + + DictScope D(W, "CGProfileEntry"); + W.printNumber("From", getSymbolName(FromIndex), FromIndex); + W.printNumber("To", getSymbolName(ToIndex), ToIndex); + W.printNumber("Weight", Count); } } +StringRef COFFDumper::getSymbolName(uint32_t Index) { + Expected<COFFSymbolRef> Sym = Obj->getSymbol(Index); + if (!Sym) + reportError(Sym.takeError(), Obj->getFileName()); + + Expected<StringRef> SymName = Obj->getSymbolName(*Sym); + if (!SymName) + reportError(SymName.takeError(), Obj->getFileName()); + + return *SymName; +} + void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer, ArrayRef<ArrayRef<uint8_t>> IpiRecords, ArrayRef<ArrayRef<uint8_t>> TpiRecords) { diff --git a/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h b/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h index 0a365d4fe72a4..27942224053f1 100644 --- a/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h +++ b/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h @@ -30,12 +30,14 @@ namespace DwarfCFIEH { template <typename ELFT> class PrinterContext { + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Phdr = typename ELFT::Phdr; + ScopedPrinter &W; const object::ELFObjectFile<ELFT> *ObjF; - void printEHFrameHdr(uint64_t Offset, uint64_t Address, uint64_t Size) const; - - void printEHFrame(const typename ELFT::Shdr *EHFrameShdr) const; + void printEHFrameHdr(const Elf_Phdr *EHFramePHdr) const; + void printEHFrame(const Elf_Shdr *EHFrameShdr) const; public: PrinterContext(ScopedPrinter &W, const object::ELFObjectFile<ELFT> *ObjF) @@ -45,13 +47,14 @@ public: }; template <class ELFT> -static const typename object::ELFObjectFile<ELFT>::Elf_Shdr * +static const typename ELFT::Shdr * findSectionByAddress(const object::ELFObjectFile<ELFT> *ObjF, uint64_t Addr) { - auto Sections = ObjF->getELFFile()->sections(); - if (Error E = Sections.takeError()) - reportError(std::move(E), ObjF->getFileName()); + Expected<typename ELFT::ShdrRange> SectionsOrErr = + ObjF->getELFFile()->sections(); + if (!SectionsOrErr) + reportError(SectionsOrErr.takeError(), ObjF->getFileName()); - for (const auto &Shdr : *Sections) + for (const typename ELFT::Shdr &Shdr : *SectionsOrErr) if (Shdr.sh_addr == Addr) return &Shdr; return nullptr; @@ -60,61 +63,59 @@ findSectionByAddress(const object::ELFObjectFile<ELFT> *ObjF, uint64_t Addr) { template <typename ELFT> void PrinterContext<ELFT>::printUnwindInformation() const { const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); - const typename ELFT::Phdr *EHFramePhdr = nullptr; - auto PHs = Obj->program_headers(); - if (Error E = PHs.takeError()) - reportError(std::move(E), ObjF->getFileName()); - - for (const auto &Phdr : *PHs) { - if (Phdr.p_type == ELF::PT_GNU_EH_FRAME) { - EHFramePhdr = &Phdr; - if (Phdr.p_memsz != Phdr.p_filesz) - reportError(object::createError( - "p_memsz does not match p_filesz for GNU_EH_FRAME"), - ObjF->getFileName()); - break; - } - } + Expected<typename ELFT::PhdrRange> PhdrsOrErr = Obj->program_headers(); + if (!PhdrsOrErr) + reportError(PhdrsOrErr.takeError(), ObjF->getFileName()); - if (EHFramePhdr) - printEHFrameHdr(EHFramePhdr->p_offset, EHFramePhdr->p_vaddr, - EHFramePhdr->p_memsz); + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { + if (Phdr.p_type != ELF::PT_GNU_EH_FRAME) + continue; - auto Sections = Obj->sections(); - if (Error E = Sections.takeError()) - reportError(std::move(E), ObjF->getFileName()); + if (Phdr.p_memsz != Phdr.p_filesz) + reportError(object::createError( + "p_memsz does not match p_filesz for GNU_EH_FRAME"), + ObjF->getFileName()); + printEHFrameHdr(&Phdr); + break; + } - for (const auto &Shdr : *Sections) { - auto SectionName = Obj->getSectionName(&Shdr); - if (Error E = SectionName.takeError()) - reportError(std::move(E), ObjF->getFileName()); + Expected<typename ELFT::ShdrRange> SectionsOrErr = + ObjF->getELFFile()->sections(); + if (!SectionsOrErr) + reportError(SectionsOrErr.takeError(), ObjF->getFileName()); - if (*SectionName == ".eh_frame") + for (const Elf_Shdr &Shdr : *SectionsOrErr) { + Expected<StringRef> NameOrErr = Obj->getSectionName(&Shdr); + if (!NameOrErr) + reportError(NameOrErr.takeError(), ObjF->getFileName()); + if (*NameOrErr == ".eh_frame") printEHFrame(&Shdr); } } template <typename ELFT> -void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset, - uint64_t EHFrameHdrAddress, - uint64_t EHFrameHdrSize) const { +void PrinterContext<ELFT>::printEHFrameHdr(const Elf_Phdr *EHFramePHdr) const { DictScope L(W, "EHFrameHeader"); + uint64_t EHFrameHdrAddress = EHFramePHdr->p_vaddr; W.startLine() << format("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress); - W.startLine() << format("Offset: 0x%" PRIx64 "\n", EHFrameHdrOffset); - W.startLine() << format("Size: 0x%" PRIx64 "\n", EHFrameHdrSize); + W.startLine() << format("Offset: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_offset); + W.startLine() << format("Size: 0x%" PRIx64 "\n", (uint64_t)EHFramePHdr->p_memsz); const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); - const auto *EHFrameHdrShdr = findSectionByAddress(ObjF, EHFrameHdrAddress); - if (EHFrameHdrShdr) { - auto SectionName = Obj->getSectionName(EHFrameHdrShdr); - if (Error E = SectionName.takeError()) - reportError(std::move(E), ObjF->getFileName()); - - W.printString("Corresponding Section", *SectionName); + if (const Elf_Shdr *EHFrameHdr = + findSectionByAddress(ObjF, EHFramePHdr->p_vaddr)) { + Expected<StringRef> NameOrErr = Obj->getSectionName(EHFrameHdr); + if (!NameOrErr) + reportError(NameOrErr.takeError(), ObjF->getFileName()); + W.printString("Corresponding Section", *NameOrErr); } - DataExtractor DE(makeArrayRef(Obj->base() + EHFrameHdrOffset, EHFrameHdrSize), + Expected<ArrayRef<uint8_t>> Content = Obj->getSegmentContents(EHFramePHdr); + if (!Content) + reportError(Content.takeError(), ObjF->getFileName()); + + DataExtractor DE(*Content, ELFT::TargetEndianness == support::endianness::little, ELFT::Is64Bits ? 8 : 4); @@ -154,7 +155,7 @@ void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset, unsigned NumEntries = 0; uint64_t PrevPC = 0; - while (Offset + 8 <= EHFrameHdrSize && NumEntries < FDECount) { + while (Offset + 8 <= EHFramePHdr->p_memsz && NumEntries < FDECount) { DictScope D(W, std::string("entry ") + std::to_string(NumEntries)); auto InitialPC = DE.getSigned(&Offset, 4) + EHFrameHdrAddress; @@ -172,8 +173,7 @@ void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset, } template <typename ELFT> -void PrinterContext<ELFT>::printEHFrame( - const typename ELFT::Shdr *EHFrameShdr) const { +void PrinterContext<ELFT>::printEHFrame(const Elf_Shdr *EHFrameShdr) const { uint64_t Address = EHFrameShdr->sh_addr; uint64_t ShOffset = EHFrameShdr->sh_offset; W.startLine() << format(".eh_frame section at offset 0x%" PRIx64 @@ -181,26 +181,23 @@ void PrinterContext<ELFT>::printEHFrame( ShOffset, Address); W.indent(); - const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); - auto Result = Obj->getSectionContents(EHFrameShdr); - if (Error E = Result.takeError()) - reportError(std::move(E), ObjF->getFileName()); + Expected<ArrayRef<uint8_t>> DataOrErr = + ObjF->getELFFile()->getSectionContents(EHFrameShdr); + if (!DataOrErr) + reportError(DataOrErr.takeError(), ObjF->getFileName()); - auto Contents = Result.get(); - DWARFDataExtractor DE( - StringRef(reinterpret_cast<const char *>(Contents.data()), - Contents.size()), - ELFT::TargetEndianness == support::endianness::little, - ELFT::Is64Bits ? 8 : 4); + DWARFDataExtractor DE(*DataOrErr, + ELFT::TargetEndianness == support::endianness::little, + ELFT::Is64Bits ? 8 : 4); DWARFDebugFrame EHFrame(Triple::ArchType(ObjF->getArch()), /*IsEH=*/true, /*EHFrameAddress=*/Address); - EHFrame.parse(DE); + if (Error E = EHFrame.parse(DE)) + reportError(std::move(E), ObjF->getFileName()); - for (const auto &Entry : EHFrame) { - if (const auto *CIE = dyn_cast<dwarf::CIE>(&Entry)) { + for (const dwarf::FrameEntry &Entry : EHFrame) { + if (const dwarf::CIE *CIE = dyn_cast<dwarf::CIE>(&Entry)) { W.startLine() << format("[0x%" PRIx64 "] CIE length=%" PRIu64 "\n", - Address + CIE->getOffset(), - CIE->getLength()); + Address + CIE->getOffset(), CIE->getLength()); W.indent(); W.printNumber("version", CIE->getVersion()); @@ -208,47 +205,33 @@ void PrinterContext<ELFT>::printEHFrame( W.printNumber("code_alignment_factor", CIE->getCodeAlignmentFactor()); W.printNumber("data_alignment_factor", CIE->getDataAlignmentFactor()); W.printNumber("return_address_register", CIE->getReturnAddressRegister()); - - W.getOStream() << "\n"; - W.startLine() << "Program:\n"; - W.indent(); - CIE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel()); - W.unindent(); - - W.unindent(); - W.getOStream() << "\n"; - - } else if (const auto *FDE = dyn_cast<dwarf::FDE>(&Entry)) { + } else { + const dwarf::FDE *FDE = cast<dwarf::FDE>(&Entry); W.startLine() << format("[0x%" PRIx64 "] FDE length=%" PRIu64 " cie=[0x%" PRIx64 "]\n", - Address + FDE->getOffset(), - FDE->getLength(), + Address + FDE->getOffset(), FDE->getLength(), Address + FDE->getLinkedCIE()->getOffset()); W.indent(); W.startLine() << format("initial_location: 0x%" PRIx64 "\n", FDE->getInitialLocation()); - W.startLine() - << format("address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n", - FDE->getAddressRange(), - FDE->getInitialLocation() + FDE->getAddressRange()); - - W.getOStream() << "\n"; - W.startLine() << "Program:\n"; - W.indent(); - FDE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel()); - W.unindent(); - - W.unindent(); - W.getOStream() << "\n"; - } else { - llvm_unreachable("unexpected DWARF frame kind"); + W.startLine() << format( + "address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n", + FDE->getAddressRange(), + FDE->getInitialLocation() + FDE->getAddressRange()); } + + W.getOStream() << "\n"; + W.startLine() << "Program:\n"; + W.indent(); + Entry.cfis().dump(W.getOStream(), nullptr, W.getIndentLevel()); + W.unindent(); + W.unindent(); + W.getOStream() << "\n"; } W.unindent(); } - } } diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 8ffb68283405b..15076f1f89337 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -52,6 +52,8 @@ #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MipsABIFlags.h" +#include "llvm/Support/RISCVAttributeParser.h" +#include "llvm/Support/RISCVAttributes.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -135,19 +137,34 @@ struct DynRegionInfo { /// Name of the file. Used for error reporting. StringRef FileName; + /// Error prefix. Used for error reporting to provide more information. + std::string Context; + /// Region size name. Used for error reporting. + StringRef SizePrintName = "size"; + /// Entry size name. Used for error reporting. If this field is empty, errors + /// will not mention the entry size. + StringRef EntSizePrintName = "entry size"; template <typename Type> ArrayRef<Type> getAsArrayRef() const { const Type *Start = reinterpret_cast<const Type *>(Addr); if (!Start) return {Start, Start}; - if (EntSize != sizeof(Type) || Size % EntSize) { - // TODO: Add a section index to this warning. - reportWarning(createError("invalid section size (" + Twine(Size) + - ") or entity size (" + Twine(EntSize) + ")"), - FileName); - return {Start, Start}; - } - return {Start, Start + (Size / EntSize)}; + if (EntSize == sizeof(Type) && (Size % EntSize == 0)) + return {Start, Start + (Size / EntSize)}; + + std::string Msg; + if (!Context.empty()) + Msg += Context + " has "; + + Msg += ("invalid " + SizePrintName + " (0x" + Twine::utohexstr(Size) + ")") + .str(); + if (!EntSizePrintName.empty()) + Msg += + (" or " + EntSizePrintName + " (0x" + Twine::utohexstr(EntSize) + ")") + .str(); + + reportWarning(createError(Msg.c_str()), FileName); + return {Start, Start}; } }; @@ -204,7 +221,7 @@ public: void printProgramHeaders(bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) override; void printHashTable() override; - void printGnuHashTable() override; + void printGnuHashTable(const object::ObjectFile *Obj) override; void printLoadName() override; void printVersionInfo() override; void printGroupSections() override; @@ -213,7 +230,7 @@ public: void printStackMap() const override; - void printHashHistogram() override; + void printHashHistograms() override; void printCGProfile() override; void printAddrsig() override; @@ -268,10 +285,10 @@ private: DynRegionInfo DynRelaRegion; DynRegionInfo DynRelrRegion; DynRegionInfo DynPLTRelRegion; - DynRegionInfo DynSymRegion; + Optional<DynRegionInfo> DynSymRegion; DynRegionInfo DynamicTable; StringRef DynamicStringTable; - std::string SOName = "<Not found>"; + StringRef SOName = "<Not found>"; const Elf_Hash *HashTable = nullptr; const Elf_GnuHash *GnuHashTable = nullptr; const Elf_Shdr *DotSymtabSec = nullptr; @@ -290,6 +307,8 @@ private: }; mutable SmallVector<Optional<VersionEntry>, 16> VersionMap; + std::unordered_set<std::string> Warnings; + public: Elf_Dyn_Range dynamic_table() const { // A valid .dynamic section contains an array of entries terminated @@ -306,26 +325,31 @@ public: return Table.slice(0, Size); } + Optional<DynRegionInfo> getDynSymRegion() const { return DynSymRegion; } + Elf_Sym_Range dynamic_symbols() const { - return DynSymRegion.getAsArrayRef<Elf_Sym>(); + if (!DynSymRegion) + return Elf_Sym_Range(); + return DynSymRegion->getAsArrayRef<Elf_Sym>(); } Elf_Rel_Range dyn_rels() const; Elf_Rela_Range dyn_relas() const; Elf_Relr_Range dyn_relrs() const; - std::string getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable, + std::string getFullSymbolName(const Elf_Sym *Symbol, + Optional<StringRef> StrTable, bool IsDynamic) const; Expected<unsigned> getSymbolSectionIndex(const Elf_Sym *Symbol, const Elf_Sym *FirstSym) const; Expected<StringRef> getSymbolSectionName(const Elf_Sym *Symbol, unsigned SectionIndex) const; - Expected<std::string> getStaticSymbolName(uint32_t Index) const; - std::string getDynamicString(uint64_t Value) const; + std::string getStaticSymbolName(uint32_t Index) const; + StringRef getDynamicString(uint64_t Value) const; Expected<StringRef> getSymbolVersionByIndex(uint32_t VersionSymbolIndex, bool &IsDefault) const; void printSymbolsHelper(bool IsDynamic) const; - void printDynamicEntry(raw_ostream &OS, uint64_t Type, uint64_t Value) const; + std::string getDynamicEntry(uint64_t Type, uint64_t Value) const; const Elf_Shdr *getDotSymtabSec() const { return DotSymtabSec; } const Elf_Shdr *getDotCGProfileSec() const { return DotCGProfileSec; } @@ -347,6 +371,12 @@ public: getVersionDefinitions(const Elf_Shdr *Sec) const; Expected<std::vector<VerNeed>> getVersionDependencies(const Elf_Shdr *Sec) const; + + Expected<std::pair<const Elf_Sym *, std::string>> + getRelocationTarget(const Elf_Shdr *SymTab, const Elf_Rela &R) const; + + std::function<Error(const Twine &Msg)> WarningHandler; + void reportUniqueWarning(Error Err) const; }; template <class ELFT> @@ -439,12 +469,12 @@ ELFDumper<ELFT>::getVersionTable(const Elf_Shdr *Sec, ArrayRef<Elf_Sym> *SymTab, Expected<std::pair<ArrayRef<Elf_Sym>, StringRef>> SymTabOrErr = getLinkAsSymtab(Obj, Sec, SecNdx, SHT_DYNSYM); if (!SymTabOrErr) { - ELFDumperStyle->reportUniqueWarning(SymTabOrErr.takeError()); + reportUniqueWarning(SymTabOrErr.takeError()); return *VersionsOrErr; } if (SymTabOrErr->first.size() != VersionsOrErr->size()) - ELFDumperStyle->reportUniqueWarning( + reportUniqueWarning( createError("SHT_GNU_versym section with index " + Twine(SecNdx) + ": the number of entries (" + Twine(VersionsOrErr->size()) + ") does not match the number of symbols (" + @@ -490,7 +520,7 @@ ELFDumper<ELFT>::getVersionDefinitions(const Elf_Shdr *Sec) const { VerdAux Aux; Aux.Offset = VerdauxBuf - Start; if (Verdaux->vda_name <= StrTabOrErr->size()) - Aux.Name = StrTabOrErr->drop_front(Verdaux->vda_name); + Aux.Name = std::string(StrTabOrErr->drop_front(Verdaux->vda_name)); else Aux.Name = "<invalid vda_name: " + to_string(Verdaux->vda_name) + ">"; return Aux; @@ -558,7 +588,7 @@ ELFDumper<ELFT>::getVersionDependencies(const Elf_Shdr *Sec) const { StringRef StrTab; Expected<StringRef> StrTabOrErr = getLinkAsStrtab(Obj, Sec, SecNdx); if (!StrTabOrErr) - ELFDumperStyle->reportUniqueWarning(StrTabOrErr.takeError()); + reportUniqueWarning(StrTabOrErr.takeError()); else StrTab = *StrTabOrErr; @@ -600,7 +630,7 @@ ELFDumper<ELFT>::getVersionDependencies(const Elf_Shdr *Sec) const { VN.Offset = VerneedBuf - Start; if (Verneed->vn_file < StrTab.size()) - VN.File = StrTab.drop_front(Verneed->vn_file); + VN.File = std::string(StrTab.drop_front(Verneed->vn_file)); else VN.File = "<corrupt vn_file: " + to_string(Verneed->vn_file) + ">"; @@ -630,7 +660,7 @@ ELFDumper<ELFT>::getVersionDependencies(const Elf_Shdr *Sec) const { if (StrTab.size() <= Vernaux->vna_name) Aux.Name = "<corrupt>"; else - Aux.Name = StrTab.drop_front(Vernaux->vna_name); + Aux.Name = std::string(StrTab.drop_front(Vernaux->vna_name)); VernauxBuf += Vernaux->vna_next; } @@ -641,7 +671,8 @@ ELFDumper<ELFT>::getVersionDependencies(const Elf_Shdr *Sec) const { template <class ELFT> void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const { - StringRef StrTable, SymtabName; + Optional<StringRef> StrTable; + StringRef SymtabName; size_t Entries = 0; Elf_Sym_Range Syms(nullptr, nullptr); const ELFFile<ELFT> *Obj = ObjF->getELFFile(); @@ -649,16 +680,36 @@ void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const { StrTable = DynamicStringTable; Syms = dynamic_symbols(); SymtabName = DynSymtabName; - if (DynSymRegion.Addr) - Entries = DynSymRegion.Size / DynSymRegion.EntSize; + Entries = Syms.size(); } else { if (!DotSymtabSec) return; - StrTable = unwrapOrError(ObjF->getFileName(), - Obj->getStringTableForSymtab(*DotSymtabSec)); - Syms = unwrapOrError(ObjF->getFileName(), Obj->symbols(DotSymtabSec)); - SymtabName = - unwrapOrError(ObjF->getFileName(), Obj->getSectionName(DotSymtabSec)); + + if (Expected<StringRef> StrTableOrErr = + Obj->getStringTableForSymtab(*DotSymtabSec)) + StrTable = *StrTableOrErr; + else + reportUniqueWarning(createError( + "unable to get the string table for the SHT_SYMTAB section: " + + toString(StrTableOrErr.takeError()))); + + if (Expected<Elf_Sym_Range> SymsOrErr = Obj->symbols(DotSymtabSec)) + Syms = *SymsOrErr; + else + reportUniqueWarning( + createError("unable to read symbols from the SHT_SYMTAB section: " + + toString(SymsOrErr.takeError()))); + + if (Expected<StringRef> SymtabNameOrErr = + Obj->getSectionName(DotSymtabSec)) { + SymtabName = *SymtabNameOrErr; + } else { + reportUniqueWarning( + createError("unable to get the name of the SHT_SYMTAB section: " + + toString(SymtabNameOrErr.takeError()))); + SymtabName = "<?>"; + } + Entries = DotSymtabSec->getEntityCount(); } if (Syms.begin() == Syms.end()) @@ -687,14 +738,6 @@ public: DumpStyle(ELFDumper<ELFT> *Dumper) : Dumper(Dumper) { FileName = this->Dumper->getElfObject()->getFileName(); - - // Dumper reports all non-critical errors as warnings. - // It does not print the same warning more than once. - WarningHandler = [this](const Twine &Msg) { - if (Warnings.insert(Msg.str()).second) - reportWarning(createError(Msg), FileName); - return Error::success(); - }; } virtual ~DumpStyle() = default; @@ -712,8 +755,9 @@ public: virtual void printSymtabMessage(const ELFFile<ELFT> *Obj, StringRef Name, size_t Offset, bool NonVisibilityBitsUsed) {} virtual void printSymbol(const ELFFile<ELFT> *Obj, const Elf_Sym *Symbol, - const Elf_Sym *FirstSym, StringRef StrTable, - bool IsDynamic, bool NonVisibilityBitsUsed) = 0; + const Elf_Sym *FirstSym, + Optional<StringRef> StrTable, bool IsDynamic, + bool NonVisibilityBitsUsed) = 0; virtual void printProgramHeaders(const ELFFile<ELFT> *Obj, bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) = 0; @@ -723,7 +767,7 @@ public: 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 printHashHistograms(const ELFFile<ELFT> *Obj) = 0; virtual void printCGProfile(const ELFFile<ELFT> *Obj) = 0; virtual void printAddrsig(const ELFFile<ELFT> *Obj) = 0; virtual void printNotes(const ELFFile<ELFT> *Obj) = 0; @@ -734,7 +778,7 @@ public: void printRelocatableStackSizes(const ELFObjectFile<ELFT> *Obj, std::function<void()> PrintHeader); void printFunctionStackSize(const ELFObjectFile<ELFT> *Obj, uint64_t SymValue, - SectionRef FunctionSec, + Optional<SectionRef> FunctionSec, const StringRef SectionName, DataExtractor Data, uint64_t *Offset); void printStackSize(const ELFObjectFile<ELFT> *Obj, RelocationRef Rel, @@ -747,14 +791,16 @@ public: virtual void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) = 0; const ELFDumper<ELFT> *dumper() const { return Dumper; } - void reportUniqueWarning(Error Err) const; - protected: - std::function<Error(const Twine &Msg)> WarningHandler; + void printDependentLibsHelper( + const ELFFile<ELFT> *Obj, + function_ref<void(const Elf_Shdr &)> OnSectionStart, + function_ref<void(StringRef, uint64_t)> OnSectionEntry); + + void reportUniqueWarning(Error Err) const; StringRef FileName; private: - std::unordered_set<std::string> Warnings; const ELFDumper<ELFT> *Dumper; }; @@ -790,7 +836,7 @@ public: const Elf_Shdr *Sec) override; void printVersionDependencySection(const ELFFile<ELFT> *Obj, const Elf_Shdr *Sec) override; - void printHashHistogram(const ELFFile<ELFT> *Obj) override; + void printHashHistograms(const ELFFile<ELFT> *Obj) override; void printCGProfile(const ELFFile<ELFT> *Obj) override; void printAddrsig(const ELFFile<ELFT> *Obj) override; void printNotes(const ELFFile<ELFT> *Obj) override; @@ -802,11 +848,18 @@ public: void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) override; private: + void printHashHistogram(const Elf_Hash &HashTable); + void printGnuHashHistogram(const Elf_GnuHash &GnuHashTable); + + void printHashTableSymbols(const ELFO *Obj, const Elf_Hash &HashTable); + void printGnuHashTableSymbols(const ELFO *Obj, + const Elf_GnuHash &GnuHashTable); + struct Field { std::string Str; unsigned Column; - Field(StringRef S, unsigned Col) : Str(S), Column(Col) {} + Field(StringRef S, unsigned Col) : Str(std::string(S)), Column(Col) {} Field(unsigned Col) : Column(Col) {} }; @@ -814,7 +867,7 @@ private: std::string printEnum(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues) { for (const auto &EnumItem : EnumValues) if (EnumItem.Value == Value) - return EnumItem.AltName; + return std::string(EnumItem.AltName); return to_hexString(Value, false); } @@ -855,20 +908,17 @@ private: void printHashedSymbol(const ELFO *Obj, const Elf_Sym *FirstSym, uint32_t Sym, StringRef StrTable, uint32_t Bucket); void printRelocHeader(unsigned SType); - void printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab, - const Elf_Rela &R, bool IsRela); + void printRelocation(const ELFO *Obj, unsigned SecIndex, + const Elf_Shdr *SymTab, const Elf_Rela &R, + unsigned RelIndex, 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, + Optional<StringRef> StrTable, bool IsDynamic, bool NonVisibilityBitsUsed) override; std::string getSymbolSectionNdx(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *FirstSym); void printDynamicRelocation(const ELFO *Obj, Elf_Rela R, bool IsRela); - bool checkTLSSections(const Elf_Phdr &Phdr, const Elf_Shdr &Sec); - 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); void printGNUVersionSectionProlog(const ELFFile<ELFT> *Obj, @@ -877,13 +927,18 @@ private: }; template <class ELFT> -void DumpStyle<ELFT>::reportUniqueWarning(Error Err) const { +void ELFDumper<ELFT>::reportUniqueWarning(Error Err) const { handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { cantFail(WarningHandler(EI.message()), "WarningHandler should always return ErrorSuccess"); }); } +template <class ELFT> +void DumpStyle<ELFT>::reportUniqueWarning(Error Err) const { + this->dumper()->reportUniqueWarning(std::move(Err)); +} + template <typename ELFT> class LLVMStyle : public DumpStyle<ELFT> { public: TYPEDEF_ELF_TYPES(ELFT) @@ -909,7 +964,7 @@ public: const Elf_Shdr *Sec) override; void printVersionDependencySection(const ELFFile<ELFT> *Obj, const Elf_Shdr *Sec) override; - void printHashHistogram(const ELFFile<ELFT> *Obj) override; + void printHashHistograms(const ELFFile<ELFT> *Obj) override; void printCGProfile(const ELFFile<ELFT> *Obj) override; void printAddrsig(const ELFFile<ELFT> *Obj) override; void printNotes(const ELFFile<ELFT> *Obj) override; @@ -921,13 +976,14 @@ public: void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) override; private: - void printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab); + void printRelocation(const ELFO *Obj, unsigned SecIndex, Elf_Rela Rel, + unsigned RelIndex, const Elf_Shdr *SymTab); void printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel); void printSymbols(const ELFO *Obj); void printDynamicSymbols(const ELFO *Obj); void printSymbolSection(const Elf_Sym *Symbol, const Elf_Sym *First); void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First, - StringRef StrTable, bool IsDynamic, + Optional<StringRef> StrTable, bool IsDynamic, bool /*NonVisibilityBitsUsed*/) override; void printProgramHeaders(const ELFO *Obj); void printSectionMapping(const ELFO *Obj) {} @@ -973,7 +1029,7 @@ std::error_code createELFDumper(const object::ObjectFile *Obj, template <class ELFT> Error ELFDumper<ELFT>::LoadVersionMap() const { // If there is no dynamic symtab or version table, there is nothing to do. - if (!DynSymRegion.Addr || !SymbolVersionSection) + if (!DynSymRegion || !SymbolVersionSection) return Error::success(); // Has the VersionMap already been loaded? @@ -988,7 +1044,7 @@ template <class ELFT> Error ELFDumper<ELFT>::LoadVersionMap() const { auto InsertEntry = [this](unsigned N, StringRef Version, bool IsVerdef) { if (N >= VersionMap.size()) VersionMap.resize(N + 1); - VersionMap[N] = {Version, IsVerdef}; + VersionMap[N] = {std::string(Version), IsVerdef}; }; if (SymbolVersionDefSection) { @@ -1023,38 +1079,85 @@ Expected<StringRef> ELFDumper<ELFT>::getSymbolVersion(const Elf_Sym *Sym, return ""; } + assert(DynSymRegion && "DynSymRegion has not been initialised"); // Determine the position in the symbol table of this entry. size_t EntryIndex = (reinterpret_cast<uintptr_t>(Sym) - - reinterpret_cast<uintptr_t>(DynSymRegion.Addr)) / - sizeof(Elf_Sym); + reinterpret_cast<uintptr_t>(DynSymRegion->Addr)) / + sizeof(Elf_Sym); // Get the corresponding version index entry. - const Elf_Versym *Versym = unwrapOrError( - ObjF->getFileName(), ObjF->getELFFile()->template getEntry<Elf_Versym>( - SymbolVersionSection, EntryIndex)); - return this->getSymbolVersionByIndex(Versym->vs_index, IsDefault); + if (Expected<const Elf_Versym *> EntryOrErr = + ObjF->getELFFile()->template getEntry<Elf_Versym>( + SymbolVersionSection, EntryIndex)) + return this->getSymbolVersionByIndex((*EntryOrErr)->vs_index, IsDefault); + else + return EntryOrErr.takeError(); +} + +template <typename ELFT> +Expected<std::pair<const typename ELFT::Sym *, std::string>> +ELFDumper<ELFT>::getRelocationTarget(const Elf_Shdr *SymTab, + const Elf_Rela &R) const { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + Expected<const Elf_Sym *> SymOrErr = Obj->getRelocationSymbol(&R, SymTab); + if (!SymOrErr) + return SymOrErr.takeError(); + const Elf_Sym *Sym = *SymOrErr; + if (!Sym) + return std::make_pair(nullptr, ""); + + // The st_name field of a STT_SECTION is usually 0 (empty string). + // This code block returns the section name. + if (Sym->getType() == ELF::STT_SECTION) { + Expected<const Elf_Shdr *> SecOrErr = + Obj->getSection(Sym, SymTab, ShndxTable); + if (!SecOrErr) + return SecOrErr.takeError(); + // A section symbol describes the section at index 0. + if (*SecOrErr == nullptr) + return std::make_pair(Sym, ""); + + Expected<StringRef> NameOrErr = Obj->getSectionName(*SecOrErr); + if (!NameOrErr) + return NameOrErr.takeError(); + return std::make_pair(Sym, NameOrErr->str()); + } + + Expected<StringRef> StrTableOrErr = Obj->getStringTableForSymtab(*SymTab); + if (!StrTableOrErr) + return StrTableOrErr.takeError(); + + std::string SymbolName = + getFullSymbolName(Sym, *StrTableOrErr, SymTab->sh_type == SHT_DYNSYM); + return std::make_pair(Sym, SymbolName); } static std::string maybeDemangle(StringRef Name) { - return opts::Demangle ? demangle(Name) : Name.str(); + return opts::Demangle ? demangle(std::string(Name)) : Name.str(); } template <typename ELFT> -Expected<std::string> -ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const { +std::string ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const { + auto Warn = [&](Error E) -> std::string { + this->reportUniqueWarning( + createError("unable to read the name of symbol with index " + + Twine(Index) + ": " + toString(std::move(E)))); + return "<?>"; + }; + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); Expected<const typename ELFT::Sym *> SymOrErr = Obj->getSymbol(DotSymtabSec, Index); if (!SymOrErr) - return SymOrErr.takeError(); + return Warn(SymOrErr.takeError()); Expected<StringRef> StrTabOrErr = Obj->getStringTableForSymtab(*DotSymtabSec); if (!StrTabOrErr) - return StrTabOrErr.takeError(); + return Warn(StrTabOrErr.takeError()); Expected<StringRef> NameOrErr = (*SymOrErr)->getName(*StrTabOrErr); if (!NameOrErr) - return NameOrErr.takeError(); + return Warn(NameOrErr.takeError()); return maybeDemangle(*NameOrErr); } @@ -1087,10 +1190,18 @@ ELFDumper<ELFT>::getSymbolVersionByIndex(uint32_t SymbolVersionIndex, template <typename ELFT> std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol, - StringRef StrTable, + Optional<StringRef> StrTable, bool IsDynamic) const { - std::string SymbolName = maybeDemangle( - unwrapOrError(ObjF->getFileName(), Symbol->getName(StrTable))); + if (!StrTable) + return "<?>"; + + std::string SymbolName; + if (Expected<StringRef> NameOrErr = Symbol->getName(*StrTable)) { + SymbolName = maybeDemangle(*NameOrErr); + } else { + reportUniqueWarning(NameOrErr.takeError()); + return "<?>"; + } if (SymbolName.empty() && Symbol->getType() == ELF::STT_SECTION) { Elf_Sym_Range Syms = unwrapOrError( @@ -1098,15 +1209,15 @@ std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol, Expected<unsigned> SectionIndex = getSymbolSectionIndex(Symbol, Syms.begin()); if (!SectionIndex) { - ELFDumperStyle->reportUniqueWarning(SectionIndex.takeError()); + reportUniqueWarning(SectionIndex.takeError()); return "<?>"; } Expected<StringRef> NameOrErr = getSymbolSectionName(Symbol, *SectionIndex); if (!NameOrErr) { - ELFDumperStyle->reportUniqueWarning(NameOrErr.takeError()); + reportUniqueWarning(NameOrErr.takeError()); return ("<section " + Twine(*SectionIndex) + ">").str(); } - return *NameOrErr; + return std::string(*NameOrErr); } if (!IsDynamic) @@ -1115,7 +1226,7 @@ std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol, bool IsDefault; Expected<StringRef> VersionOrErr = getSymbolVersion(&*Symbol, IsDefault); if (!VersionOrErr) { - ELFDumperStyle->reportUniqueWarning(VersionOrErr.takeError()); + reportUniqueWarning(VersionOrErr.takeError()); return SymbolName + "@<corrupt>"; } @@ -1170,7 +1281,7 @@ template <class ELFO> static const typename ELFO::Elf_Shdr * findNotEmptySectionByAddress(const ELFO *Obj, StringRef FileName, uint64_t Addr) { - for (const auto &Shdr : unwrapOrError(FileName, Obj->sections())) + for (const typename ELFO::Elf_Shdr &Shdr : cantFail(Obj->sections())) if (Shdr.sh_addr == Addr && Shdr.sh_size > 0) return &Shdr; return nullptr; @@ -1179,7 +1290,7 @@ findNotEmptySectionByAddress(const ELFO *Obj, StringRef FileName, template <class ELFO> static const typename ELFO::Elf_Shdr * findSectionByName(const ELFO &Obj, StringRef FileName, StringRef Name) { - for (const auto &Shdr : unwrapOrError(FileName, Obj.sections())) + for (const typename ELFO::Elf_Shdr &Shdr : cantFail(Obj.sections())) if (Name == unwrapOrError(FileName, Obj.getSectionName(&Shdr))) return &Shdr; return nullptr; @@ -1372,6 +1483,8 @@ static const EnumEntry<unsigned> ElfMachineType[] = { ENUM_ENT(EM_STXP7X, "STMicroelectronics STxP7x family"), ENUM_ENT(EM_NDS32, "Andes Technology compact code size embedded RISC processor family"), ENUM_ENT(EM_ECOG1, "Cyan Technology eCOG1 microprocessor"), + // FIXME: Following EM_ECOG1X definitions is dead code since EM_ECOG1X has + // an identical number to EM_ECOG1. ENUM_ENT(EM_ECOG1X, "Cyan Technology eCOG1X family"), ENUM_ENT(EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core microcontrollers"), ENUM_ENT(EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor"), @@ -1406,6 +1519,7 @@ static const EnumEntry<unsigned> ElfMachineType[] = { ENUM_ENT(EM_RISCV, "RISC-V"), ENUM_ENT(EM_LANAI, "EM_LANAI"), ENUM_ENT(EM_BPF, "EM_BPF"), + ENUM_ENT(EM_VE, "NEC SX-Aurora Vector Engine"), }; static const EnumEntry<unsigned> ElfSymbolBindings[] = { @@ -1731,6 +1845,7 @@ static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1010), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1011), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1012), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX1030), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_XNACK), LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_SRAM_ECC) }; @@ -1786,18 +1901,22 @@ std::pair<const typename ELFT::Phdr *, const typename ELFT::Shdr *> ELFDumper<ELFT>::findDynamic(const ELFFile<ELFT> *Obj) { // Try to locate the PT_DYNAMIC header. const Elf_Phdr *DynamicPhdr = nullptr; - for (const Elf_Phdr &Phdr : - unwrapOrError(ObjF->getFileName(), Obj->program_headers())) { - if (Phdr.p_type != ELF::PT_DYNAMIC) - continue; - DynamicPhdr = &Phdr; - break; + if (Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers()) { + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { + if (Phdr.p_type != ELF::PT_DYNAMIC) + continue; + DynamicPhdr = &Phdr; + break; + } + } else { + this->reportUniqueWarning(createError( + "unable to read program headers to locate the PT_DYNAMIC segment: " + + toString(PhdrsOrErr.takeError()))); } // Try to locate the .dynamic section in the sections header table. const Elf_Shdr *DynamicSec = nullptr; - for (const Elf_Shdr &Sec : - unwrapOrError(ObjF->getFileName(), Obj->sections())) { + for (const Elf_Shdr &Sec : cantFail(Obj->sections())) { if (Sec.sh_type != ELF::SHT_DYNAMIC) continue; DynamicSec = &Sec; @@ -1847,6 +1966,9 @@ void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) { bool IsPhdrTableValid = false; if (DynamicPhdr) { FromPhdr = createDRIFrom(DynamicPhdr, sizeof(Elf_Dyn)); + FromPhdr.SizePrintName = "PT_DYNAMIC size"; + FromPhdr.EntSizePrintName = ""; + IsPhdrTableValid = !FromPhdr.getAsArrayRef<Elf_Dyn>().empty(); } @@ -1860,6 +1982,11 @@ void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) { FromSec = checkDRI({ObjF->getELFFile()->base() + DynamicSec->sh_offset, DynamicSec->sh_size, sizeof(Elf_Dyn), ObjF->getFileName()}); + FromSec.Context = ("section with index " + + Twine(DynamicSec - &cantFail(Obj->sections()).front())) + .str(); + FromSec.EntSizePrintName = ""; + IsSecTableValid = !FromSec.getAsArrayRef<Elf_Dyn>().empty(); } @@ -1917,19 +2044,33 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, ScopedPrinter &Writer) : ObjDumper(Writer), ObjF(ObjF), DynRelRegion(ObjF->getFileName()), DynRelaRegion(ObjF->getFileName()), DynRelrRegion(ObjF->getFileName()), - DynPLTRelRegion(ObjF->getFileName()), DynSymRegion(ObjF->getFileName()), - DynamicTable(ObjF->getFileName()) { + DynPLTRelRegion(ObjF->getFileName()), DynamicTable(ObjF->getFileName()) { + // Dumper reports all non-critical errors as warnings. + // It does not print the same warning more than once. + WarningHandler = [this](const Twine &Msg) { + if (Warnings.insert(Msg.str()).second) + reportWarning(createError(Msg), this->ObjF->getFileName()); + return Error::success(); + }; + + if (opts::Output == opts::GNU) + ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this)); + else + ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this)); + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); - for (const Elf_Shdr &Sec : - unwrapOrError(ObjF->getFileName(), Obj->sections())) { + typename ELFT::ShdrRange Sections = cantFail(Obj->sections()); + for (const Elf_Shdr &Sec : Sections) { switch (Sec.sh_type) { case ELF::SHT_SYMTAB: if (!DotSymtabSec) DotSymtabSec = &Sec; break; case ELF::SHT_DYNSYM: - if (!DynSymRegion.Size) { + if (!DynSymRegion) { DynSymRegion = createDRIFrom(&Sec); + DynSymRegion->Context = + ("section with index " + Twine(&Sec - &Sections.front())).str(); // This is only used (if Elf_Shdr present)for naming section in GNU // style DynSymtabName = @@ -1968,11 +2109,6 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, } loadDynamicTable(Obj); - - if (opts::Output == opts::GNU) - ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this)); - else - ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this)); } template <typename ELFT> @@ -1993,6 +2129,7 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) { uint64_t SONameOffset = 0; const char *StringTableBegin = nullptr; uint64_t StringTableSize = 0; + Optional<DynRegionInfo> DynSymFromTable; for (const Elf_Dyn &Dyn : dynamic_table()) { switch (Dyn.d_tag) { case ELF::DT_HASH: @@ -2011,36 +2148,36 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) { StringTableSize = Dyn.getVal(); break; case ELF::DT_SYMTAB: { - // Often we find the information about the dynamic symbol table - // location in the SHT_DYNSYM section header. However, the value in - // DT_SYMTAB has priority, because it is used by dynamic loaders to - // locate .dynsym at runtime. The location we find in the section header - // and the location we find here should match. If we can't map the - // DT_SYMTAB value to an address (e.g. when there are no program headers), we - // ignore its value. + // If we can't map the DT_SYMTAB value to an address (e.g. when there are + // no program headers), we ignore its value. if (const uint8_t *VA = toMappedAddr(Dyn.getTag(), Dyn.getPtr())) { - // EntSize is non-zero if the dynamic symbol table has been found via a - // section header. - if (DynSymRegion.EntSize && VA != DynSymRegion.Addr) - reportWarning( - createError( - "SHT_DYNSYM section header and DT_SYMTAB disagree about " - "the location of the dynamic symbol table"), - ObjF->getFileName()); - - DynSymRegion.Addr = VA; - DynSymRegion.EntSize = sizeof(Elf_Sym); + DynSymFromTable.emplace(ObjF->getFileName()); + DynSymFromTable->Addr = VA; + DynSymFromTable->EntSize = sizeof(Elf_Sym); + DynSymFromTable->EntSizePrintName = ""; } break; } + case ELF::DT_SYMENT: { + uint64_t Val = Dyn.getVal(); + if (Val != sizeof(Elf_Sym)) + reportWarning(createError("DT_SYMENT value of 0x" + + Twine::utohexstr(Val) + + " is not the size of a symbol (0x" + + Twine::utohexstr(sizeof(Elf_Sym)) + ")"), + ObjF->getFileName()); + break; + } case ELF::DT_RELA: DynRelaRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); break; case ELF::DT_RELASZ: DynRelaRegion.Size = Dyn.getVal(); + DynRelaRegion.SizePrintName = "DT_RELASZ value"; break; case ELF::DT_RELAENT: DynRelaRegion.EntSize = Dyn.getVal(); + DynRelaRegion.EntSizePrintName = "DT_RELAENT value"; break; case ELF::DT_SONAME: SONameOffset = Dyn.getVal(); @@ -2050,9 +2187,11 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) { break; case ELF::DT_RELSZ: DynRelRegion.Size = Dyn.getVal(); + DynRelRegion.SizePrintName = "DT_RELSZ value"; break; case ELF::DT_RELENT: DynRelRegion.EntSize = Dyn.getVal(); + DynRelRegion.EntSizePrintName = "DT_RELENT value"; break; case ELF::DT_RELR: case ELF::DT_ANDROID_RELR: @@ -2061,10 +2200,16 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) { case ELF::DT_RELRSZ: case ELF::DT_ANDROID_RELRSZ: DynRelrRegion.Size = Dyn.getVal(); + DynRelrRegion.SizePrintName = Dyn.d_tag == ELF::DT_RELRSZ + ? "DT_RELRSZ value" + : "DT_ANDROID_RELRSZ value"; break; case ELF::DT_RELRENT: case ELF::DT_ANDROID_RELRENT: DynRelrRegion.EntSize = Dyn.getVal(); + DynRelrRegion.EntSizePrintName = Dyn.d_tag == ELF::DT_RELRENT + ? "DT_RELRENT value" + : "DT_ANDROID_RELRENT value"; break; case ELF::DT_PLTREL: if (Dyn.getVal() == DT_REL) @@ -2075,18 +2220,78 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) { reportError(createError(Twine("unknown DT_PLTREL value of ") + Twine((uint64_t)Dyn.getVal())), ObjF->getFileName()); + DynPLTRelRegion.EntSizePrintName = ""; break; case ELF::DT_JMPREL: DynPLTRelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); break; case ELF::DT_PLTRELSZ: DynPLTRelRegion.Size = Dyn.getVal(); + DynPLTRelRegion.SizePrintName = "DT_PLTRELSZ value"; break; } } - if (StringTableBegin) - DynamicStringTable = StringRef(StringTableBegin, StringTableSize); + + if (StringTableBegin) { + const uint64_t FileSize = ObjF->getELFFile()->getBufSize(); + const uint64_t Offset = + (const uint8_t *)StringTableBegin - ObjF->getELFFile()->base(); + if (StringTableSize > FileSize - Offset) + reportUniqueWarning(createError( + "the dynamic string table at 0x" + Twine::utohexstr(Offset) + + " goes past the end of the file (0x" + Twine::utohexstr(FileSize) + + ") with DT_STRSZ = 0x" + Twine::utohexstr(StringTableSize))); + else + DynamicStringTable = StringRef(StringTableBegin, StringTableSize); + } + SOName = getDynamicString(SONameOffset); + + if (DynSymRegion) { + // Often we find the information about the dynamic symbol table + // location in the SHT_DYNSYM section header. However, the value in + // DT_SYMTAB has priority, because it is used by dynamic loaders to + // locate .dynsym at runtime. The location we find in the section header + // and the location we find here should match. + if (DynSymFromTable && DynSymFromTable->Addr != DynSymRegion->Addr) + reportUniqueWarning( + createError("SHT_DYNSYM section header and DT_SYMTAB disagree about " + "the location of the dynamic symbol table")); + + // According to the ELF gABI: "The number of symbol table entries should + // equal nchain". Check to see if the DT_HASH hash table nchain value + // conflicts with the number of symbols in the dynamic symbol table + // according to the section header. + if (HashTable) { + if (DynSymRegion->EntSize == 0) + reportUniqueWarning( + createError("SHT_DYNSYM section has sh_entsize == 0")); + else if (HashTable->nchain != DynSymRegion->Size / DynSymRegion->EntSize) + reportUniqueWarning(createError( + "hash table nchain (" + Twine(HashTable->nchain) + + ") differs from symbol count derived from SHT_DYNSYM section " + "header (" + + Twine(DynSymRegion->Size / DynSymRegion->EntSize) + ")")); + } + } + + // Delay the creation of the actual dynamic symbol table until now, so that + // checks can always be made against the section header-based properties, + // without worrying about tag order. + if (DynSymFromTable) { + if (!DynSymRegion) { + DynSymRegion = DynSymFromTable; + } else { + DynSymRegion->Addr = DynSymFromTable->Addr; + DynSymRegion->EntSize = DynSymFromTable->EntSize; + DynSymRegion->EntSizePrintName = DynSymFromTable->EntSizePrintName; + } + } + + // Derive the dynamic symbol table size from the DT_HASH hash table, if + // present. + if (HashTable && DynSymRegion) + DynSymRegion->Size = HashTable->nchain * DynSymRegion->EntSize; } template <typename ELFT> @@ -2156,8 +2361,8 @@ template <class ELFT> void ELFDumper<ELFT>::printHashSymbols() { ELFDumperStyle->printHashSymbols(ObjF->getELFFile()); } -template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() { - ELFDumperStyle->printHashHistogram(ObjF->getELFFile()); +template <class ELFT> void ELFDumper<ELFT>::printHashHistograms() { + ELFDumperStyle->printHashHistograms(ObjF->getELFFile()); } template <class ELFT> void ELFDumper<ELFT>::printCGProfile() { @@ -2213,7 +2418,8 @@ static const EnumEntry<unsigned> ElfDynamicDTFlags1[] = { LLVM_READOBJ_DT_FLAG_ENT(DF_1, NORELOC), LLVM_READOBJ_DT_FLAG_ENT(DF_1, SYMINTPOSE), LLVM_READOBJ_DT_FLAG_ENT(DF_1, GLOBAUDIT), - LLVM_READOBJ_DT_FLAG_ENT(DF_1, SINGLETON) + LLVM_READOBJ_DT_FLAG_ENT(DF_1, SINGLETON), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, PIE), }; static const EnumEntry<unsigned> ElfDynamicDTMipsFlags[] = { @@ -2257,10 +2463,24 @@ void printFlags(T Value, ArrayRef<EnumEntry<TFlag>> Flags, raw_ostream &OS) { } template <class ELFT> -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; +std::string ELFDumper<ELFT>::getDynamicEntry(uint64_t Type, + uint64_t Value) const { + auto FormatHexValue = [](uint64_t V) { + std::string Str; + raw_string_ostream OS(Str); + const char *ConvChar = + (opts::Output == opts::GNU) ? "0x%" PRIx64 : "0x%" PRIX64; + OS << format(ConvChar, V); + return OS.str(); + }; + + auto FormatFlags = [](uint64_t V, + llvm::ArrayRef<llvm::EnumEntry<unsigned int>> Array) { + std::string Str; + raw_string_ostream OS(Str); + printFlags(V, Array, OS); + return OS.str(); + }; // Handle custom printing of architecture specific tags switch (ObjF->getELFFile()->getHeader()->e_machine) { @@ -2268,8 +2488,7 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, switch (Type) { case DT_AARCH64_BTI_PLT: case DT_AARCH64_PAC_PLT: - OS << Value; - return; + return std::to_string(Value); default: break; } @@ -2277,12 +2496,10 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, case EM_HEXAGON: switch (Type) { case DT_HEXAGON_VER: - OS << Value; - return; + return std::to_string(Value); case DT_HEXAGON_SYMSZ: case DT_HEXAGON_PLT: - OS << format(ConvChar, Value); - return; + return FormatHexValue(Value); default: break; } @@ -2293,8 +2510,7 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, case DT_MIPS_LOCAL_GOTNO: case DT_MIPS_SYMTABNO: case DT_MIPS_UNREFEXTNO: - OS << Value; - return; + return std::to_string(Value); case DT_MIPS_TIME_STAMP: case DT_MIPS_ICHECKSUM: case DT_MIPS_IVERSION: @@ -2335,11 +2551,9 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, case DT_MIPS_PLTGOT: case DT_MIPS_RWPLT: case DT_MIPS_RLD_MAP_REL: - OS << format(ConvChar, Value); - return; + return FormatHexValue(Value); case DT_MIPS_FLAGS: - printFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags), OS); - return; + return FormatFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags)); default: break; } @@ -2350,13 +2564,10 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, switch (Type) { case DT_PLTREL: - if (Value == DT_REL) { - OS << "REL"; - break; - } else if (Value == DT_RELA) { - OS << "RELA"; - break; - } + if (Value == DT_REL) + return "REL"; + if (Value == DT_RELA) + return "RELA"; LLVM_FALLTHROUGH; case DT_PLTGOT: case DT_HASH: @@ -2376,14 +2587,12 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, case DT_VERSYM: case DT_GNU_HASH: case DT_NULL: - OS << format(ConvChar, Value); - break; + return FormatHexValue(Value); case DT_RELACOUNT: case DT_RELCOUNT: case DT_VERDEFNUM: case DT_VERNEEDNUM: - OS << Value; - break; + return std::to_string(Value); case DT_PLTRELSZ: case DT_RELASZ: case DT_RELAENT: @@ -2396,8 +2605,7 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, case DT_PREINIT_ARRAYSZ: case DT_ANDROID_RELSZ: case DT_ANDROID_RELASZ: - OS << Value << " (bytes)"; - break; + return std::to_string(Value) + " (bytes)"; case DT_NEEDED: case DT_SONAME: case DT_AUXILIARY: @@ -2405,37 +2613,62 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, case DT_FILTER: case DT_RPATH: 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"}, + 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) << ": [" << getDynamicString(Value) << "]"; - break; + + return (Twine(TagNames.at(Type)) + ": [" + getDynamicString(Value) + "]") + .str(); } case DT_FLAGS: - printFlags(Value, makeArrayRef(ElfDynamicDTFlags), OS); - break; + return FormatFlags(Value, makeArrayRef(ElfDynamicDTFlags)); case DT_FLAGS_1: - printFlags(Value, makeArrayRef(ElfDynamicDTFlags1), OS); - break; + return FormatFlags(Value, makeArrayRef(ElfDynamicDTFlags1)); default: - OS << format(ConvChar, Value); - break; + return FormatHexValue(Value); } } template <class ELFT> -std::string ELFDumper<ELFT>::getDynamicString(uint64_t Value) const { - if (DynamicStringTable.empty()) - return "<String table is empty or was not found>"; - if (Value < DynamicStringTable.size()) - return DynamicStringTable.data() + Value; - return Twine("<Invalid offset 0x" + utohexstr(Value) + ">").str(); +StringRef ELFDumper<ELFT>::getDynamicString(uint64_t Value) const { + if (DynamicStringTable.empty() && !DynamicStringTable.data()) { + reportUniqueWarning(createError("string table was not found")); + return "<?>"; + } + + auto WarnAndReturn = [this](const Twine &Msg, uint64_t Offset) { + reportUniqueWarning(createError("string table at offset 0x" + + Twine::utohexstr(Offset) + Msg)); + return "<?>"; + }; + + const uint64_t FileSize = ObjF->getELFFile()->getBufSize(); + const uint64_t Offset = + (const uint8_t *)DynamicStringTable.data() - ObjF->getELFFile()->base(); + if (DynamicStringTable.size() > FileSize - Offset) + return WarnAndReturn(" with size 0x" + + Twine::utohexstr(DynamicStringTable.size()) + + " goes past the end of the file (0x" + + Twine::utohexstr(FileSize) + ")", + Offset); + + if (Value >= DynamicStringTable.size()) + return WarnAndReturn( + ": unable to read the string at 0x" + Twine::utohexstr(Offset + Value) + + ": it goes past the end of the table (0x" + + Twine::utohexstr(Offset + DynamicStringTable.size()) + ")", + Offset); + + if (DynamicStringTable.back() != '\0') + return WarnAndReturn(": unable to read the string at 0x" + + Twine::utohexstr(Offset + Value) + + ": the string table is not null-terminated", + Offset); + + return DynamicStringTable.data() + Value; } template <class ELFT> void ELFDumper<ELFT>::printUnwindInfo() { @@ -2466,42 +2699,159 @@ template <class ELFT> void ELFDumper<ELFT>::printDynamicTable() { template <class ELFT> void ELFDumper<ELFT>::printNeededLibraries() { ListScope D(W, "NeededLibraries"); - std::vector<std::string> Libs; + std::vector<StringRef> Libs; for (const auto &Entry : dynamic_table()) if (Entry.d_tag == ELF::DT_NEEDED) Libs.push_back(getDynamicString(Entry.d_un.d_val)); - llvm::stable_sort(Libs); + llvm::sort(Libs); - for (const auto &L : Libs) + for (StringRef L : Libs) W.startLine() << L << "\n"; } +template <class ELFT> +static Error checkHashTable(const ELFFile<ELFT> *Obj, + const typename ELFT::Hash *H, + bool *IsHeaderValid = nullptr) { + auto MakeError = [&](uint64_t Off, const Twine &Msg = "") { + return createError("the hash table at offset 0x" + Twine::utohexstr(Off) + + " goes past the end of the file (0x" + + Twine::utohexstr(Obj->getBufSize()) + ")" + Msg); + }; + + // Each SHT_HASH section starts from two 32-bit fields: nbucket and nchain. + const unsigned HeaderSize = 2 * sizeof(typename ELFT::Word); + const uint64_t SecOffset = (const uint8_t *)H - Obj->base(); + + if (IsHeaderValid) + *IsHeaderValid = Obj->getBufSize() - SecOffset >= HeaderSize; + + if (Obj->getBufSize() - SecOffset < HeaderSize) + return MakeError(SecOffset); + + if (Obj->getBufSize() - SecOffset - HeaderSize < + ((uint64_t)H->nbucket + H->nchain) * sizeof(typename ELFT::Word)) + return MakeError(SecOffset, ", nbucket = " + Twine(H->nbucket) + + ", nchain = " + Twine(H->nchain)); + return Error::success(); +} + +template <class ELFT> +static Error checkGNUHashTable(const ELFFile<ELFT> *Obj, + const typename ELFT::GnuHash *GnuHashTable, + bool *IsHeaderValid = nullptr) { + const uint8_t *TableData = reinterpret_cast<const uint8_t *>(GnuHashTable); + assert(TableData >= Obj->base() && + TableData < Obj->base() + Obj->getBufSize() && + "GnuHashTable must always point to a location inside the file"); + + uint64_t TableOffset = TableData - Obj->base(); + if (IsHeaderValid) + *IsHeaderValid = TableOffset + /*Header size:*/ 16 < Obj->getBufSize(); + if (TableOffset + 16 + (uint64_t)GnuHashTable->nbuckets * 4 + + (uint64_t)GnuHashTable->maskwords * sizeof(typename ELFT::Off) >= + Obj->getBufSize()) + return createError("unable to dump the SHT_GNU_HASH " + "section at 0x" + + Twine::utohexstr(TableOffset) + + ": it goes past the end of the file"); + return Error::success(); +} + template <typename ELFT> void ELFDumper<ELFT>::printHashTable() { DictScope D(W, "HashTable"); if (!HashTable) return; - W.printNumber("Num Buckets", HashTable->nbucket); - W.printNumber("Num Chains", HashTable->nchain); + + bool IsHeaderValid; + Error Err = checkHashTable(ObjF->getELFFile(), HashTable, &IsHeaderValid); + if (IsHeaderValid) { + W.printNumber("Num Buckets", HashTable->nbucket); + W.printNumber("Num Chains", HashTable->nchain); + } + + if (Err) { + reportUniqueWarning(std::move(Err)); + return; + } + W.printList("Buckets", HashTable->buckets()); W.printList("Chains", HashTable->chains()); } -template <typename ELFT> void ELFDumper<ELFT>::printGnuHashTable() { +template <class ELFT> +static Expected<ArrayRef<typename ELFT::Word>> +getGnuHashTableChains(Optional<DynRegionInfo> DynSymRegion, + const typename ELFT::GnuHash *GnuHashTable) { + if (!DynSymRegion) + return createError("no dynamic symbol table found"); + + ArrayRef<typename ELFT::Sym> DynSymTable = + DynSymRegion->getAsArrayRef<typename ELFT::Sym>(); + size_t NumSyms = DynSymTable.size(); + if (!NumSyms) + return createError("the dynamic symbol table is empty"); + + if (GnuHashTable->symndx < NumSyms) + return GnuHashTable->values(NumSyms); + + // A normal empty GNU hash table section produced by linker might have + // symndx set to the number of dynamic symbols + 1 (for the zero symbol) + // and have dummy null values in the Bloom filter and in the buckets + // vector (or no values at all). It happens because the value of symndx is not + // important for dynamic loaders when the GNU hash table is empty. They just + // skip the whole object during symbol lookup. In such cases, the symndx value + // is irrelevant and we should not report a warning. + ArrayRef<typename ELFT::Word> Buckets = GnuHashTable->buckets(); + if (!llvm::all_of(Buckets, [](typename ELFT::Word V) { return V == 0; })) + return createError("the first hashed symbol index (" + + Twine(GnuHashTable->symndx) + + ") is larger than the number of dynamic symbols (" + + Twine(NumSyms) + ")"); + // There is no way to represent an array of (dynamic symbols count - symndx) + // length. + return ArrayRef<typename ELFT::Word>(); +} + +template <typename ELFT> +void ELFDumper<ELFT>::printGnuHashTable(const object::ObjectFile *Obj) { DictScope D(W, "GnuHashTable"); if (!GnuHashTable) return; - W.printNumber("Num Buckets", GnuHashTable->nbuckets); - W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx); - W.printNumber("Num Mask Words", GnuHashTable->maskwords); - W.printNumber("Shift Count", GnuHashTable->shift2); - W.printHexList("Bloom Filter", GnuHashTable->filter()); - W.printList("Buckets", GnuHashTable->buckets()); - Elf_Sym_Range Syms = dynamic_symbols(); - unsigned NumSyms = std::distance(Syms.begin(), Syms.end()); - if (!NumSyms) - reportError(createError("No dynamic symbol section"), ObjF->getFileName()); - W.printHexList("Values", GnuHashTable->values(NumSyms)); + + bool IsHeaderValid; + Error Err = + checkGNUHashTable<ELFT>(ObjF->getELFFile(), GnuHashTable, &IsHeaderValid); + if (IsHeaderValid) { + W.printNumber("Num Buckets", GnuHashTable->nbuckets); + W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx); + W.printNumber("Num Mask Words", GnuHashTable->maskwords); + W.printNumber("Shift Count", GnuHashTable->shift2); + } + + if (Err) { + reportUniqueWarning(std::move(Err)); + return; + } + + ArrayRef<typename ELFT::Off> BloomFilter = GnuHashTable->filter(); + W.printHexList("Bloom Filter", BloomFilter); + + ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets(); + W.printList("Buckets", Buckets); + + Expected<ArrayRef<Elf_Word>> Chains = + getGnuHashTableChains<ELFT>(DynSymRegion, GnuHashTable); + if (!Chains) { + reportUniqueWarning( + createError("unable to dump 'Values' for the SHT_GNU_HASH " + "section: " + + toString(Chains.takeError()))); + return; + } + + W.printHexList("Values", *Chains); } template <typename ELFT> void ELFDumper<ELFT>::printLoadName() { @@ -2512,6 +2862,7 @@ template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() { const ELFFile<ELFT> *Obj = ObjF->getELFFile(); switch (Obj->getHeader()->e_machine) { case EM_ARM: + case EM_RISCV: printAttributes(); break; case EM_MIPS: { @@ -2521,9 +2872,14 @@ template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() { MipsGOTParser<ELFT> Parser(Obj, ObjF->getFileName(), dynamic_table(), dynamic_symbols()); - if (Parser.hasGot()) + if (Error E = Parser.findGOT(dynamic_table(), dynamic_symbols())) + reportError(std::move(E), ObjF->getFileName()); + else if (!Parser.isGotEmpty()) ELFDumperStyle->printMipsGOT(Parser); - if (Parser.hasPlt()) + + if (Error E = Parser.findPLT(dynamic_table())) + reportError(std::move(E), ObjF->getFileName()); + else if (!Parser.isPltEmpty()) ELFDumperStyle->printMipsPLT(Parser); break; } @@ -2532,38 +2888,45 @@ template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() { } } -template <class ELFT> void ELFDumper<ELFT>::printAttributes() { - W.startLine() << "Attributes not implemented.\n"; -} - namespace { -template <> void ELFDumper<ELF32LE>::printAttributes() { - const ELFFile<ELF32LE> *Obj = ObjF->getELFFile(); - if (Obj->getHeader()->e_machine != EM_ARM) { +template <class ELFT> void ELFDumper<ELFT>::printAttributes() { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + if (!Obj->isLE()) { W.startLine() << "Attributes not implemented.\n"; return; } + const unsigned Machine = Obj->getHeader()->e_machine; + assert((Machine == EM_ARM || Machine == EM_RISCV) && + "Attributes not implemented."); + DictScope BA(W, "BuildAttributes"); - for (const ELFO::Elf_Shdr &Sec : - unwrapOrError(ObjF->getFileName(), Obj->sections())) { - if (Sec.sh_type != ELF::SHT_ARM_ATTRIBUTES) + for (const Elf_Shdr &Sec : cantFail(Obj->sections())) { + if (Sec.sh_type != ELF::SHT_ARM_ATTRIBUTES && + Sec.sh_type != ELF::SHT_RISCV_ATTRIBUTES) continue; ArrayRef<uint8_t> Contents = unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(&Sec)); - if (Contents[0] != ARMBuildAttrs::Format_Version) { - errs() << "unrecognised FormatVersion: 0x" - << Twine::utohexstr(Contents[0]) << '\n'; + if (Contents[0] != ELFAttrs::Format_Version) { + reportWarning(createError(Twine("unrecognised FormatVersion: 0x") + + Twine::utohexstr(Contents[0])), + ObjF->getFileName()); continue; } - W.printHex("FormatVersion", Contents[0]); if (Contents.size() == 1) continue; - ARMAttributeParser(&W).Parse(Contents, true); + // TODO: Delete the redundant FormatVersion check above. + if (Machine == EM_ARM) { + if (Error E = ARMAttributeParser(&W).parse(Contents, support::little)) + reportWarning(std::move(E), ObjF->getFileName()); + } else if (Machine == EM_RISCV) { + if (Error E = RISCVAttributeParser(&W).parse(Contents, support::little)) + reportWarning(std::move(E), ObjF->getFileName()); + } } } @@ -2578,9 +2941,11 @@ public: MipsGOTParser(const ELFO *Obj, StringRef FileName, Elf_Dyn_Range DynTable, Elf_Sym_Range DynSyms); + Error findGOT(Elf_Dyn_Range DynTable, Elf_Sym_Range DynSyms); + Error findPLT(Elf_Dyn_Range DynTable); - bool hasGot() const { return !GotEntries.empty(); } - bool hasPlt() const { return !PltEntries.empty(); } + bool isGotEmpty() const { return GotEntries.empty(); } + bool isPltEmpty() const { return PltEntries.empty(); } uint64_t getGp() const; @@ -2628,7 +2993,11 @@ MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, StringRef FileName, Elf_Sym_Range DynSyms) : IsStatic(DynTable.empty()), Obj(Obj), GotSec(nullptr), LocalNum(0), GlobalNum(0), PltSec(nullptr), PltRelSec(nullptr), PltSymTable(nullptr), - FileName(FileName) { + FileName(FileName) {} + +template <class ELFT> +Error MipsGOTParser<ELFT>::findGOT(Elf_Dyn_Range DynTable, + Elf_Sym_Range DynSyms) { // See "Global Offset Table" in Chapter 5 in the following document // for detailed GOT description. // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf @@ -2637,22 +3006,20 @@ MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, StringRef FileName, if (IsStatic) { GotSec = findSectionByName(*Obj, FileName, ".got"); if (!GotSec) - return; + return Error::success(); ArrayRef<uint8_t> Content = unwrapOrError(FileName, Obj->getSectionContents(GotSec)); GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()), Content.size() / sizeof(Entry)); LocalNum = GotEntries.size(); - return; + return Error::success(); } - // Lookup dynamic table tags which define GOT/PLT layouts. + // Lookup dynamic table tags which define the GOT layout. Optional<uint64_t> DtPltGot; Optional<uint64_t> DtLocalGotNum; Optional<uint64_t> DtGotSym; - Optional<uint64_t> DtMipsPltGot; - Optional<uint64_t> DtJmpRel; for (const auto &Entry : DynTable) { switch (Entry.getTag()) { case ELF::DT_PLTGOT: @@ -2664,6 +3031,49 @@ MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, StringRef FileName, case ELF::DT_MIPS_GOTSYM: DtGotSym = Entry.getVal(); break; + } + } + + if (!DtPltGot && !DtLocalGotNum && !DtGotSym) + return Error::success(); + + if (!DtPltGot) + return createError("cannot find PLTGOT dynamic tag"); + if (!DtLocalGotNum) + return createError("cannot find MIPS_LOCAL_GOTNO dynamic tag"); + if (!DtGotSym) + return createError("cannot find MIPS_GOTSYM dynamic tag"); + + size_t DynSymTotal = DynSyms.size(); + if (*DtGotSym > DynSymTotal) + return createError("DT_MIPS_GOTSYM value (" + Twine(*DtGotSym) + + ") exceeds the number of dynamic symbols (" + + Twine(DynSymTotal) + ")"); + + GotSec = findNotEmptySectionByAddress(Obj, FileName, *DtPltGot); + if (!GotSec) + return createError("there is no non-empty GOT section at 0x" + + Twine::utohexstr(*DtPltGot)); + + LocalNum = *DtLocalGotNum; + GlobalNum = DynSymTotal - *DtGotSym; + + ArrayRef<uint8_t> Content = + unwrapOrError(FileName, Obj->getSectionContents(GotSec)); + GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()), + Content.size() / sizeof(Entry)); + GotDynSyms = DynSyms.drop_front(*DtGotSym); + + return Error::success(); +} + +template <class ELFT> +Error MipsGOTParser<ELFT>::findPLT(Elf_Dyn_Range DynTable) { + // Lookup dynamic table tags which define the PLT layout. + Optional<uint64_t> DtMipsPltGot; + Optional<uint64_t> DtJmpRel; + for (const auto &Entry : DynTable) { + switch (Entry.getTag()) { case ELF::DT_MIPS_PLTGOT: DtMipsPltGot = Entry.getVal(); break; @@ -2673,63 +3083,56 @@ MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, StringRef FileName, } } - // Find dynamic GOT section. - if (DtPltGot || DtLocalGotNum || DtGotSym) { - if (!DtPltGot) - report_fatal_error("Cannot find PLTGOT dynamic table tag."); - if (!DtLocalGotNum) - report_fatal_error("Cannot find MIPS_LOCAL_GOTNO dynamic table tag."); - if (!DtGotSym) - report_fatal_error("Cannot find MIPS_GOTSYM dynamic table tag."); - - size_t DynSymTotal = DynSyms.size(); - if (*DtGotSym > DynSymTotal) - reportError( - createError("MIPS_GOTSYM exceeds a number of dynamic symbols"), - FileName); - - GotSec = findNotEmptySectionByAddress(Obj, FileName, *DtPltGot); - if (!GotSec) - reportError(createError("There is no not empty GOT section at 0x" + - Twine::utohexstr(*DtPltGot)), - FileName); - - LocalNum = *DtLocalGotNum; - GlobalNum = DynSymTotal - *DtGotSym; - - ArrayRef<uint8_t> Content = - unwrapOrError(FileName, Obj->getSectionContents(GotSec)); - GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()), - Content.size() / sizeof(Entry)); - GotDynSyms = DynSyms.drop_front(*DtGotSym); - } + if (!DtMipsPltGot && !DtJmpRel) + return Error::success(); // Find PLT section. - if (DtMipsPltGot || DtJmpRel) { - if (!DtMipsPltGot) - report_fatal_error("Cannot find MIPS_PLTGOT dynamic table tag."); - if (!DtJmpRel) - report_fatal_error("Cannot find JMPREL dynamic table tag."); - - PltSec = findNotEmptySectionByAddress(Obj, FileName, * DtMipsPltGot); - if (!PltSec) - report_fatal_error("There is no not empty PLTGOT section at 0x " + - Twine::utohexstr(*DtMipsPltGot)); - - PltRelSec = findNotEmptySectionByAddress(Obj, FileName, * DtJmpRel); - if (!PltRelSec) - report_fatal_error("There is no not empty RELPLT section at 0x" + - Twine::utohexstr(*DtJmpRel)); + if (!DtMipsPltGot) + return createError("cannot find MIPS_PLTGOT dynamic tag"); + if (!DtJmpRel) + return createError("cannot find JMPREL dynamic tag"); + + PltSec = findNotEmptySectionByAddress(Obj, FileName, *DtMipsPltGot); + if (!PltSec) + return createError("there is no non-empty PLTGOT section at 0x" + + Twine::utohexstr(*DtMipsPltGot)); + + PltRelSec = findNotEmptySectionByAddress(Obj, FileName, *DtJmpRel); + if (!PltRelSec) + return createError("there is no non-empty RELPLT section at 0x" + + Twine::utohexstr(*DtJmpRel)); + + if (Expected<ArrayRef<uint8_t>> PltContentOrErr = + Obj->getSectionContents(PltSec)) + PltEntries = + Entries(reinterpret_cast<const Entry *>(PltContentOrErr->data()), + PltContentOrErr->size() / sizeof(Entry)); + else + return createError("unable to read PLTGOT section content: " + + toString(PltContentOrErr.takeError())); - ArrayRef<uint8_t> PltContent = - unwrapOrError(FileName, Obj->getSectionContents(PltSec)); - PltEntries = Entries(reinterpret_cast<const Entry *>(PltContent.data()), - PltContent.size() / sizeof(Entry)); + if (Expected<const Elf_Shdr *> PltSymTableOrErr = + Obj->getSection(PltRelSec->sh_link)) { + PltSymTable = *PltSymTableOrErr; + } else { + unsigned SecNdx = PltRelSec - &cantFail(Obj->sections()).front(); + return createError("unable to get a symbol table linked to the RELPLT " + "section with index " + + Twine(SecNdx) + ": " + + toString(PltSymTableOrErr.takeError())); + } - PltSymTable = unwrapOrError(FileName, Obj->getSection(PltRelSec->sh_link)); - PltStrTable = - unwrapOrError(FileName, Obj->getStringTableForSymtab(*PltSymTable)); + if (Expected<StringRef> StrTabOrErr = + Obj->getStringTableForSymtab(*PltSymTable)) { + PltStrTable = *StrTabOrErr; + } else { + unsigned SecNdx = PltSymTable - &cantFail(Obj->sections()).front(); + return createError( + "unable to get a string table for the symbol table with index " + + Twine(SecNdx) + ": " + toString(StrTabOrErr.takeError())); } + + return Error::success(); } template <class ELFT> uint64_t MipsGOTParser<ELFT>::getGp() const { @@ -2977,7 +3380,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() { template <class ELFT> void ELFDumper<ELFT>::printStackMap() const { const ELFFile<ELFT> *Obj = ObjF->getELFFile(); const Elf_Shdr *StackMapSection = nullptr; - for (const auto &Sec : unwrapOrError(ObjF->getFileName(), Obj->sections())) { + for (const Elf_Shdr &Sec : cantFail(Obj->sections())) { StringRef Name = unwrapOrError(ObjF->getFileName(), Obj->getSectionName(&Sec)); if (Name == ".llvm_stackmaps") { @@ -3020,7 +3423,7 @@ static std::string getSectionHeadersNumString(const ELFFile<ELFT> *Obj, if (ElfHeader->e_shnum != 0) return to_string(ElfHeader->e_shnum); - ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(FileName, Obj->sections()); + ArrayRef<typename ELFT::Shdr> Arr = cantFail(Obj->sections()); if (Arr.empty()) return "0"; return "0 (" + to_string(Arr[0].sh_size) + ")"; @@ -3033,7 +3436,7 @@ static std::string getSectionHeaderTableIndexString(const ELFFile<ELFT> *Obj, if (ElfHeader->e_shstrndx != SHN_XINDEX) return to_string(ElfHeader->e_shstrndx); - ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(FileName, Obj->sections()); + ArrayRef<typename ELFT::Shdr> Arr = cantFail(Obj->sections()); if (Arr.empty()) return "65535 (corrupt: out of range)"; return to_string(ElfHeader->e_shstrndx) + " (" + to_string(Arr[0].sh_link) + @@ -3127,7 +3530,7 @@ std::vector<GroupSection> getGroups(const ELFFile<ELFT> *Obj, std::vector<GroupSection> Ret; uint64_t I = 0; - for (const Elf_Shdr &Sec : unwrapOrError(FileName, Obj->sections())) { + for (const Elf_Shdr &Sec : cantFail(Obj->sections())) { ++I; if (Sec.sh_type != ELF::SHT_GROUP) continue; @@ -3202,23 +3605,18 @@ 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) { - const Elf_Sym *Sym = - unwrapOrError(this->FileName, Obj->getRelocationSymbol(&R, SymTab)); - std::string TargetName; - if (Sym && Sym->getType() == ELF::STT_SECTION) { - const Elf_Shdr *Sec = unwrapOrError( - this->FileName, - Obj->getSection(Sym, SymTab, this->dumper()->getShndxTable())); - TargetName = unwrapOrError(this->FileName, Obj->getSectionName(Sec)); - } else if (Sym) { - StringRef StrTable = - unwrapOrError(this->FileName, Obj->getStringTableForSymtab(*SymTab)); - TargetName = this->dumper()->getFullSymbolName( - Sym, StrTable, SymTab->sh_type == SHT_DYNSYM /* IsDynamic */); - } - printRelocation(Obj, Sym, TargetName, R, IsRela); +void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, unsigned SecIndex, + const Elf_Shdr *SymTab, const Elf_Rela &R, + unsigned RelIndex, bool IsRela) { + Expected<std::pair<const typename ELFT::Sym *, std::string>> Target = + this->dumper()->getRelocationTarget(SymTab, R); + if (!Target) + this->reportUniqueWarning(createError( + "unable to print relocation " + Twine(RelIndex) + " in section " + + Twine(SecIndex) + ": " + toString(Target.takeError()))); + else + printRelocation(Obj, /*Sym=*/Target->first, /*Name=*/Target->second, R, + IsRela); } template <class ELFT> @@ -3237,10 +3635,10 @@ void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Sym *Sym, Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName); Fields[2].Str = RelocName.c_str(); - if (Sym && (!SymbolName.empty() || Sym->getValue() != 0)) + if (Sym) Fields[3].Str = to_string(format_hex_no_prefix(Sym->getValue(), Width)); - Fields[4].Str = SymbolName; + Fields[4].Str = std::string(SymbolName); for (const Field &F : Fields) printField(F); @@ -3283,7 +3681,7 @@ 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(this->FileName, Obj->sections())) { + for (const Elf_Shdr &Sec : cantFail(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 && Sec.sh_type != ELF::SHT_ANDROID_RELA && @@ -3316,6 +3714,9 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) { printRelocHeader(Sec.sh_type); const Elf_Shdr *SymTab = unwrapOrError(this->FileName, Obj->getSection(Sec.sh_link)); + unsigned SecNdx = &Sec - &cantFail(Obj->sections()).front(); + unsigned RelNdx = 0; + switch (Sec.sh_type) { case ELF::SHT_REL: for (const auto &R : unwrapOrError(this->FileName, Obj->rels(&Sec))) { @@ -3323,12 +3724,12 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) { Rela.r_offset = R.r_offset; Rela.r_info = R.r_info; Rela.r_addend = 0; - printRelocation(Obj, SymTab, Rela, false); + printRelocation(Obj, SecNdx, SymTab, Rela, ++RelNdx, false); } break; case ELF::SHT_RELA: for (const auto &R : unwrapOrError(this->FileName, Obj->relas(&Sec))) - printRelocation(Obj, SymTab, R, true); + printRelocation(Obj, SecNdx, SymTab, R, ++RelNdx, true); break; case ELF::SHT_RELR: case ELF::SHT_ANDROID_RELR: @@ -3338,12 +3739,13 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) { << "\n"; else for (const auto &R : RelrRelas) - printRelocation(Obj, SymTab, R, false); + printRelocation(Obj, SecNdx, SymTab, R, ++RelNdx, false); break; case ELF::SHT_ANDROID_REL: case ELF::SHT_ANDROID_RELA: for (const auto &R : AndroidRelas) - printRelocation(Obj, SymTab, R, Sec.sh_type == ELF::SHT_ANDROID_RELA); + printRelocation(Obj, SecNdx, SymTab, R, ++RelNdx, + Sec.sh_type == ELF::SHT_ANDROID_RELA); break; } } @@ -3402,6 +3804,11 @@ static std::string getSectionTypeString(unsigned Arch, unsigned Type) { return "MIPS_ABIFLAGS"; } break; + case EM_RISCV: + switch (Type) { + case SHT_RISCV_ATTRIBUTES: + return "RISCV_ATTRIBUTES"; + } } switch (Type) { case SHT_NULL: @@ -3500,7 +3907,7 @@ static void printSectionDescription(formatted_raw_ostream &OS, template <class ELFT> void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { unsigned Bias = ELFT::Is64Bits ? 0 : 8; - ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections()); + ArrayRef<Elf_Shdr> Sections = cantFail(Obj->sections()); OS << "There are " << to_string(Sections.size()) << " section headers, starting at offset " << "0x" << to_hexString(Obj->getHeader()->e_shoff, false) << ":\n\n"; @@ -3514,12 +3921,21 @@ void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { printField(F); OS << "\n"; - const ELFObjectFile<ELFT> *ElfObj = this->dumper()->getElfObject(); + StringRef SecStrTable; + if (Expected<StringRef> SecStrTableOrErr = + Obj->getSectionStringTable(Sections, this->dumper()->WarningHandler)) + SecStrTable = *SecStrTableOrErr; + else + this->reportUniqueWarning(SecStrTableOrErr.takeError()); + size_t SectionIndex = 0; for (const Elf_Shdr &Sec : Sections) { Fields[0].Str = to_string(SectionIndex); - Fields[1].Str = unwrapOrError<StringRef>( - ElfObj->getFileName(), Obj->getSectionName(&Sec, this->WarningHandler)); + if (SecStrTable.empty()) + Fields[1].Str = "<no-strings>"; + else + Fields[1].Str = std::string(unwrapOrError<StringRef>( + this->FileName, Obj->getSectionName(&Sec, SecStrTable))); Fields[2].Str = getSectionTypeString(Obj->getHeader()->e_machine, Sec.sh_type); Fields[3].Str = @@ -3555,10 +3971,10 @@ void GNUStyle<ELFT>::printSymtabMessage(const ELFO *Obj, StringRef Name, size_t Entries, bool NonVisibilityBitsUsed) { if (!Name.empty()) - OS << "\nSymbol table '" << Name << "' contains " << Entries - << " entries:\n"; + OS << "\nSymbol table '" << Name << "'"; else - OS << "\n Symbol table for image:\n"; + OS << "\nSymbol table for image"; + OS << " contains " << Entries << " entries:\n"; if (ELFT::Is64Bits) OS << " Num: Value Size Type Bind Vis"; @@ -3616,8 +4032,9 @@ std::string GNUStyle<ELFT>::getSymbolSectionNdx(const ELFO *Obj, template <class ELFT> void GNUStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, - const Elf_Sym *FirstSym, StringRef StrTable, - bool IsDynamic, bool NonVisibilityBitsUsed) { + const Elf_Sym *FirstSym, + Optional<StringRef> StrTable, bool IsDynamic, + bool NonVisibilityBitsUsed) { static int Idx = 0; static bool Dynamic = true; @@ -3707,67 +4124,110 @@ void GNUStyle<ELFT>::printSymbols(const ELFO *Obj, bool PrintSymbols, this->dumper()->printSymbolsHelper(false); } -template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) { - if (this->dumper()->getDynamicStringTable().empty()) +template <class ELFT> +void GNUStyle<ELFT>::printHashTableSymbols(const ELFO *Obj, + const Elf_Hash &SysVHash) { + StringRef StringTable = this->dumper()->getDynamicStringTable(); + if (StringTable.empty()) return; - auto StringTable = this->dumper()->getDynamicStringTable(); - auto DynSyms = this->dumper()->dynamic_symbols(); - // Try printing .hash - 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"; - else - OS << " Num Buc: Value Size Type Bind Vis Ndx Name"; - OS << "\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"; - auto Buckets = SysVHash->buckets(); - auto Chains = SysVHash->chains(); - for (uint32_t Buc = 0; Buc < SysVHash->nbucket; Buc++) { - if (Buckets[Buc] == ELF::STN_UNDEF) - continue; - std::vector<bool> Visited(SysVHash->nchain); - for (uint32_t Ch = Buckets[Buc]; Ch < SysVHash->nchain; Ch = Chains[Ch]) { - if (Ch == ELF::STN_UNDEF) - break; + Elf_Sym_Range DynSyms = this->dumper()->dynamic_symbols(); + const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0]; + if (!FirstSym) { + Optional<DynRegionInfo> DynSymRegion = this->dumper()->getDynSymRegion(); + this->reportUniqueWarning( + createError(Twine("unable to print symbols for the .hash table: the " + "dynamic symbol table ") + + (DynSymRegion ? "is empty" : "was not found"))); + return; + } - if (Visited[Ch]) { - reportWarning( - createError(".hash section is invalid: bucket " + Twine(Ch) + - ": a cycle was detected in the linked chain"), - this->FileName); - break; - } + auto Buckets = SysVHash.buckets(); + auto Chains = SysVHash.chains(); + for (uint32_t Buc = 0; Buc < SysVHash.nbucket; Buc++) { + if (Buckets[Buc] == ELF::STN_UNDEF) + continue; + std::vector<bool> Visited(SysVHash.nchain); + 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); - Visited[Ch] = true; + if (Visited[Ch]) { + reportWarning(createError(".hash section is invalid: bucket " + + Twine(Ch) + + ": a cycle was detected in the linked chain"), + this->FileName); + break; } + + printHashedSymbol(Obj, FirstSym, Ch, StringTable, Buc); + Visited[Ch] = true; + } + } +} + +template <class ELFT> +void GNUStyle<ELFT>::printGnuHashTableSymbols(const ELFO *Obj, + const Elf_GnuHash &GnuHash) { + StringRef StringTable = this->dumper()->getDynamicStringTable(); + if (StringTable.empty()) + return; + + Elf_Sym_Range DynSyms = this->dumper()->dynamic_symbols(); + const Elf_Sym *FirstSym = DynSyms.empty() ? nullptr : &DynSyms[0]; + if (!FirstSym) { + Optional<DynRegionInfo> DynSymRegion = this->dumper()->getDynSymRegion(); + this->reportUniqueWarning(createError( + Twine("unable to print symbols for the .gnu.hash table: the " + "dynamic symbol table ") + + (DynSymRegion ? "is empty" : "was not found"))); + return; + } + + ArrayRef<Elf_Word> Buckets = GnuHash.buckets(); + for (uint32_t Buc = 0; Buc < GnuHash.nbuckets; Buc++) { + if (Buckets[Buc] == ELF::STN_UNDEF) + continue; + uint32_t Index = Buckets[Buc]; + uint32_t GnuHashable = Index - GnuHash.symndx; + // Print whole chain + while (true) { + printHashedSymbol(Obj, FirstSym, Index++, StringTable, Buc); + // Chain ends at symbol with stopper bit + if ((GnuHash.values(DynSyms.size())[GnuHashable++] & 1) == 1) + break; } } +} + +template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) { + if (const Elf_Hash *SysVHash = this->dumper()->getHashTable()) { + OS << "\n Symbol table of .hash for image:\n"; + if (Error E = checkHashTable<ELFT>(Obj, SysVHash)) + this->reportUniqueWarning(std::move(E)); + else + printHashTableSymbols(Obj, *SysVHash); + } - // Try printing .gnu.hash - if (auto GnuHash = this->dumper()->getGnuHashTable()) { + // Try printing the .gnu.hash table. + if (const Elf_GnuHash *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"; - auto Buckets = GnuHash->buckets(); - for (uint32_t Buc = 0; Buc < GnuHash->nbuckets; Buc++) { - if (Buckets[Buc] == ELF::STN_UNDEF) - continue; - uint32_t Index = Buckets[Buc]; - uint32_t GnuHashable = Index - GnuHash->symndx; - // Print whole chain - while (true) { - printHashedSymbol(Obj, &DynSyms[0], Index++, StringTable, Buc); - // Chain ends at symbol with stopper bit - if ((GnuHash->values(DynSyms.size())[GnuHashable++] & 1) == 1) - break; - } - } + + if (Error E = checkGNUHashTable<ELFT>(Obj, GnuHash)) + this->reportUniqueWarning(std::move(E)); + else + printGnuHashTableSymbols(Obj, *GnuHash); } } @@ -3779,63 +4239,76 @@ static inline std::string printPhdrFlags(unsigned Flag) { return Str; } -// SHF_TLS sections are only in PT_TLS, PT_LOAD or PT_GNU_RELRO -// PT_TLS must only have SHF_TLS sections template <class ELFT> -bool GNUStyle<ELFT>::checkTLSSections(const Elf_Phdr &Phdr, - const Elf_Shdr &Sec) { - return (((Sec.sh_flags & ELF::SHF_TLS) && - ((Phdr.p_type == ELF::PT_TLS) || (Phdr.p_type == ELF::PT_LOAD) || - (Phdr.p_type == ELF::PT_GNU_RELRO))) || - (!(Sec.sh_flags & ELF::SHF_TLS) && Phdr.p_type != ELF::PT_TLS)); +static bool checkTLSSections(const typename ELFT::Phdr &Phdr, + const typename ELFT::Shdr &Sec) { + if (Sec.sh_flags & ELF::SHF_TLS) { + // .tbss must only be shown in the PT_TLS segment. + if (Sec.sh_type == ELF::SHT_NOBITS) + return Phdr.p_type == ELF::PT_TLS; + + // SHF_TLS sections are only shown in PT_TLS, PT_LOAD or PT_GNU_RELRO + // segments. + return (Phdr.p_type == ELF::PT_TLS) || (Phdr.p_type == ELF::PT_LOAD) || + (Phdr.p_type == ELF::PT_GNU_RELRO); + } + + // PT_TLS must only have SHF_TLS sections. + return Phdr.p_type != ELF::PT_TLS; } -// Non-SHT_NOBITS must have its offset inside the segment -// Only non-zero section can be at end of segment template <class ELFT> -bool GNUStyle<ELFT>::checkoffsets(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { +static bool checkOffsets(const typename ELFT::Phdr &Phdr, + const typename ELFT::Shdr &Sec) { + // SHT_NOBITS sections don't need to have an offset inside the segment. if (Sec.sh_type == ELF::SHT_NOBITS) return true; - bool IsSpecial = - (Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0); - // .tbss is special, it only has memory in PT_TLS and has NOBITS properties - auto SectionSize = - (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)); - return false; -} - -// SHF_ALLOC must have VMA inside segment -// Only non-zero section can be at end of segment + + if (Sec.sh_offset < Phdr.p_offset) + return false; + + // Only non-empty sections can be at the end of a segment. + if (Sec.sh_size == 0) + return (Sec.sh_offset + 1 <= Phdr.p_offset + Phdr.p_filesz); + return Sec.sh_offset + Sec.sh_size <= Phdr.p_offset + Phdr.p_filesz; +} + +// Check that an allocatable section belongs to a virtual address +// space of a segment. template <class ELFT> -bool GNUStyle<ELFT>::checkVMA(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { +static bool checkVMA(const typename ELFT::Phdr &Phdr, + const typename ELFT::Shdr &Sec) { if (!(Sec.sh_flags & ELF::SHF_ALLOC)) return true; - bool IsSpecial = + + if (Sec.sh_addr < Phdr.p_vaddr) + return false; + + bool IsTbss = (Sec.sh_type == ELF::SHT_NOBITS) && ((Sec.sh_flags & ELF::SHF_TLS) != 0); - // .tbss is special, it only has memory in PT_TLS and has NOBITS properties - auto SectionSize = - (IsSpecial && Phdr.p_type != ELF::PT_TLS) ? 0 : Sec.sh_size; - if (Sec.sh_addr >= Phdr.p_vaddr) - return ((Sec.sh_addr + SectionSize <= Phdr.p_vaddr + Phdr.p_memsz) && - (Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz)); - return false; + // .tbss is special, it only has memory in PT_TLS and has NOBITS properties. + bool IsTbssInNonTLS = IsTbss && Phdr.p_type != ELF::PT_TLS; + // Only non-empty sections can be at the end of a segment. + if (Sec.sh_size == 0 || IsTbssInNonTLS) + return Sec.sh_addr + 1 <= Phdr.p_vaddr + Phdr.p_memsz; + return Sec.sh_addr + Sec.sh_size <= Phdr.p_vaddr + Phdr.p_memsz; } -// No section with zero size must be at start or end of PT_DYNAMIC template <class ELFT> -bool GNUStyle<ELFT>::checkPTDynamic(const Elf_Phdr &Phdr, const Elf_Shdr &Sec) { - if (Phdr.p_type != ELF::PT_DYNAMIC || Sec.sh_size != 0 || Phdr.p_memsz == 0) +static bool checkPTDynamic(const typename ELFT::Phdr &Phdr, + const typename ELFT::Shdr &Sec) { + if (Phdr.p_type != ELF::PT_DYNAMIC || Phdr.p_memsz == 0 || Sec.sh_size != 0) return true; - // Is section within the phdr both based on offset and VMA ? - return ((Sec.sh_type == ELF::SHT_NOBITS) || - (Sec.sh_offset > Phdr.p_offset && - Sec.sh_offset < Phdr.p_offset + Phdr.p_filesz)) && - (!(Sec.sh_flags & ELF::SHF_ALLOC) || - (Sec.sh_addr > Phdr.p_vaddr && Sec.sh_addr < Phdr.p_memsz)); + + // We get here when we have an empty section. Only non-empty sections can be + // at the start or at the end of PT_DYNAMIC. + // Is section within the phdr both based on offset and VMA? + bool CheckOffset = (Sec.sh_type == ELF::SHT_NOBITS) || + (Sec.sh_offset > Phdr.p_offset && + Sec.sh_offset < Phdr.p_offset + Phdr.p_filesz); + bool CheckVA = !(Sec.sh_flags & ELF::SHF_ALLOC) || + (Sec.sh_addr > Phdr.p_vaddr && Sec.sh_addr < Phdr.p_memsz); + return CheckOffset && CheckVA; } template <class ELFT> @@ -3872,8 +4345,15 @@ void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { unsigned Width = ELFT::Is64Bits ? 18 : 10; unsigned SizeWidth = ELFT::Is64Bits ? 8 : 7; - for (const auto &Phdr : - unwrapOrError(this->FileName, Obj->program_headers())) { + + Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers(); + if (!PhdrsOrErr) { + this->reportUniqueWarning(createError("unable to dump program headers: " + + toString(PhdrsOrErr.takeError()))); + return; + } + + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { 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)); @@ -3885,8 +4365,31 @@ void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { for (auto Field : Fields) printField(Field); if (Phdr.p_type == ELF::PT_INTERP) { - OS << "\n [Requesting program interpreter: "; - OS << reinterpret_cast<const char *>(Obj->base()) + Phdr.p_offset << "]"; + OS << "\n"; + auto ReportBadInterp = [&](const Twine &Msg) { + reportWarning( + createError("unable to read program interpreter name at offset 0x" + + Twine::utohexstr(Phdr.p_offset) + ": " + Msg), + this->FileName); + }; + + if (Phdr.p_offset >= Obj->getBufSize()) { + ReportBadInterp("it goes past the end of the file (0x" + + Twine::utohexstr(Obj->getBufSize()) + ")"); + continue; + } + + const char *Data = + reinterpret_cast<const char *>(Obj->base()) + Phdr.p_offset; + size_t MaxSize = Obj->getBufSize() - Phdr.p_offset; + size_t Len = strnlen(Data, MaxSize); + if (Len == MaxSize) { + ReportBadInterp("it is not null-terminated"); + continue; + } + + OS << " [Requesting program interpreter: "; + OS << StringRef(Data, Len) << "]"; } OS << "\n"; } @@ -3897,21 +4400,28 @@ 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(this->FileName, Obj->program_headers())) { + + Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers(); + if (!PhdrsOrErr) { + this->reportUniqueWarning(createError( + "can't read program headers to build section to segment mapping: " + + toString(PhdrsOrErr.takeError()))); + return; + } + + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { std::string Sections; OS << format(" %2.2d ", Phnum++); - for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) { - // Check if each section is in a segment and then print mapping. + // Check if each section is in a segment and then print mapping. + for (const Elf_Shdr &Sec : cantFail(Obj->sections())) { + if (Sec.sh_type == ELF::SHT_NULL) + continue; + // readelf additionally makes sure it does not print zero sized sections // at end of segments and for PT_DYNAMIC both start and end of section // .tbss must only be shown in PT_TLS section. - bool TbssInNonTLS = (Sec.sh_type == ELF::SHT_NOBITS) && - ((Sec.sh_flags & ELF::SHF_TLS) != 0) && - 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)) { + if (checkTLSSections<ELFT>(Phdr, Sec) && checkOffsets<ELFT>(Phdr, Sec) && + checkVMA<ELFT>(Phdr, Sec) && checkPTDynamic<ELFT>(Phdr, Sec)) { Sections += unwrapOrError(this->FileName, Obj->getSectionName(&Sec)).str() + " "; @@ -3924,7 +4434,7 @@ void GNUStyle<ELFT>::printSectionMapping(const ELFO *Obj) { // Display sections that do not belong to a segment. std::string Sections; - for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) { + for (const Elf_Shdr &Sec : cantFail(Obj->sections())) { if (BelongsToSegment.find(&Sec) == BelongsToSegment.end()) Sections += unwrapOrError(this->FileName, Obj->getSectionName(&Sec)).str() + ' '; @@ -3946,21 +4456,35 @@ RelSymbol<ELFT> getSymbolForReloc(const ELFFile<ELFT> *Obj, StringRef FileName, const ELFDumper<ELFT> *Dumper, const typename ELFT::Rela &Reloc) { uint32_t SymIndex = Reloc.getSymbol(Obj->isMips64EL()); - const typename ELFT::Sym *Sym = Dumper->dynamic_symbols().begin() + SymIndex; - Expected<StringRef> ErrOrName = Sym->getName(Dumper->getDynamicStringTable()); - - std::string Name; - if (ErrOrName) { - Name = maybeDemangle(*ErrOrName); - } else { + auto WarnAndReturn = [&](const typename ELFT::Sym *Sym, + const Twine &Reason) -> RelSymbol<ELFT> { reportWarning( createError("unable to get name of the dynamic symbol with index " + - Twine(SymIndex) + ": " + toString(ErrOrName.takeError())), + Twine(SymIndex) + ": " + Reason), FileName); - Name = "<corrupt>"; - } + return {Sym, "<corrupt>"}; + }; - return {Sym, std::move(Name)}; + ArrayRef<typename ELFT::Sym> Symbols = Dumper->dynamic_symbols(); + const typename ELFT::Sym *FirstSym = Symbols.begin(); + if (!FirstSym) + return WarnAndReturn(nullptr, "no dynamic symbol table found"); + + // We might have an object without a section header. In this case the size of + // Symbols is zero, because there is no way to know the size of the dynamic + // table. We should allow this case and not print a warning. + if (!Symbols.empty() && SymIndex >= Symbols.size()) + return WarnAndReturn( + nullptr, + "index is greater than or equal to the number of dynamic symbols (" + + Twine(Symbols.size()) + ")"); + + const typename ELFT::Sym *Sym = FirstSym + SymIndex; + Expected<StringRef> ErrOrName = Sym->getName(Dumper->getDynamicStringTable()); + if (!ErrOrName) + return WarnAndReturn(Sym, toString(ErrOrName.takeError())); + + return {Sym == FirstSym ? nullptr : Sym, maybeDemangle(*ErrOrName)}; } } // namespace @@ -3971,6 +4495,15 @@ void GNUStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela R, printRelocation(Obj, S.Sym, S.Name, R, IsRela); } +template <class ELFT> +static size_t getMaxDynamicTagSize(const ELFFile<ELFT> *Obj, + typename ELFT::DynRange Tags) { + size_t Max = 0; + for (const typename ELFT::Dyn &Dyn : Tags) + Max = std::max(Max, Obj->getDynamicTagAsString(Dyn.d_tag).size()); + return Max; +} + template <class ELFT> void GNUStyle<ELFT>::printDynamic(const ELFO *Obj) { Elf_Dyn_Range Table = this->dumper()->dynamic_table(); if (Table.empty()) @@ -3985,19 +4518,22 @@ template <class ELFT> void GNUStyle<ELFT>::printDynamic(const ELFO *Obj) { 1) << " contains " << Table.size() << " entries:\n"; - bool Is64 = ELFT::Is64Bits; - if (Is64) - OS << " Tag Type Name/Value\n"; - else - OS << " Tag Type Name/Value\n"; + // The type name is surrounded with round brackets, hence add 2. + size_t MaxTagSize = getMaxDynamicTagSize(Obj, Table) + 2; + // The "Name/Value" column should be indented from the "Type" column by N + // spaces, where N = MaxTagSize - length of "Type" (4) + trailing + // space (1) = 3. + OS << " Tag" + std::string(ELFT::Is64Bits ? 16 : 8, ' ') + "Type" + << std::string(MaxTagSize - 3, ' ') << "Name/Value\n"; + + std::string ValueFmt = " %-" + std::to_string(MaxTagSize) + "s "; for (auto Entry : Table) { uintX_t Tag = Entry.getTag(); - std::string TypeString = + std::string Type = std::string("(") + Obj->getDynamicTagAsString(Tag).c_str() + ")"; - OS << " " << format_hex(Tag, Is64 ? 18 : 10) - << format(" %-20s ", TypeString.c_str()); - this->dumper()->printDynamicEntry(OS, Tag, Entry.getVal()); - OS << "\n"; + std::string Value = this->dumper()->getDynamicEntry(Tag, Entry.getVal()); + OS << " " << format_hex(Tag, ELFT::Is64Bits ? 18 : 10) + << format(ValueFmt.c_str(), Type.c_str()) << Value << "\n"; } } @@ -4052,19 +4588,20 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { Obj->base(), 1) << " contains " << DynPLTRelRegion.Size << " bytes:\n"; - } - if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) { - printRelocHeader(ELF::SHT_RELA); - for (const Elf_Rela &Rela : DynPLTRelRegion.getAsArrayRef<Elf_Rela>()) - printDynamicRelocation(Obj, Rela, true); - } else { - printRelocHeader(ELF::SHT_REL); - for (const Elf_Rel &Rel : DynPLTRelRegion.getAsArrayRef<Elf_Rel>()) { - Elf_Rela Rela; - Rela.r_offset = Rel.r_offset; - Rela.r_info = Rel.r_info; - Rela.r_addend = 0; - printDynamicRelocation(Obj, Rela, false); + + if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) { + printRelocHeader(ELF::SHT_RELA); + for (const Elf_Rela &Rela : DynPLTRelRegion.getAsArrayRef<Elf_Rela>()) + printDynamicRelocation(Obj, Rela, true); + } else { + printRelocHeader(ELF::SHT_REL); + for (const Elf_Rel &Rel : DynPLTRelRegion.getAsArrayRef<Elf_Rel>()) { + Elf_Rela Rela; + Rela.r_offset = Rel.r_offset; + Rela.r_info = Rel.r_info; + Rela.r_addend = 0; + printDynamicRelocation(Obj, Rela, false); + } } } } @@ -4231,116 +4768,137 @@ void GNUStyle<ELFT>::printVersionDependencySection(const ELFFile<ELFT> *Obj, 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) { - // Print histogram for .hash section - if (const Elf_Hash *HashTable = this->dumper()->getHashTable()) { - size_t NBucket = HashTable->nbucket; - size_t NChain = HashTable->nchain; - ArrayRef<Elf_Word> Buckets = HashTable->buckets(); - ArrayRef<Elf_Word> Chains = HashTable->chains(); - size_t TotalSyms = 0; - // If hash table is correct, we have at least chains with 0 length - size_t MaxChain = 1; - size_t CumulativeNonZero = 0; - - if (NChain == 0 || NBucket == 0) - return; +void GNUStyle<ELFT>::printHashHistogram(const Elf_Hash &HashTable) { + size_t NBucket = HashTable.nbucket; + size_t NChain = HashTable.nchain; + ArrayRef<Elf_Word> Buckets = HashTable.buckets(); + ArrayRef<Elf_Word> Chains = HashTable.chains(); + size_t TotalSyms = 0; + // If hash table is correct, we have at least chains with 0 length + size_t MaxChain = 1; + size_t CumulativeNonZero = 0; + + if (NChain == 0 || NBucket == 0) + return; - std::vector<size_t> ChainLen(NBucket, 0); - // Go over all buckets and and note chain lengths of each bucket (total - // unique chain lengths). - for (size_t B = 0; B < NBucket; B++) { - std::vector<bool> Visited(NChain); - for (size_t C = Buckets[B]; C < NChain; C = Chains[C]) { - if (C == ELF::STN_UNDEF) - break; - if (Visited[C]) { - reportWarning( - createError(".hash section is invalid: bucket " + Twine(C) + - ": a cycle was detected in the linked chain"), - this->FileName); - break; - } - Visited[C] = true; - if (MaxChain <= ++ChainLen[B]) - MaxChain++; + std::vector<size_t> ChainLen(NBucket, 0); + // Go over all buckets and and note chain lengths of each bucket (total + // unique chain lengths). + for (size_t B = 0; B < NBucket; B++) { + std::vector<bool> Visited(NChain); + for (size_t C = Buckets[B]; C < NChain; C = Chains[C]) { + if (C == ELF::STN_UNDEF) + break; + if (Visited[C]) { + reportWarning(createError(".hash section is invalid: bucket " + + Twine(C) + + ": a cycle was detected in the linked chain"), + this->FileName); + break; } - TotalSyms += ChainLen[B]; + Visited[C] = true; + if (MaxChain <= ++ChainLen[B]) + MaxChain++; } + TotalSyms += ChainLen[B]; + } - if (!TotalSyms) - return; + if (!TotalSyms) + return; - std::vector<size_t> Count(MaxChain, 0) ; - // Count how long is the chain for each bucket - for (size_t B = 0; B < NBucket; B++) - ++Count[ChainLen[B]]; - // Print Number of buckets with each chain lengths and their cumulative - // coverage of the symbols - OS << "Histogram for bucket list length (total of " << NBucket - << " buckets)\n" - << " Length Number % of total Coverage\n"; - for (size_t I = 0; I < MaxChain; I++) { - CumulativeNonZero += Count[I] * I; - OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I], - (Count[I] * 100.0) / NBucket, - (CumulativeNonZero * 100.0) / TotalSyms); - } + std::vector<size_t> Count(MaxChain, 0); + // Count how long is the chain for each bucket + for (size_t B = 0; B < NBucket; B++) + ++Count[ChainLen[B]]; + // Print Number of buckets with each chain lengths and their cumulative + // coverage of the symbols + OS << "Histogram for bucket list length (total of " << NBucket + << " buckets)\n" + << " Length Number % of total Coverage\n"; + for (size_t I = 0; I < MaxChain; I++) { + CumulativeNonZero += Count[I] * I; + OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I], + (Count[I] * 100.0) / NBucket, + (CumulativeNonZero * 100.0) / TotalSyms); } +} - // Print histogram for .gnu.hash section - 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(); - if (!NumSyms) - return; - ArrayRef<Elf_Word> Chains = GnuHashTable->values(NumSyms); - size_t Symndx = GnuHashTable->symndx; - size_t TotalSyms = 0; - size_t MaxChain = 1; - size_t CumulativeNonZero = 0; +template <class ELFT> +void GNUStyle<ELFT>::printGnuHashHistogram(const Elf_GnuHash &GnuHashTable) { + Expected<ArrayRef<Elf_Word>> ChainsOrErr = getGnuHashTableChains<ELFT>( + this->dumper()->getDynSymRegion(), &GnuHashTable); + if (!ChainsOrErr) { + this->reportUniqueWarning( + createError("unable to print the GNU hash table histogram: " + + toString(ChainsOrErr.takeError()))); + return; + } - if (Chains.empty() || NBucket == 0) - return; + ArrayRef<Elf_Word> Chains = *ChainsOrErr; + size_t Symndx = GnuHashTable.symndx; + size_t TotalSyms = 0; + size_t MaxChain = 1; + size_t CumulativeNonZero = 0; - std::vector<size_t> ChainLen(NBucket, 0); + size_t NBucket = GnuHashTable.nbuckets; + if (Chains.empty() || NBucket == 0) + return; - for (size_t B = 0; B < NBucket; B++) { - if (!Buckets[B]) - continue; - size_t Len = 1; - for (size_t C = Buckets[B] - Symndx; - C < Chains.size() && (Chains[C] & 1) == 0; C++) - if (MaxChain < ++Len) - MaxChain++; - ChainLen[B] = Len; - TotalSyms += Len; - } - MaxChain++; + ArrayRef<Elf_Word> Buckets = GnuHashTable.buckets(); + std::vector<size_t> ChainLen(NBucket, 0); + for (size_t B = 0; B < NBucket; B++) { + if (!Buckets[B]) + continue; + size_t Len = 1; + for (size_t C = Buckets[B] - Symndx; + C < Chains.size() && (Chains[C] & 1) == 0; C++) + if (MaxChain < ++Len) + MaxChain++; + ChainLen[B] = Len; + TotalSyms += Len; + } + MaxChain++; - if (!TotalSyms) - return; + if (!TotalSyms) + return; - std::vector<size_t> Count(MaxChain, 0) ; - for (size_t B = 0; B < NBucket; B++) - ++Count[ChainLen[B]]; - // Print Number of buckets with each chain lengths and their cumulative - // coverage of the symbols - OS << "Histogram for `.gnu.hash' bucket list length (total of " << NBucket - << " buckets)\n" - << " Length Number % of total Coverage\n"; - for (size_t I = 0; I <MaxChain; I++) { - CumulativeNonZero += Count[I] * I; - OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I], - (Count[I] * 100.0) / NBucket, - (CumulativeNonZero * 100.0) / TotalSyms); - } + std::vector<size_t> Count(MaxChain, 0); + for (size_t B = 0; B < NBucket; B++) + ++Count[ChainLen[B]]; + // Print Number of buckets with each chain lengths and their cumulative + // coverage of the symbols + OS << "Histogram for `.gnu.hash' bucket list length (total of " << NBucket + << " buckets)\n" + << " Length Number % of total Coverage\n"; + for (size_t I = 0; I < MaxChain; I++) { + CumulativeNonZero += Count[I] * I; + OS << format("%7lu %-10lu (%5.1f%%) %5.1f%%\n", I, Count[I], + (Count[I] * 100.0) / NBucket, + (CumulativeNonZero * 100.0) / TotalSyms); + } +} + +// Hash histogram shows statistics of how efficient the hash was for the +// dynamic symbol table. The table shows the number of hash buckets for +// different lengths of chains as an absolute number and percentage of the total +// buckets, and the cumulative coverage of symbols for each set of buckets. +template <class ELFT> +void GNUStyle<ELFT>::printHashHistograms(const ELFFile<ELFT> *Obj) { + // Print histogram for the .hash section. + if (const Elf_Hash *HashTable = this->dumper()->getHashTable()) { + if (Error E = checkHashTable<ELFT>(Obj, HashTable)) + this->reportUniqueWarning(std::move(E)); + else + printHashHistogram(*HashTable); + } + + // Print histogram for the .gnu.hash section. + if (const Elf_GnuHash *GnuHashTable = this->dumper()->getGnuHashTable()) { + if (Error E = checkGNUHashTable<ELFT>(Obj, GnuHashTable)) + this->reportUniqueWarning(std::move(E)); + else + printGnuHashHistogram(*GnuHashTable); } } @@ -4713,7 +5271,7 @@ template <typename ELFT> static GNUAbiTag getGNUAbiTag(ArrayRef<uint8_t> Desc) { std::string str; raw_string_ostream ABI(str); ABI << Major << "." << Minor << "." << Patch; - return {OSName, ABI.str(), /*IsValid=*/true}; + return {std::string(OSName), ABI.str(), /*IsValid=*/true}; } static std::string getGNUBuildId(ArrayRef<uint8_t> Desc) { @@ -4883,11 +5441,18 @@ static void printCoreNote(raw_ostream &OS, const CoreNote &Note) { template <class ELFT> void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { - auto PrintHeader = [&](const typename ELFT::Off Offset, + auto PrintHeader = [&](Optional<StringRef> SecName, + const typename ELFT::Off Offset, const typename ELFT::Addr Size) { - OS << "Displaying notes found at file offset " << format_hex(Offset, 10) - << " with length " << format_hex(Size, 10) << ":\n" - << " Owner Data size \tDescription\n"; + OS << "Displaying notes found "; + + if (SecName) + OS << "in: " << *SecName << "\n"; + else + OS << "at file offset " << format_hex(Offset, 10) << " with length " + << format_hex(Size, 10) << ":\n"; + + OS << " Owner Data size \tDescription\n"; }; auto ProcessNote = [&](const Elf_Note &Note) { @@ -4947,12 +5512,13 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { } }; - ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections()); + ArrayRef<Elf_Shdr> Sections = cantFail(Obj->sections()); if (Obj->getHeader()->e_type != ELF::ET_CORE && !Sections.empty()) { for (const auto &S : Sections) { if (S.sh_type != SHT_NOTE) continue; - PrintHeader(S.sh_offset, S.sh_size); + PrintHeader(expectedToOptional(Obj->getSectionName(&S)), S.sh_offset, + S.sh_size); Error Err = Error::success(); for (auto Note : Obj->notes(S, Err)) ProcessNote(Note); @@ -4960,11 +5526,18 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { reportError(std::move(Err), this->FileName); } } else { - for (const auto &P : - unwrapOrError(this->FileName, Obj->program_headers())) { + Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers(); + if (!PhdrsOrErr) { + this->reportUniqueWarning(createError( + "unable to read program headers to locate the PT_NOTE segment: " + + toString(PhdrsOrErr.takeError()))); + return; + } + + for (const Elf_Phdr &P : *PhdrsOrErr) { if (P.p_type != PT_NOTE) continue; - PrintHeader(P.p_offset, P.p_filesz); + PrintHeader(/*SecName=*/None, P.p_offset, P.p_filesz); Error Err = Error::success(); for (auto Note : Obj->notes(P, Err)) ProcessNote(Note); @@ -4980,8 +5553,87 @@ void GNUStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) { } template <class ELFT> +void DumpStyle<ELFT>::printDependentLibsHelper( + const ELFFile<ELFT> *Obj, + function_ref<void(const Elf_Shdr &)> OnSectionStart, + function_ref<void(StringRef, uint64_t)> OnLibEntry) { + auto Warn = [this](unsigned SecNdx, StringRef Msg) { + this->reportUniqueWarning( + createError("SHT_LLVM_DEPENDENT_LIBRARIES section at index " + + Twine(SecNdx) + " is broken: " + Msg)); + }; + + unsigned I = -1; + for (const Elf_Shdr &Shdr : cantFail(Obj->sections())) { + ++I; + if (Shdr.sh_type != ELF::SHT_LLVM_DEPENDENT_LIBRARIES) + continue; + + OnSectionStart(Shdr); + + Expected<ArrayRef<uint8_t>> ContentsOrErr = Obj->getSectionContents(&Shdr); + if (!ContentsOrErr) { + Warn(I, toString(ContentsOrErr.takeError())); + continue; + } + + ArrayRef<uint8_t> Contents = *ContentsOrErr; + if (!Contents.empty() && Contents.back() != 0) { + Warn(I, "the content is not null-terminated"); + continue; + } + + for (const uint8_t *I = Contents.begin(), *E = Contents.end(); I < E;) { + StringRef Lib((const char *)I); + OnLibEntry(Lib, I - Contents.begin()); + I += Lib.size() + 1; + } + } +} + +template <class ELFT> void GNUStyle<ELFT>::printDependentLibs(const ELFFile<ELFT> *Obj) { - OS << "printDependentLibs not implemented!\n"; + bool SectionStarted = false; + struct NameOffset { + StringRef Name; + uint64_t Offset; + }; + std::vector<NameOffset> SecEntries; + NameOffset Current; + auto PrintSection = [&]() { + OS << "Dependent libraries section " << Current.Name << " at offset " + << format_hex(Current.Offset, 1) << " contains " << SecEntries.size() + << " entries:\n"; + for (NameOffset Entry : SecEntries) + OS << " [" << format("%6tx", Entry.Offset) << "] " << Entry.Name + << "\n"; + OS << "\n"; + SecEntries.clear(); + }; + + auto OnSectionStart = [&](const Elf_Shdr &Shdr) { + if (SectionStarted) + PrintSection(); + SectionStarted = true; + Current.Offset = Shdr.sh_offset; + Expected<StringRef> Name = Obj->getSectionName(&Shdr); + if (!Name) { + Current.Name = "<?>"; + this->reportUniqueWarning( + createError("cannot get section name of " + "SHT_LLVM_DEPENDENT_LIBRARIES section: " + + toString(Name.takeError()))); + } else { + Current.Name = *Name; + } + }; + auto OnLibEntry = [&](StringRef Lib, uint64_t Offset) { + SecEntries.push_back(NameOffset{Lib, Offset}); + }; + + this->printDependentLibsHelper(Obj, OnSectionStart, OnLibEntry); + if (SectionStarted) + PrintSection(); } // Used for printing section names in places where possible errors can be @@ -5005,9 +5657,12 @@ static std::string getSymbolName(const ELFSymbolRef &Sym) { } template <class ELFT> -void DumpStyle<ELFT>::printFunctionStackSize( - const ELFObjectFile<ELFT> *Obj, uint64_t SymValue, SectionRef FunctionSec, - const StringRef SectionName, DataExtractor Data, uint64_t *Offset) { +void DumpStyle<ELFT>::printFunctionStackSize(const ELFObjectFile<ELFT> *Obj, + uint64_t SymValue, + Optional<SectionRef> FunctionSec, + const StringRef SectionName, + DataExtractor Data, + uint64_t *Offset) { // This function ignores potentially erroneous input, unless it is directly // related to stack size reporting. SymbolRef FuncSym; @@ -5017,9 +5672,15 @@ void DumpStyle<ELFT>::printFunctionStackSize( consumeError(SymAddrOrErr.takeError()); continue; } + if (Expected<uint32_t> SymFlags = Symbol.getFlags()) { + if (*SymFlags & SymbolRef::SF_Undefined) + continue; + } else + consumeError(SymFlags.takeError()); if (Symbol.getELFType() == ELF::STT_FUNC && *SymAddrOrErr == SymValue) { - // Check if the symbol is in the right section. - if (FunctionSec.containsSymbol(Symbol)) { + // Check if the symbol is in the right section. FunctionSec == None means + // "any section". + if (!FunctionSec || FunctionSec->containsSymbol(Symbol)) { FuncSym = Symbol; break; } @@ -5130,11 +5791,6 @@ void DumpStyle<ELFT>::printNonRelocatableStackSizes( ArrayRef<uint8_t> Contents = unwrapOrError(this->FileName, EF->getSectionContents(ElfSec)); DataExtractor Data(Contents, Obj->isLittleEndian(), sizeof(Elf_Addr)); - // A .stack_sizes section header's sh_link field is supposed to point - // to the section that contains the functions whose stack sizes are - // described in it. - const Elf_Shdr *FunctionELFSec = - unwrapOrError(this->FileName, EF->getSection(ElfSec->sh_link)); uint64_t Offset = 0; while (Offset < Contents.size()) { // The function address is followed by a ULEB representing the stack @@ -5148,8 +5804,8 @@ void DumpStyle<ELFT>::printNonRelocatableStackSizes( FileStr); } uint64_t SymValue = Data.getAddress(&Offset); - printFunctionStackSize(Obj, SymValue, Obj->toSectionRef(FunctionELFSec), - SectionName, Data, &Offset); + printFunctionStackSize(Obj, SymValue, /*FunctionSec=*/None, SectionName, + Data, &Offset); } } } @@ -5532,7 +6188,7 @@ template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const ELFO *Obj) { ListScope D(W, "Relocations"); int SectionNumber = -1; - for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) { + for (const Elf_Shdr &Sec : cantFail(Obj->sections())) { ++SectionNumber; if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA && @@ -5557,6 +6213,8 @@ template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) { const Elf_Shdr *SymTab = unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); + unsigned SecNdx = Sec - &cantFail(Obj->sections()).front(); + unsigned RelNdx = 0; switch (Sec->sh_type) { case ELF::SHT_REL: @@ -5565,12 +6223,12 @@ void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) { Rela.r_offset = R.r_offset; Rela.r_info = R.r_info; Rela.r_addend = 0; - printRelocation(Obj, Rela, SymTab); + printRelocation(Obj, SecNdx, Rela, ++RelNdx, SymTab); } break; case ELF::SHT_RELA: for (const Elf_Rela &R : unwrapOrError(this->FileName, Obj->relas(Sec))) - printRelocation(Obj, R, SymTab); + printRelocation(Obj, SecNdx, R, ++RelNdx, SymTab); break; case ELF::SHT_RELR: case ELF::SHT_ANDROID_RELR: { @@ -5582,7 +6240,7 @@ void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) { std::vector<Elf_Rela> RelrRelas = unwrapOrError(this->FileName, Obj->decode_relrs(Relrs)); for (const Elf_Rela &R : RelrRelas) - printRelocation(Obj, R, SymTab); + printRelocation(Obj, SecNdx, R, ++RelNdx, SymTab); } break; } @@ -5590,30 +6248,27 @@ void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) { case ELF::SHT_ANDROID_RELA: for (const Elf_Rela &R : unwrapOrError(this->FileName, Obj->android_relas(Sec))) - printRelocation(Obj, R, SymTab); + printRelocation(Obj, SecNdx, R, ++RelNdx, SymTab); break; } } template <class ELFT> -void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, Elf_Rela Rel, +void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, unsigned SecIndex, + Elf_Rela Rel, unsigned RelIndex, const Elf_Shdr *SymTab) { + Expected<std::pair<const typename ELFT::Sym *, std::string>> Target = + this->dumper()->getRelocationTarget(SymTab, Rel); + if (!Target) { + this->reportUniqueWarning(createError( + "unable to print relocation " + Twine(RelIndex) + " in section " + + Twine(SecIndex) + ": " + toString(Target.takeError()))); + return; + } + + std::string TargetName = Target->second; SmallString<32> RelocName; Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); - std::string TargetName; - const Elf_Sym *Sym = - unwrapOrError(this->FileName, Obj->getRelocationSymbol(&Rel, SymTab)); - if (Sym && Sym->getType() == ELF::STT_SECTION) { - const Elf_Shdr *Sec = unwrapOrError( - this->FileName, - Obj->getSection(Sym, SymTab, this->dumper()->getShndxTable())); - TargetName = unwrapOrError(this->FileName, Obj->getSectionName(Sec)); - } else if (Sym) { - StringRef StrTable = - unwrapOrError(this->FileName, Obj->getStringTableForSymtab(*SymTab)); - TargetName = this->dumper()->getFullSymbolName( - Sym, StrTable, SymTab->sh_type == SHT_DYNSYM /* IsDynamic */); - } if (opts::ExpandRelocs) { DictScope Group(W, "Relocation"); @@ -5635,13 +6290,16 @@ void LLVMStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { ListScope SectionsD(W, "Sections"); int SectionIndex = -1; - ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections()); - const ELFObjectFile<ELFT> *ElfObj = this->dumper()->getElfObject(); std::vector<EnumEntry<unsigned>> FlagsList = getSectionFlagsForTarget(Obj->getHeader()->e_machine); - for (const Elf_Shdr &Sec : Sections) { - StringRef Name = unwrapOrError( - ElfObj->getFileName(), Obj->getSectionName(&Sec, this->WarningHandler)); + for (const Elf_Shdr &Sec : cantFail(Obj->sections())) { + StringRef Name = "<?>"; + if (Expected<StringRef> SecNameOrErr = + Obj->getSectionName(&Sec, this->dumper()->WarningHandler)) + Name = *SecNameOrErr; + else + this->reportUniqueWarning(SecNameOrErr.takeError()); + DictScope SectionD(W, "Section"); W.printNumber("Index", ++SectionIndex); W.printNumber("Name", Name, Sec.sh_name); @@ -5709,7 +6367,12 @@ void LLVMStyle<ELFT>::printSymbolSection(const Elf_Sym *Symbol, Expected<StringRef> SectionName = this->dumper()->getSymbolSectionName(Symbol, *SectionIndex); if (!SectionName) { - this->reportUniqueWarning(SectionName.takeError()); + // Don't report an invalid section name if the section headers are missing. + // In such situations, all sections will be "invalid". + if (!this->dumper()->getElfObject()->sections().empty()) + this->reportUniqueWarning(SectionName.takeError()); + else + consumeError(SectionName.takeError()); W.printHex("Section", "<?>", *SectionIndex); } else { W.printHex("Section", *SectionName, *SectionIndex); @@ -5718,8 +6381,8 @@ void LLVMStyle<ELFT>::printSymbolSection(const Elf_Sym *Symbol, template <class ELFT> void LLVMStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, - const Elf_Sym *First, StringRef StrTable, - bool IsDynamic, + const Elf_Sym *First, + Optional<StringRef> StrTable, bool IsDynamic, bool /*NonVisibilityBitsUsed*/) { std::string FullSymbolName = this->dumper()->getFullSymbolName(Symbol, StrTable, IsDynamic); @@ -5785,20 +6448,24 @@ template <class ELFT> void LLVMStyle<ELFT>::printDynamic(const ELFFile<ELFT> *Ob 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"; + size_t MaxTagSize = getMaxDynamicTagSize(Obj, Table); + // The "Name/Value" column should be indented from the "Type" column by N + // spaces, where N = MaxTagSize - length of "Type" (4) + trailing + // space (1) = -3. + W.startLine() << " Tag" << std::string(ELFT::Is64Bits ? 16 : 8, ' ') + << "Type" << std::string(MaxTagSize - 3, ' ') << "Name/Value\n"; + + std::string ValueFmt = "%-" + std::to_string(MaxTagSize) + "s "; for (auto Entry : Table) { uintX_t Tag = Entry.getTag(); - W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, true) << " " - << format("%-21s", Obj->getDynamicTagAsString(Tag).c_str()); - this->dumper()->printDynamicEntry(OS, Tag, Entry.getVal()); - OS << "\n"; + std::string Value = this->dumper()->getDynamicEntry(Tag, Entry.getVal()); + W.startLine() << " " << format_hex(Tag, ELFT::Is64Bits ? 18 : 10, true) + << " " + << format(ValueFmt.c_str(), + Obj->getDynamicTagAsString(Tag).c_str()) + << Value << "\n"; } W.startLine() << "]\n"; } @@ -5809,14 +6476,14 @@ void LLVMStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { const DynRegionInfo &DynRelaRegion = this->dumper()->getDynRelaRegion(); const DynRegionInfo &DynRelrRegion = this->dumper()->getDynRelrRegion(); const DynRegionInfo &DynPLTRelRegion = this->dumper()->getDynPLTRelRegion(); - if (DynRelRegion.Size && DynRelaRegion.Size) - report_fatal_error("There are both REL and RELA dynamic relocations"); + W.startLine() << "Dynamic Relocations {\n"; W.indent(); - if (DynRelaRegion.Size > 0) + if (DynRelaRegion.Size > 0) { for (const Elf_Rela &Rela : this->dumper()->dyn_relas()) printDynamicRelocation(Obj, Rela); - else + } + if (DynRelRegion.Size > 0) { for (const Elf_Rel &Rel : this->dumper()->dyn_rels()) { Elf_Rela Rela; Rela.r_offset = Rel.r_offset; @@ -5824,6 +6491,8 @@ void LLVMStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { Rela.r_addend = 0; printDynamicRelocation(Obj, Rela); } + } + if (DynRelrRegion.Size > 0) { Elf_Relr_Range Relrs = this->dumper()->dyn_relrs(); std::vector<Elf_Rela> RelrRelas = @@ -5881,8 +6550,14 @@ template <class ELFT> void LLVMStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { ListScope L(W, "ProgramHeaders"); - for (const Elf_Phdr &Phdr : - unwrapOrError(this->FileName, Obj->program_headers())) { + Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers(); + if (!PhdrsOrErr) { + this->reportUniqueWarning(createError("unable to dump program headers: " + + toString(PhdrsOrErr.takeError()))); + return; + } + + for (const Elf_Phdr &Phdr : *PhdrsOrErr) { DictScope P(W, "ProgramHeader"); W.printHex("Type", getElfSegmentType(Obj->getHeader()->e_machine, Phdr.p_type), @@ -5982,7 +6657,7 @@ void LLVMStyle<ELFT>::printVersionDependencySection(const ELFFile<ELFT> *Obj, } template <class ELFT> -void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { +void LLVMStyle<ELFT>::printHashHistograms(const ELFFile<ELFT> *Obj) { W.startLine() << "Hash Histogram not implemented!\n"; } @@ -5991,21 +6666,23 @@ void LLVMStyle<ELFT>::printCGProfile(const ELFFile<ELFT> *Obj) { ListScope L(W, "CGProfile"); if (!this->dumper()->getDotCGProfileSec()) return; - auto CGProfile = unwrapOrError( - this->FileName, Obj->template getSectionContentsAsArray<Elf_CGProfile>( - this->dumper()->getDotCGProfileSec())); - for (const Elf_CGProfile &CGPE : CGProfile) { + + Expected<ArrayRef<Elf_CGProfile>> CGProfileOrErr = + Obj->template getSectionContentsAsArray<Elf_CGProfile>( + this->dumper()->getDotCGProfileSec()); + if (!CGProfileOrErr) { + this->reportUniqueWarning( + createError("unable to dump the SHT_LLVM_CALL_GRAPH_PROFILE section: " + + toString(CGProfileOrErr.takeError()))); + return; + } + + for (const Elf_CGProfile &CGPE : *CGProfileOrErr) { DictScope D(W, "CGProfileEntry"); - W.printNumber( - "From", - unwrapOrError(this->FileName, - this->dumper()->getStaticSymbolName(CGPE.cgp_from)), - CGPE.cgp_from); - W.printNumber( - "To", - unwrapOrError(this->FileName, - this->dumper()->getStaticSymbolName(CGPE.cgp_to)), - CGPE.cgp_to); + W.printNumber("From", this->dumper()->getStaticSymbolName(CGPE.cgp_from), + CGPE.cgp_from); + W.printNumber("To", this->dumper()->getStaticSymbolName(CGPE.cgp_to), + CGPE.cgp_to); W.printNumber("Weight", CGPE.cgp_weight); } } @@ -6096,8 +6773,10 @@ template <class ELFT> void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { ListScope L(W, "Notes"); - auto PrintHeader = [&](const typename ELFT::Off Offset, + auto PrintHeader = [&](Optional<StringRef> SecName, + const typename ELFT::Off Offset, const typename ELFT::Addr Size) { + W.printString("Name", SecName ? *SecName : "<?>"); W.printHex("Offset", Offset); W.printHex("Size", Size); }; @@ -6158,13 +6837,14 @@ void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { } }; - ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections()); + ArrayRef<Elf_Shdr> Sections = cantFail(Obj->sections()); if (Obj->getHeader()->e_type != ELF::ET_CORE && !Sections.empty()) { for (const auto &S : Sections) { if (S.sh_type != SHT_NOTE) continue; DictScope D(W, "NoteSection"); - PrintHeader(S.sh_offset, S.sh_size); + PrintHeader(expectedToOptional(Obj->getSectionName(&S)), S.sh_offset, + S.sh_size); Error Err = Error::success(); for (auto Note : Obj->notes(S, Err)) ProcessNote(Note); @@ -6172,12 +6852,19 @@ void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { reportError(std::move(Err), this->FileName); } } else { - for (const auto &P : - unwrapOrError(this->FileName, Obj->program_headers())) { + Expected<ArrayRef<Elf_Phdr>> PhdrsOrErr = Obj->program_headers(); + if (!PhdrsOrErr) { + this->reportUniqueWarning(createError( + "unable to read program headers to locate the PT_NOTE segment: " + + toString(PhdrsOrErr.takeError()))); + return; + } + + for (const Elf_Phdr &P : *PhdrsOrErr) { if (P.p_type != PT_NOTE) continue; DictScope D(W, "NoteSection"); - PrintHeader(P.p_offset, P.p_filesz); + PrintHeader(/*SecName=*/None, P.p_offset, P.p_filesz); Error Err = Error::success(); for (auto Note : Obj->notes(P, Err)) ProcessNote(Note); @@ -6192,35 +6879,38 @@ void LLVMStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) { ListScope L(W, "LinkerOptions"); unsigned I = -1; - for (const Elf_Shdr &Shdr : unwrapOrError(this->FileName, Obj->sections())) { + for (const Elf_Shdr &Shdr : cantFail(Obj->sections())) { ++I; if (Shdr.sh_type != ELF::SHT_LLVM_LINKER_OPTIONS) continue; - ArrayRef<uint8_t> Contents = - unwrapOrError(this->FileName, Obj->getSectionContents(&Shdr)); - if (Contents.empty()) + Expected<ArrayRef<uint8_t>> ContentsOrErr = Obj->getSectionContents(&Shdr); + if (!ContentsOrErr) { + this->reportUniqueWarning( + createError("unable to read the content of the " + "SHT_LLVM_LINKER_OPTIONS section: " + + toString(ContentsOrErr.takeError()))); + continue; + } + if (ContentsOrErr->empty()) continue; - if (Contents.back() != 0) { - reportWarning(createError("SHT_LLVM_LINKER_OPTIONS section at index " + - Twine(I) + - " is broken: the " - "content is not null-terminated"), - this->FileName); + if (ContentsOrErr->back() != 0) { + this->reportUniqueWarning( + createError("SHT_LLVM_LINKER_OPTIONS section at index " + Twine(I) + + " is broken: the " + "content is not null-terminated")); continue; } SmallVector<StringRef, 16> Strings; - toStringRef(Contents.drop_back()).split(Strings, '\0'); + toStringRef(ContentsOrErr->drop_back()).split(Strings, '\0'); if (Strings.size() % 2 != 0) { - reportWarning( - createError( - "SHT_LLVM_LINKER_OPTIONS section at index " + Twine(I) + - " is broken: an incomplete " - "key-value pair was found. The last possible key was: \"" + - Strings.back() + "\""), - this->FileName); + this->reportUniqueWarning(createError( + "SHT_LLVM_LINKER_OPTIONS section at index " + Twine(I) + + " is broken: an incomplete " + "key-value pair was found. The last possible key was: \"" + + Strings.back() + "\"")); continue; } @@ -6232,37 +6922,9 @@ void LLVMStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) { template <class ELFT> void LLVMStyle<ELFT>::printDependentLibs(const ELFFile<ELFT> *Obj) { ListScope L(W, "DependentLibs"); - - auto Warn = [this](unsigned SecNdx, StringRef Msg) { - this->reportUniqueWarning( - createError("SHT_LLVM_DEPENDENT_LIBRARIES section at index " + - Twine(SecNdx) + " is broken: " + Msg)); - }; - - unsigned I = -1; - for (const Elf_Shdr &Shdr : unwrapOrError(this->FileName, Obj->sections())) { - ++I; - if (Shdr.sh_type != ELF::SHT_LLVM_DEPENDENT_LIBRARIES) - continue; - - Expected<ArrayRef<uint8_t>> ContentsOrErr = Obj->getSectionContents(&Shdr); - if (!ContentsOrErr) { - Warn(I, toString(ContentsOrErr.takeError())); - continue; - } - - ArrayRef<uint8_t> Contents = *ContentsOrErr; - if (!Contents.empty() && Contents.back() != 0) { - Warn(I, "the content is not null-terminated"); - continue; - } - - for (const uint8_t *I = Contents.begin(), *E = Contents.end(); I < E;) { - StringRef Lib((const char *)I); - W.printString(Lib); - I += Lib.size() + 1; - } - } + this->printDependentLibsHelper( + Obj, [](const Elf_Shdr &) {}, + [this](StringRef Lib, uint64_t) { W.printString(Lib); }); } template <class ELFT> diff --git a/llvm/tools/llvm-readobj/ObjDumper.cpp b/llvm/tools/llvm-readobj/ObjDumper.cpp index 6229b52693d87..ce61f1c53a4dd 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.cpp +++ b/llvm/tools/llvm-readobj/ObjDumper.cpp @@ -48,13 +48,13 @@ getSectionRefsByNameOrIndex(const object::ObjectFile *Obj, if (!Section.getAsInteger(0, SecIndex)) SecIndices.emplace(SecIndex, false); else - SecNames.emplace(Section, false); + SecNames.emplace(std::string(Section), false); } SecIndex = Obj->isELF() ? 0 : 1; for (object::SectionRef SecRef : Obj->sections()) { StringRef SecName = unwrapOrError(Obj->getFileName(), SecRef.getName()); - auto NameIt = SecNames.find(SecName); + auto NameIt = SecNames.find(std::string(SecName)); if (NameIt != SecNames.end()) NameIt->second = true; auto IndexIt = SecIndices.find(SecIndex); diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index 3fc8d3e79ac1c..57477606d6e8e 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -59,12 +59,12 @@ public: virtual void printNeededLibraries() { } virtual void printSectionAsHex(StringRef SectionName) {} virtual void printHashTable() { } - virtual void printGnuHashTable() { } + virtual void printGnuHashTable(const object::ObjectFile *Obj) {} virtual void printHashSymbols() {} virtual void printLoadName() {} virtual void printVersionInfo() {} virtual void printGroupSections() {} - virtual void printHashHistogram() {} + virtual void printHashHistograms() {} virtual void printCGProfile() {} virtual void printAddrsig() {} virtual void printNotes() {} diff --git a/llvm/tools/llvm-readobj/WasmDumper.cpp b/llvm/tools/llvm-readobj/WasmDumper.cpp index dfab9f40d71b4..a02dbb9998267 100644 --- a/llvm/tools/llvm-readobj/WasmDumper.cpp +++ b/llvm/tools/llvm-readobj/WasmDumper.cpp @@ -93,18 +93,8 @@ void WasmDumper::printRelocation(const SectionRef &Section, if (SI != Obj->symbol_end()) SymName = unwrapOrError(Obj->getFileName(), SI->getName()); - bool HasAddend = false; - switch (RelocType) { - 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: - break; - } + bool HasAddend = wasm::relocTypeHasAddend(static_cast<uint32_t>(RelocType)); + if (opts::ExpandRelocs) { DictScope Group(W, "Relocation"); W.printNumber("Type", RelocTypeName, RelocType); @@ -192,6 +182,10 @@ void WasmDumper::printSectionHeaders() { W.printNumber("Size", static_cast<uint64_t>(Seg.Content.size())); if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I32_CONST) W.printNumber("Offset", Seg.Offset.Value.Int32); + else if (Seg.Offset.Opcode == wasm::WASM_OPCODE_I64_CONST) + W.printNumber("Offset", Seg.Offset.Value.Int64); + else + llvm_unreachable("unknown init expr opcode"); } break; } @@ -227,8 +221,12 @@ void WasmDumper::printSymbol(const SymbolRef &Sym) { 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.ImportName) { + W.printString("ImportName", *Symbol.Info.ImportName); + } + if (Symbol.Info.ImportModule) { + W.printString("ImportModule", *Symbol.Info.ImportModule); + } } if (Symbol.Info.Kind != wasm::WASM_SYMBOL_TYPE_DATA) { W.printHex("ElementIndex", Symbol.Info.ElementIndex); diff --git a/llvm/tools/llvm-readobj/XCOFFDumper.cpp b/llvm/tools/llvm-readobj/XCOFFDumper.cpp index 1f94036655943..dd62f98d95957 100644 --- a/llvm/tools/llvm-readobj/XCOFFDumper.cpp +++ b/llvm/tools/llvm-readobj/XCOFFDumper.cpp @@ -22,11 +22,6 @@ using namespace object; namespace { class XCOFFDumper : public ObjDumper { - enum { - SymbolTypeMask = 0x07, - SymbolAlignmentMask = 0xF8, - SymbolAlignmentBitOffset = 3 - }; public: XCOFFDumper(const XCOFFObjectFile &Obj, ScopedPrinter &Writer) @@ -211,17 +206,15 @@ void XCOFFDumper::printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr) { DictScope SymDs(W, "CSECT Auxiliary Entry"); W.printNumber("Index", Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr))); - if ((AuxEntPtr->SymbolAlignmentAndType & SymbolTypeMask) == XCOFF::XTY_LD) + if (AuxEntPtr->isLabel()) W.printNumber("ContainingCsectSymbolIndex", AuxEntPtr->SectionOrLength); else W.printNumber("SectionLen", AuxEntPtr->SectionOrLength); W.printHex("ParameterHashIndex", AuxEntPtr->ParameterHashIndex); W.printHex("TypeChkSectNum", AuxEntPtr->TypeChkSectNum); // Print out symbol alignment and type. - W.printNumber("SymbolAlignmentLog2", - (AuxEntPtr->SymbolAlignmentAndType & SymbolAlignmentMask) >> - SymbolAlignmentBitOffset); - W.printEnum("SymbolType", AuxEntPtr->SymbolAlignmentAndType & SymbolTypeMask, + W.printNumber("SymbolAlignmentLog2", AuxEntPtr->getAlignmentLog2()); + W.printEnum("SymbolType", AuxEntPtr->getSymbolType(), makeArrayRef(CsectSymbolTypeClass)); W.printEnum("StorageMappingClass", static_cast<uint8_t>(AuxEntPtr->StorageMappingClass), diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index fadeec1072d67..b9c6ad2256ae2 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -345,8 +345,11 @@ namespace opts { cl::desc("Alias for --elf-hash-histogram"), cl::aliasopt(HashHistogram)); - // --elf-cg-profile - cl::opt<bool> CGProfile("elf-cg-profile", cl::desc("Display callgraph profile section")); + // --cg-profile + cl::opt<bool> CGProfile("cg-profile", + cl::desc("Display callgraph profile section")); + cl::alias ELFCGProfile("elf-cg-profile", cl::desc("Alias for --cg-profile"), + cl::aliasopt(CGProfile)); // -addrsig cl::opt<bool> Addrsig("addrsig", @@ -456,8 +459,9 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, Writer.printString("Format", Obj->getFileFormatName()); Writer.printString("Arch", Triple::getArchTypeName( (llvm::Triple::ArchType)Obj->getArch())); - Writer.printString("AddressSize", - formatv("{0}bit", 8 * Obj->getBytesInAddress())); + Writer.printString( + "AddressSize", + std::string(formatv("{0}bit", 8 * Obj->getBytesInAddress()))); Dumper->printLoadName(); } @@ -465,22 +469,22 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, Dumper->printFileHeaders(); if (opts::SectionHeaders) Dumper->printSectionHeaders(); - if (opts::Relocations) - Dumper->printRelocations(); - if (opts::DynRelocs) - Dumper->printDynamicRelocations(); - if (opts::Symbols || opts::DynamicSymbols) - Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols); if (opts::HashSymbols) Dumper->printHashSymbols(); - if (opts::UnwindInfo) - Dumper->printUnwindInfo(); + if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE) + Dumper->printProgramHeaders(opts::ProgramHeaders, opts::SectionMapping); if (opts::DynamicTable) Dumper->printDynamicTable(); if (opts::NeededLibraries) Dumper->printNeededLibraries(); - if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE) - Dumper->printProgramHeaders(opts::ProgramHeaders, opts::SectionMapping); + if (opts::Relocations) + Dumper->printRelocations(); + if (opts::DynRelocs) + Dumper->printDynamicRelocations(); + if (opts::UnwindInfo) + Dumper->printUnwindInfo(); + if (opts::Symbols || opts::DynamicSymbols) + Dumper->printSymbols(opts::Symbols, opts::DynamicSymbols); if (!opts::StringDump.empty()) Dumper->printSectionsAsString(Obj, opts::StringDump); if (!opts::HexDump.empty()) @@ -488,7 +492,7 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, if (opts::HashTable) Dumper->printHashTable(); if (opts::GnuHashTable) - Dumper->printGnuHashTable(); + Dumper->printGnuHashTable(Obj); if (opts::VersionInfo) Dumper->printVersionInfo(); if (Obj->isELF()) { @@ -501,7 +505,7 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, if (opts::SectionGroups) Dumper->printGroupSections(); if (opts::HashHistogram) - Dumper->printHashHistogram(); + Dumper->printHashHistograms(); if (opts::CGProfile) Dumper->printCGProfile(); if (opts::Addrsig) @@ -524,6 +528,8 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, Dumper->printCOFFResources(); if (opts::COFFLoadConfig) Dumper->printCOFFLoadConfig(); + if (opts::CGProfile) + Dumper->printCGProfile(); if (opts::Addrsig) Dumper->printAddrsig(); if (opts::CodeView) diff --git a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp index 9b84c46d39010..be5dbdd1c5597 100644 --- a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -189,7 +189,7 @@ class TrivialMemoryManager : public RTDyldMemoryManager { public: struct SectionInfo { SectionInfo(StringRef Name, sys::MemoryBlock MB, unsigned SectionID) - : Name(Name), MB(std::move(MB)), SectionID(SectionID) {} + : Name(std::string(Name)), MB(std::move(MB)), SectionID(SectionID) {} std::string Name; sys::MemoryBlock MB; unsigned SectionID = ~0U; @@ -599,7 +599,7 @@ void applySpecificSectionMappings(RuntimeDyld &Dyld, for (StringRef Mapping : SpecificSectionMappings) { size_t EqualsIdx = Mapping.find_first_of("="); - std::string SectionIDStr = Mapping.substr(0, EqualsIdx); + std::string SectionIDStr = std::string(Mapping.substr(0, EqualsIdx)); size_t ComaIdx = Mapping.find_first_of(","); if (ComaIdx == StringRef::npos) @@ -612,7 +612,7 @@ void applySpecificSectionMappings(RuntimeDyld &Dyld, ExitOnErr(getSectionId(FileToSecIDMap, FileName, SectionName)); auto* OldAddr = Dyld.getSectionContent(SectionID).data(); - std::string NewAddrStr = Mapping.substr(EqualsIdx + 1); + std::string NewAddrStr = std::string(Mapping.substr(EqualsIdx + 1)); uint64_t NewAddr; if (StringRef(NewAddrStr).getAsInteger(0, NewAddr)) diff --git a/llvm/tools/llvm-size/llvm-size.cpp b/llvm/tools/llvm-size/llvm-size.cpp new file mode 100644 index 0000000000000..987270e98c483 --- /dev/null +++ b/llvm/tools/llvm-size/llvm-size.cpp @@ -0,0 +1,910 @@ +//===-- llvm-size.cpp - Print the size of each object section ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This program is a utility that works like traditional Unix "size", +// that is, it prints out the size of each section, and the total size of all +// sections. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/APInt.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <string> +#include <system_error> + +using namespace llvm; +using namespace object; + +cl::OptionCategory SizeCat("llvm-size Options"); + +enum OutputFormatTy { berkeley, sysv, darwin }; +static cl::opt<OutputFormatTy> + OutputFormat("format", cl::desc("Specify output format"), + cl::values(clEnumVal(sysv, "System V format"), + clEnumVal(berkeley, "Berkeley format"), + clEnumVal(darwin, "Darwin -m format")), + cl::init(berkeley), cl::cat(SizeCat)); + +static cl::opt<OutputFormatTy> + OutputFormatShort(cl::desc("Specify output format"), + cl::values(clEnumValN(sysv, "A", "System V format"), + clEnumValN(berkeley, "B", "Berkeley format"), + clEnumValN(darwin, "m", "Darwin -m format")), + cl::init(berkeley), cl::cat(SizeCat)); + +static bool BerkeleyHeaderPrinted = false; +static bool MoreThanOneFile = false; +static uint64_t TotalObjectText = 0; +static uint64_t TotalObjectData = 0; +static uint64_t TotalObjectBss = 0; +static uint64_t TotalObjectTotal = 0; + +cl::opt<bool> + DarwinLongFormat("l", + cl::desc("When format is darwin, use long format " + "to include addresses and offsets."), + cl::cat(SizeCat)); + +cl::opt<bool> + ELFCommons("common", + cl::desc("Print common symbols in the ELF file. When using " + "Berkeley format, this is added to bss."), + cl::init(false), cl::cat(SizeCat)); + +static cl::list<std::string> + ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), + cl::ZeroOrMore, cl::cat(SizeCat)); +static bool ArchAll = false; + +enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 }; +static cl::opt<RadixTy> Radix( + "radix", cl::desc("Print size in radix"), cl::init(decimal), + cl::values(clEnumValN(octal, "8", "Print size in octal"), + clEnumValN(decimal, "10", "Print size in decimal"), + clEnumValN(hexadecimal, "16", "Print size in hexadecimal")), + cl::cat(SizeCat)); + +static cl::opt<RadixTy> RadixShort( + cl::desc("Print size in radix:"), + cl::values(clEnumValN(octal, "o", "Print size in octal"), + clEnumValN(decimal, "d", "Print size in decimal"), + clEnumValN(hexadecimal, "x", "Print size in hexadecimal")), + cl::init(decimal), cl::cat(SizeCat)); + +static cl::opt<bool> + TotalSizes("totals", + cl::desc("Print totals of all objects - Berkeley format only"), + cl::init(false), cl::cat(SizeCat)); + +static cl::alias TotalSizesShort("t", cl::desc("Short for --totals"), + cl::aliasopt(TotalSizes)); + +static cl::list<std::string> + InputFilenames(cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore); + +static cl::extrahelp + HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); + +static bool HadError = false; + +static std::string ToolName; + +static void error(const Twine &Message, StringRef File) { + HadError = true; + WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n"; +} + +// This version of error() prints the archive name and member name, for example: +// "libx.a(foo.o)" after the ToolName before the error message. It sets +// HadError but returns allowing the code to move on to other archive members. +static void error(llvm::Error E, StringRef FileName, const Archive::Child &C, + StringRef ArchitectureName = StringRef()) { + HadError = true; + WithColor::error(errs(), ToolName) << "'" << FileName << "'"; + + 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()); + errs() << "(" << "???" << ")"; + } else + errs() << "(" << NameOrErr.get() << ")"; + + if (!ArchitectureName.empty()) + errs() << " (for architecture " << ArchitectureName << ") "; + + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(std::move(E), OS); + OS.flush(); + errs() << ": " << Buf << "\n"; +} + +// This version of error() prints the file name and which architecture slice it // is from, for example: "foo.o (for architecture i386)" after the ToolName +// before the error message. It sets HadError but returns allowing the code to +// move on to other architecture slices. +static void error(llvm::Error E, StringRef FileName, + StringRef ArchitectureName = StringRef()) { + HadError = true; + WithColor::error(errs(), ToolName) << "'" << FileName << "'"; + + if (!ArchitectureName.empty()) + errs() << " (for architecture " << ArchitectureName << ") "; + + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(std::move(E), OS); + OS.flush(); + errs() << ": " << Buf << "\n"; +} + +/// Get the length of the string that represents @p num in Radix including the +/// leading 0x or 0 for hexadecimal and octal respectively. +static size_t getNumLengthAsString(uint64_t num) { + APInt conv(64, num); + SmallString<32> result; + conv.toString(result, Radix, false, true); + return result.size(); +} + +/// Return the printing format for the Radix. +static const char *getRadixFmt() { + switch (Radix) { + case octal: + return PRIo64; + case decimal: + return PRIu64; + case hexadecimal: + return PRIx64; + } + return nullptr; +} + +/// Remove unneeded ELF sections from calculation +static bool considerForSize(ObjectFile *Obj, SectionRef Section) { + if (!Obj->isELF()) + return true; + switch (static_cast<ELFSectionRef>(Section).getType()) { + case ELF::SHT_NULL: + case ELF::SHT_SYMTAB: + return false; + case ELF::SHT_STRTAB: + case ELF::SHT_REL: + case ELF::SHT_RELA: + return static_cast<ELFSectionRef>(Section).getFlags() & ELF::SHF_ALLOC; + } + return true; +} + +/// Total size of all ELF common symbols +static Expected<uint64_t> getCommonSize(ObjectFile *Obj) { + uint64_t TotalCommons = 0; + for (auto &Sym : Obj->symbols()) { + Expected<uint32_t> SymFlagsOrErr = + Obj->getSymbolFlags(Sym.getRawDataRefImpl()); + if (!SymFlagsOrErr) + return SymFlagsOrErr.takeError(); + if (*SymFlagsOrErr & SymbolRef::SF_Common) + TotalCommons += Obj->getCommonSymbolSize(Sym.getRawDataRefImpl()); + } + return TotalCommons; +} + +/// Print the size of each Mach-O segment and section in @p MachO. +/// +/// This is when used when @c OutputFormat is darwin and produces the same +/// output as darwin's size(1) -m output. +static void printDarwinSectionSizes(MachOObjectFile *MachO) { + std::string fmtbuf; + raw_string_ostream fmt(fmtbuf); + const char *radix_fmt = getRadixFmt(); + if (Radix == hexadecimal) + fmt << "0x"; + fmt << "%" << radix_fmt; + + uint32_t Filetype = MachO->getHeader().filetype; + + uint64_t total = 0; + for (const auto &Load : MachO->load_commands()) { + if (Load.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load); + outs() << "Segment " << Seg.segname << ": " + << format(fmt.str().c_str(), Seg.vmsize); + if (DarwinLongFormat) + outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) << " fileoff " + << Seg.fileoff << ")"; + outs() << "\n"; + total += Seg.vmsize; + uint64_t sec_total = 0; + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section_64 Sec = MachO->getSection64(Load, J); + if (Filetype == MachO::MH_OBJECT) + outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", " + << format("%.16s", &Sec.sectname) << "): "; + else + outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": "; + outs() << format(fmt.str().c_str(), Sec.size); + if (DarwinLongFormat) + outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) << " offset " + << Sec.offset << ")"; + outs() << "\n"; + sec_total += Sec.size; + } + if (Seg.nsects != 0) + outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n"; + } else if (Load.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load); + uint64_t Seg_vmsize = Seg.vmsize; + outs() << "Segment " << Seg.segname << ": " + << format(fmt.str().c_str(), Seg_vmsize); + if (DarwinLongFormat) + outs() << " (vmaddr 0x" << format("%" PRIx32, Seg.vmaddr) << " fileoff " + << Seg.fileoff << ")"; + outs() << "\n"; + total += Seg.vmsize; + uint64_t sec_total = 0; + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section Sec = MachO->getSection(Load, J); + if (Filetype == MachO::MH_OBJECT) + outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", " + << format("%.16s", &Sec.sectname) << "): "; + else + outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": "; + uint64_t Sec_size = Sec.size; + outs() << format(fmt.str().c_str(), Sec_size); + if (DarwinLongFormat) + outs() << " (addr 0x" << format("%" PRIx32, Sec.addr) << " offset " + << Sec.offset << ")"; + outs() << "\n"; + sec_total += Sec.size; + } + if (Seg.nsects != 0) + outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n"; + } + } + outs() << "total " << format(fmt.str().c_str(), total) << "\n"; +} + +/// Print the summary sizes of the standard Mach-O segments in @p MachO. +/// +/// This is when used when @c OutputFormat is berkeley with a Mach-O file and +/// produces the same output as darwin's size(1) default output. +static void printDarwinSegmentSizes(MachOObjectFile *MachO) { + uint64_t total_text = 0; + uint64_t total_data = 0; + uint64_t total_objc = 0; + uint64_t total_others = 0; + for (const auto &Load : MachO->load_commands()) { + if (Load.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load); + if (MachO->getHeader().filetype == MachO::MH_OBJECT) { + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section_64 Sec = MachO->getSection64(Load, J); + StringRef SegmentName = StringRef(Sec.segname); + if (SegmentName == "__TEXT") + total_text += Sec.size; + else if (SegmentName == "__DATA") + total_data += Sec.size; + else if (SegmentName == "__OBJC") + total_objc += Sec.size; + else + total_others += Sec.size; + } + } else { + StringRef SegmentName = StringRef(Seg.segname); + if (SegmentName == "__TEXT") + total_text += Seg.vmsize; + else if (SegmentName == "__DATA") + total_data += Seg.vmsize; + else if (SegmentName == "__OBJC") + total_objc += Seg.vmsize; + else + total_others += Seg.vmsize; + } + } else if (Load.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load); + if (MachO->getHeader().filetype == MachO::MH_OBJECT) { + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section Sec = MachO->getSection(Load, J); + StringRef SegmentName = StringRef(Sec.segname); + if (SegmentName == "__TEXT") + total_text += Sec.size; + else if (SegmentName == "__DATA") + total_data += Sec.size; + else if (SegmentName == "__OBJC") + total_objc += Sec.size; + else + total_others += Sec.size; + } + } else { + StringRef SegmentName = StringRef(Seg.segname); + if (SegmentName == "__TEXT") + total_text += Seg.vmsize; + else if (SegmentName == "__DATA") + total_data += Seg.vmsize; + else if (SegmentName == "__OBJC") + total_objc += Seg.vmsize; + else + total_others += Seg.vmsize; + } + } + } + uint64_t total = total_text + total_data + total_objc + total_others; + + if (!BerkeleyHeaderPrinted) { + outs() << "__TEXT\t__DATA\t__OBJC\tothers\tdec\thex\n"; + BerkeleyHeaderPrinted = true; + } + outs() << total_text << "\t" << total_data << "\t" << total_objc << "\t" + << total_others << "\t" << total << "\t" << format("%" PRIx64, total) + << "\t"; +} + +/// Print the size of each section in @p Obj. +/// +/// The format used is determined by @c OutputFormat and @c Radix. +static void printObjectSectionSizes(ObjectFile *Obj) { + uint64_t total = 0; + std::string fmtbuf; + raw_string_ostream fmt(fmtbuf); + const char *radix_fmt = getRadixFmt(); + + // If OutputFormat is darwin and we have a MachOObjectFile print as darwin's + // size(1) -m output, else if OutputFormat is darwin and not a Mach-O object + // let it fall through to OutputFormat berkeley. + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj); + if (OutputFormat == darwin && MachO) + printDarwinSectionSizes(MachO); + // If we have a MachOObjectFile and the OutputFormat is berkeley print as + // darwin's default berkeley format for Mach-O files. + else if (MachO && OutputFormat == berkeley) + printDarwinSegmentSizes(MachO); + else if (OutputFormat == sysv) { + // Run two passes over all sections. The first gets the lengths needed for + // formatting the output. The second actually does the output. + std::size_t max_name_len = strlen("section"); + std::size_t max_size_len = strlen("size"); + std::size_t max_addr_len = strlen("addr"); + for (const SectionRef &Section : Obj->sections()) { + if (!considerForSize(Obj, Section)) + continue; + uint64_t size = Section.getSize(); + total += size; + + Expected<StringRef> name_or_err = Section.getName(); + if (!name_or_err) { + error(name_or_err.takeError(), Obj->getFileName()); + return; + } + + uint64_t addr = Section.getAddress(); + max_name_len = std::max(max_name_len, name_or_err->size()); + max_size_len = std::max(max_size_len, getNumLengthAsString(size)); + max_addr_len = std::max(max_addr_len, getNumLengthAsString(addr)); + } + + // Add extra padding. + max_name_len += 2; + max_size_len += 2; + max_addr_len += 2; + + // Setup header format. + fmt << "%-" << max_name_len << "s " + << "%" << max_size_len << "s " + << "%" << max_addr_len << "s\n"; + + // Print header + outs() << format(fmt.str().c_str(), static_cast<const char *>("section"), + static_cast<const char *>("size"), + static_cast<const char *>("addr")); + fmtbuf.clear(); + + // Setup per section format. + fmt << "%-" << max_name_len << "s " + << "%#" << max_size_len << radix_fmt << " " + << "%#" << max_addr_len << radix_fmt << "\n"; + + // Print each section. + for (const SectionRef &Section : Obj->sections()) { + if (!considerForSize(Obj, Section)) + continue; + + Expected<StringRef> name_or_err = Section.getName(); + if (!name_or_err) { + error(name_or_err.takeError(), Obj->getFileName()); + return; + } + + uint64_t size = Section.getSize(); + uint64_t addr = Section.getAddress(); + outs() << format(fmt.str().c_str(), name_or_err->str().c_str(), size, addr); + } + + if (ELFCommons) { + if (Expected<uint64_t> CommonSizeOrErr = getCommonSize(Obj)) { + total += *CommonSizeOrErr; + outs() << format(fmt.str().c_str(), std::string("*COM*").c_str(), + *CommonSizeOrErr, static_cast<uint64_t>(0)); + } else { + error(CommonSizeOrErr.takeError(), Obj->getFileName()); + return; + } + } + + // Print total. + fmtbuf.clear(); + fmt << "%-" << max_name_len << "s " + << "%#" << max_size_len << radix_fmt << "\n"; + outs() << format(fmt.str().c_str(), static_cast<const char *>("Total"), + total) + << "\n\n"; + } else { + // The Berkeley format does not display individual section sizes. It + // displays the cumulative size for each section type. + uint64_t total_text = 0; + uint64_t total_data = 0; + uint64_t total_bss = 0; + + // Make one pass over the section table to calculate sizes. + for (const SectionRef &Section : Obj->sections()) { + uint64_t size = Section.getSize(); + bool isText = Section.isBerkeleyText(); + bool isData = Section.isBerkeleyData(); + bool isBSS = Section.isBSS(); + if (isText) + total_text += size; + else if (isData) + total_data += size; + else if (isBSS) + total_bss += size; + } + + if (ELFCommons) { + if (Expected<uint64_t> CommonSizeOrErr = getCommonSize(Obj)) + total_bss += *CommonSizeOrErr; + else { + error(CommonSizeOrErr.takeError(), Obj->getFileName()); + return; + } + } + + total = total_text + total_data + total_bss; + + if (TotalSizes) { + TotalObjectText += total_text; + TotalObjectData += total_data; + TotalObjectBss += total_bss; + TotalObjectTotal += total; + } + + if (!BerkeleyHeaderPrinted) { + outs() << " text\t" + " data\t" + " bss\t" + " " + << (Radix == octal ? "oct" : "dec") + << "\t" + " hex\t" + "filename\n"; + BerkeleyHeaderPrinted = true; + } + + // Print result. + fmt << "%#7" << radix_fmt << "\t" + << "%#7" << radix_fmt << "\t" + << "%#7" << radix_fmt << "\t"; + outs() << format(fmt.str().c_str(), total_text, total_data, total_bss); + fmtbuf.clear(); + fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t" + << "%7" PRIx64 "\t"; + outs() << format(fmt.str().c_str(), total, total); + } +} + +/// Checks to see if the @p O ObjectFile is a Mach-O file and if it is and there +/// is a list of architecture flags specified then check to make sure this +/// Mach-O file is one of those architectures or all architectures was +/// specificed. If not then an error is generated and this routine returns +/// false. Else it returns true. +static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) { + auto *MachO = dyn_cast<MachOObjectFile>(O); + + if (!MachO || ArchAll || ArchFlags.empty()) + return true; + + MachO::mach_header H; + MachO::mach_header_64 H_64; + Triple T; + if (MachO->is64Bit()) { + H_64 = MachO->MachOObjectFile::getHeader64(); + T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype); + } else { + H = MachO->MachOObjectFile::getHeader(); + T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype); + } + if (none_of(ArchFlags, [&](const std::string &Name) { + return Name == T.getArchName(); + })) { + error("no architecture specified", Filename); + return false; + } + return true; +} + +/// Print the section sizes for @p file. If @p file is an archive, print the +/// section sizes for each archive member. +static void printFileSectionSizes(StringRef file) { + + // Attempt to open the binary. + Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(file); + if (!BinaryOrErr) { + error(BinaryOrErr.takeError(), file); + return; + } + Binary &Bin = *BinaryOrErr.get().getBinary(); + + if (Archive *a = dyn_cast<Archive>(&Bin)) { + // This is an archive. Iterate over each member and display its sizes. + Error Err = Error::success(); + for (auto &C : a->children(Err)) { + Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); + if (!ChildOrErr) { + if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + error(std::move(E), a->getFileName(), C); + continue; + } + if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) { + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o); + if (!checkMachOAndArchFlags(o, file)) + return; + if (OutputFormat == sysv) + outs() << o->getFileName() << " (ex " << a->getFileName() << "):\n"; + else if (MachO && OutputFormat == darwin) + outs() << a->getFileName() << "(" << o->getFileName() << "):\n"; + printObjectSectionSizes(o); + if (OutputFormat == berkeley) { + if (MachO) + outs() << a->getFileName() << "(" << o->getFileName() << ")\n"; + else + outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n"; + } + } + } + if (Err) + error(std::move(Err), a->getFileName()); + } else if (MachOUniversalBinary *UB = + dyn_cast<MachOUniversalBinary>(&Bin)) { + // If we have a list of architecture flags specified dump only those. + if (!ArchAll && !ArchFlags.empty()) { + // Look for a slice in the universal binary that matches each ArchFlag. + bool ArchFound; + for (unsigned i = 0; i < ArchFlags.size(); ++i) { + ArchFound = false; + for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), + E = UB->end_objects(); + I != E; ++I) { + if (ArchFlags[i] == I->getArchFlagName()) { + ArchFound = true; + Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile(); + if (UO) { + if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) { + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o); + if (OutputFormat == sysv) + outs() << o->getFileName() << " :\n"; + else if (MachO && OutputFormat == darwin) { + if (MoreThanOneFile || ArchFlags.size() > 1) + outs() << o->getFileName() << " (for architecture " + << I->getArchFlagName() << "): \n"; + } + printObjectSectionSizes(o); + if (OutputFormat == berkeley) { + if (!MachO || MoreThanOneFile || ArchFlags.size() > 1) + outs() << o->getFileName() << " (for architecture " + << I->getArchFlagName() << ")"; + outs() << "\n"; + } + } + } else if (auto E = isNotObjectErrorInvalidFileType( + UO.takeError())) { + error(std::move(E), file, ArchFlags.size() > 1 ? + StringRef(I->getArchFlagName()) : StringRef()); + return; + } else if (Expected<std::unique_ptr<Archive>> AOrErr = + I->getAsArchive()) { + std::unique_ptr<Archive> &UA = *AOrErr; + // This is an archive. Iterate over each member and display its + // sizes. + Error Err = Error::success(); + for (auto &C : UA->children(Err)) { + Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); + if (!ChildOrErr) { + if (auto E = isNotObjectErrorInvalidFileType( + ChildOrErr.takeError())) + error(std::move(E), UA->getFileName(), C, + ArchFlags.size() > 1 ? + StringRef(I->getArchFlagName()) : StringRef()); + continue; + } + if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) { + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o); + if (OutputFormat == sysv) + outs() << o->getFileName() << " (ex " << UA->getFileName() + << "):\n"; + else if (MachO && OutputFormat == darwin) + outs() << UA->getFileName() << "(" << o->getFileName() + << ")" + << " (for architecture " << I->getArchFlagName() + << "):\n"; + printObjectSectionSizes(o); + if (OutputFormat == berkeley) { + if (MachO) { + outs() << UA->getFileName() << "(" << o->getFileName() + << ")"; + if (ArchFlags.size() > 1) + outs() << " (for architecture " << I->getArchFlagName() + << ")"; + outs() << "\n"; + } else + outs() << o->getFileName() << " (ex " << UA->getFileName() + << ")\n"; + } + } + } + if (Err) + error(std::move(Err), UA->getFileName()); + } else { + consumeError(AOrErr.takeError()); + error("mach-o universal file for architecture " + + StringRef(I->getArchFlagName()) + + " is not a mach-o file or an archive file", + file); + } + } + } + if (!ArchFound) { + error("file does not contain architecture " + ArchFlags[i], file); + return; + } + } + return; + } + // No architecture flags were specified so if this contains a slice that + // matches the host architecture dump only that. + if (!ArchAll) { + StringRef HostArchName = MachOObjectFile::getHostArch().getArchName(); + for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), + E = UB->end_objects(); + I != E; ++I) { + if (HostArchName == I->getArchFlagName()) { + Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile(); + if (UO) { + if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) { + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o); + if (OutputFormat == sysv) + outs() << o->getFileName() << " :\n"; + else if (MachO && OutputFormat == darwin) { + if (MoreThanOneFile) + outs() << o->getFileName() << " (for architecture " + << I->getArchFlagName() << "):\n"; + } + printObjectSectionSizes(o); + if (OutputFormat == berkeley) { + if (!MachO || MoreThanOneFile) + outs() << o->getFileName() << " (for architecture " + << I->getArchFlagName() << ")"; + outs() << "\n"; + } + } + } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) { + error(std::move(E), file); + return; + } else if (Expected<std::unique_ptr<Archive>> AOrErr = + I->getAsArchive()) { + std::unique_ptr<Archive> &UA = *AOrErr; + // This is an archive. Iterate over each member and display its + // sizes. + Error Err = Error::success(); + for (auto &C : UA->children(Err)) { + Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); + if (!ChildOrErr) { + if (auto E = isNotObjectErrorInvalidFileType( + ChildOrErr.takeError())) + error(std::move(E), UA->getFileName(), C); + continue; + } + if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) { + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o); + if (OutputFormat == sysv) + outs() << o->getFileName() << " (ex " << UA->getFileName() + << "):\n"; + else if (MachO && OutputFormat == darwin) + outs() << UA->getFileName() << "(" << o->getFileName() << ")" + << " (for architecture " << I->getArchFlagName() + << "):\n"; + printObjectSectionSizes(o); + if (OutputFormat == berkeley) { + if (MachO) + outs() << UA->getFileName() << "(" << o->getFileName() + << ")\n"; + else + outs() << o->getFileName() << " (ex " << UA->getFileName() + << ")\n"; + } + } + } + if (Err) + error(std::move(Err), UA->getFileName()); + } else { + consumeError(AOrErr.takeError()); + error("mach-o universal file for architecture " + + StringRef(I->getArchFlagName()) + + " is not a mach-o file or an archive file", + file); + } + return; + } + } + } + // 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>> UO = I->getAsObjectFile(); + if (UO) { + if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) { + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o); + if (OutputFormat == sysv) + outs() << o->getFileName() << " :\n"; + else if (MachO && OutputFormat == darwin) { + if (MoreThanOneFile || MoreThanOneArch) + outs() << o->getFileName() << " (for architecture " + << I->getArchFlagName() << "):"; + outs() << "\n"; + } + printObjectSectionSizes(o); + if (OutputFormat == berkeley) { + if (!MachO || MoreThanOneFile || MoreThanOneArch) + outs() << o->getFileName() << " (for architecture " + << I->getArchFlagName() << ")"; + outs() << "\n"; + } + } + } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) { + error(std::move(E), file, MoreThanOneArch ? + StringRef(I->getArchFlagName()) : StringRef()); + return; + } else if (Expected<std::unique_ptr<Archive>> AOrErr = + I->getAsArchive()) { + std::unique_ptr<Archive> &UA = *AOrErr; + // This is an archive. Iterate over each member and display its sizes. + Error Err = Error::success(); + for (auto &C : UA->children(Err)) { + Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); + if (!ChildOrErr) { + if (auto E = isNotObjectErrorInvalidFileType( + ChildOrErr.takeError())) + error(std::move(E), UA->getFileName(), C, MoreThanOneArch ? + StringRef(I->getArchFlagName()) : StringRef()); + continue; + } + if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) { + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o); + if (OutputFormat == sysv) + outs() << o->getFileName() << " (ex " << UA->getFileName() + << "):\n"; + else if (MachO && OutputFormat == darwin) + outs() << UA->getFileName() << "(" << o->getFileName() << ")" + << " (for architecture " << I->getArchFlagName() << "):\n"; + printObjectSectionSizes(o); + if (OutputFormat == berkeley) { + if (MachO) + outs() << UA->getFileName() << "(" << o->getFileName() << ")" + << " (for architecture " << I->getArchFlagName() + << ")\n"; + else + outs() << o->getFileName() << " (ex " << UA->getFileName() + << ")\n"; + } + } + } + if (Err) + error(std::move(Err), UA->getFileName()); + } else { + consumeError(AOrErr.takeError()); + error("mach-o universal file for architecture " + + StringRef(I->getArchFlagName()) + + " is not a mach-o file or an archive file", + file); + } + } + } else if (ObjectFile *o = dyn_cast<ObjectFile>(&Bin)) { + if (!checkMachOAndArchFlags(o, file)) + return; + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o); + if (OutputFormat == sysv) + outs() << o->getFileName() << " :\n"; + else if (MachO && OutputFormat == darwin && MoreThanOneFile) + outs() << o->getFileName() << ":\n"; + printObjectSectionSizes(o); + if (OutputFormat == berkeley) { + if (!MachO || MoreThanOneFile) + outs() << o->getFileName(); + outs() << "\n"; + } + } else { + error("unsupported file type", file); + } +} + +static void printBerkeleyTotals() { + std::string fmtbuf; + raw_string_ostream fmt(fmtbuf); + const char *radix_fmt = getRadixFmt(); + fmt << "%#7" << radix_fmt << "\t" + << "%#7" << radix_fmt << "\t" + << "%#7" << radix_fmt << "\t"; + outs() << format(fmt.str().c_str(), TotalObjectText, TotalObjectData, + TotalObjectBss); + fmtbuf.clear(); + fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t" + << "%7" PRIx64 "\t"; + outs() << format(fmt.str().c_str(), TotalObjectTotal, TotalObjectTotal) + << "(TOTALS)\n"; +} + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + cl::HideUnrelatedOptions(SizeCat); + cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n"); + + ToolName = argv[0]; + if (OutputFormatShort.getNumOccurrences()) + OutputFormat = static_cast<OutputFormatTy>(OutputFormatShort); + if (RadixShort.getNumOccurrences()) + Radix = RadixShort.getValue(); + + for (StringRef Arch : ArchFlags) { + if (Arch == "all") { + ArchAll = true; + } else { + if (!MachOObjectFile::isValidArch(Arch)) { + outs() << ToolName << ": for the -arch option: Unknown architecture " + << "named '" << Arch << "'"; + return 1; + } + } + } + + if (InputFilenames.empty()) + InputFilenames.push_back("a.out"); + + MoreThanOneFile = InputFilenames.size() > 1; + llvm::for_each(InputFilenames, printFileSectionSizes); + if (OutputFormat == berkeley && TotalSizes) + printBerkeleyTotals(); + + if (HadError) + return 1; +} diff --git a/llvm/tools/llvm-stress/llvm-stress.cpp b/llvm/tools/llvm-stress/llvm-stress.cpp index 5f36a785332b5..22f530dde1670 100644 --- a/llvm/tools/llvm-stress/llvm-stress.cpp +++ b/llvm/tools/llvm-stress/llvm-stress.cpp @@ -300,7 +300,7 @@ protected: if (len != (unsigned)-1) width = len; - return VectorType::get(Ty, width); + return FixedVectorType::get(Ty, width); } /// Pick a random scalar type. @@ -343,7 +343,9 @@ struct LoadModifier: public Modifier { void Act() override { // Try to use predefined pointers. If non-exist, use undef pointer value; Value *Ptr = getRandomPointerValue(); - Value *V = new LoadInst(Ptr, "L", BB->getTerminator()); + PointerType *Tp = cast<PointerType>(Ptr->getType()); + Value *V = new LoadInst(Tp->getElementType(), Ptr, "L", + BB->getTerminator()); PT->push_back(V); } }; @@ -626,9 +628,10 @@ struct SelectModifier: public Modifier { // If the value type is a vector, and we allow vector select, then in 50% // of the cases generate a vector select. - if (Val0->getType()->isVectorTy() && (getRandom() % 1)) { - unsigned NumElem = cast<VectorType>(Val0->getType())->getNumElements(); - CondTy = VectorType::get(CondTy, NumElem); + if (isa<FixedVectorType>(Val0->getType()) && (getRandom() % 1)) { + unsigned NumElem = + cast<FixedVectorType>(Val0->getType())->getNumElements(); + CondTy = FixedVectorType::get(CondTy, NumElem); } Value *Cond = getRandomValue(CondTy); diff --git a/llvm/tools/llvm-strings/llvm-strings.cpp b/llvm/tools/llvm-strings/llvm-strings.cpp new file mode 100644 index 0000000000000..51313d73401e2 --- /dev/null +++ b/llvm/tools/llvm-strings/llvm-strings.cpp @@ -0,0 +1,120 @@ +//===-- llvm-strings.cpp - Printable String dumping utility ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This program is a utility that works like binutils "strings", that is, it +// prints out printable strings in a binary, objdump, or archive file. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/Binary.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Program.h" +#include <cctype> +#include <string> + +using namespace llvm; +using namespace llvm::object; + +static cl::list<std::string> InputFileNames(cl::Positional, + cl::desc("<input object files>"), + cl::ZeroOrMore); + +static cl::opt<bool> + PrintFileName("print-file-name", + cl::desc("Print the name of the file before each string")); +static cl::alias PrintFileNameShort("f", cl::desc(""), + cl::aliasopt(PrintFileName)); + +static cl::opt<int> + MinLength("bytes", cl::desc("Print sequences of the specified length"), + cl::init(4)); +static cl::alias MinLengthShort("n", cl::desc(""), cl::aliasopt(MinLength)); + +static cl::opt<bool> + AllSections("all", + cl::desc("Check all sections, not just the data section")); +static cl::alias AllSectionsShort("a", cl::desc(""), + cl::aliasopt(AllSections)); + +enum radix { none, octal, hexadecimal, decimal }; +static cl::opt<radix> + Radix("radix", cl::desc("print the offset within the file"), + cl::values(clEnumValN(octal, "o", "octal"), + clEnumValN(hexadecimal, "x", "hexadecimal"), + clEnumValN(decimal, "d", "decimal")), + cl::init(none)); +static cl::alias RadixShort("t", cl::desc(""), cl::aliasopt(Radix)); + +static cl::extrahelp + HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); + +static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) { + auto print = [&OS, FileName](unsigned Offset, StringRef L) { + if (L.size() < static_cast<size_t>(MinLength)) + return; + if (PrintFileName) + OS << FileName << ": "; + switch (Radix) { + case none: + break; + case octal: + OS << format("%7o ", Offset); + break; + case hexadecimal: + OS << format("%7x ", Offset); + break; + case decimal: + OS << format("%7u ", Offset); + break; + } + OS << L << '\n'; + }; + + const char *B = Contents.begin(); + const char *P = nullptr, *E = nullptr, *S = nullptr; + for (P = Contents.begin(), E = Contents.end(); P < E; ++P) { + if (isPrint(*P) || *P == '\t') { + if (S == nullptr) + S = P; + } else if (S) { + print(S - B, StringRef(S, P - S)); + S = nullptr; + } + } + if (S) + print(S - B, StringRef(S, E - S)); +} + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv, "llvm string dumper\n"); + if (MinLength == 0) { + errs() << "invalid minimum string length 0\n"; + return EXIT_FAILURE; + } + + if (InputFileNames.empty()) + InputFileNames.push_back("-"); + + for (const auto &File : InputFileNames) { + ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = + MemoryBuffer::getFileOrSTDIN(File); + if (std::error_code EC = Buffer.getError()) + errs() << File << ": " << EC.message() << '\n'; + else + strings(llvm::outs(), File == "-" ? "{standard input}" : File, + Buffer.get()->getMemBufferRef().getBuffer()); + } + + return EXIT_SUCCESS; +} diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp index 96b2b72d8ba11..6a702c64a1053 100644 --- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -71,13 +71,15 @@ static cl::alias ClPrintInliningAliasInlines("inlines", cl::desc("Alias for -inlining"), cl::NotHidden, cl::aliasopt(ClPrintInlining)); -// -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> + ClRelativenames("relativenames", cl::init(false), + cl::desc("Strip the compilation directory from paths")); + static cl::opt<bool> ClDemangle("demangle", cl::init(true), cl::desc("Demangle function names")); static cl::alias @@ -91,7 +93,6 @@ static cl::opt<std::string> ClDefaultArch("default-arch", cl::init(""), cl::desc("Default architecture " "(for multi-arch objects)")); -// -obj, -exe, -e static cl::opt<std::string> ClBinaryName("obj", cl::init(""), cl::desc("Path to object file to be symbolized (if not provided, " @@ -112,7 +113,6 @@ ClDsymHint("dsym-hint", cl::ZeroOrMore, cl::desc("Path to .dSYM bundles to search for debug info for the " "object files")); -// -print-address, -addresses, -a static cl::opt<bool> ClPrintAddress("print-address", cl::init(false), cl::desc("Show address before line information")); @@ -123,7 +123,6 @@ static cl::alias ClPrintAddressAliasA("a", cl::desc("Alias for -print-address"), cl::NotHidden, cl::aliasopt(ClPrintAddress), cl::Grouping); -// -pretty-print, -p static cl::opt<bool> ClPrettyPrint("pretty-print", cl::init(false), cl::desc("Make the output more human friendly")); @@ -138,7 +137,6 @@ 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")); @@ -165,6 +163,10 @@ static cl::opt<DIPrinter::OutputStyle> clEnumValN(DIPrinter::OutputStyle::GNU, "GNU", "GNU addr2line style"))); +static cl::opt<bool> + ClUseNativePDBReader("use-native-pdb-reader", cl::init(0), + cl::desc("Use native PDB functionality")); + static cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); @@ -183,7 +185,7 @@ enum class Command { Frame, }; -static bool parseCommand(StringRef InputString, Command &Cmd, +static bool parseCommand(bool IsAddr2Line, StringRef InputString, Command &Cmd, std::string &ModuleName, uint64_t &ModuleOffset) { const char kDelimiters[] = " \n\r"; ModuleName = ""; @@ -197,38 +199,44 @@ static bool parseCommand(StringRef InputString, Command &Cmd, // If no cmd, assume it's CODE. Cmd = Command::Code; } - const char *pos = InputString.data(); + const char *Pos = InputString.data(); // Skip delimiters and parse input filename (if needed). if (ClBinaryName.empty()) { - pos += strspn(pos, kDelimiters); - if (*pos == '"' || *pos == '\'') { - char quote = *pos; - pos++; - const char *end = strchr(pos, quote); - if (!end) + Pos += strspn(Pos, kDelimiters); + if (*Pos == '"' || *Pos == '\'') { + char Quote = *Pos; + Pos++; + const char *End = strchr(Pos, Quote); + if (!End) return false; - ModuleName = std::string(pos, end - pos); - pos = end + 1; + ModuleName = std::string(Pos, End - Pos); + Pos = End + 1; } else { - int name_length = strcspn(pos, kDelimiters); - ModuleName = std::string(pos, name_length); - pos += name_length; + int NameLength = strcspn(Pos, kDelimiters); + ModuleName = std::string(Pos, NameLength); + Pos += NameLength; } } else { ModuleName = ClBinaryName; } // Skip delimiters and parse module offset. - pos += strspn(pos, kDelimiters); - int offset_length = strcspn(pos, kDelimiters); - return !StringRef(pos, offset_length).getAsInteger(0, ModuleOffset); + Pos += strspn(Pos, kDelimiters); + int OffsetLength = strcspn(Pos, kDelimiters); + StringRef Offset(Pos, OffsetLength); + // GNU addr2line assumes the offset is hexadecimal and allows a redundant + // "0x" or "0X" prefix; do the same for compatibility. + if (IsAddr2Line) + Offset.consume_front("0x") || Offset.consume_front("0X"); + return !Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset); } -static void symbolizeInput(StringRef InputString, LLVMSymbolizer &Symbolizer, - DIPrinter &Printer) { +static void symbolizeInput(bool IsAddr2Line, StringRef InputString, + LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { Command Cmd; std::string ModuleName; uint64_t Offset = 0; - if (!parseCommand(StringRef(InputString), Cmd, ModuleName, Offset)) { + if (!parseCommand(IsAddr2Line, StringRef(InputString), Cmd, ModuleName, + Offset)) { outs() << InputString << "\n"; return; } @@ -309,6 +317,14 @@ int main(int argc, char **argv) { Opts.FallbackDebugPath = ClFallbackDebugPath; Opts.DWPName = ClDwpName; Opts.DebugFileDirectory = ClDebugFileDirectory; + Opts.UseNativePDBReader = ClUseNativePDBReader; + Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath; + // If both --basenames and --relativenames are specified then pick the last + // one. + if (ClBasenames.getPosition() > ClRelativenames.getPosition()) + Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly; + else if (ClRelativenames) + Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath; for (const auto &hint : ClDsymHint) { if (sys::path::extension(hint) == ".dSYM") { @@ -322,7 +338,7 @@ int main(int argc, char **argv) { DIPrinter Printer(outs(), ClPrintFunctions != FunctionNameKind::None, ClPrettyPrint, ClPrintSourceContextLines, ClVerbose, - ClBasenames, ClOutputStyle); + ClOutputStyle); if (ClInputAddresses.empty()) { const int kMaxInputStringLength = 1024; @@ -335,12 +351,12 @@ int main(int argc, char **argv) { std::remove_if(StrippedInputString.begin(), StrippedInputString.end(), [](char c) { return c == '\r' || c == '\n'; }), StrippedInputString.end()); - symbolizeInput(StrippedInputString, Symbolizer, Printer); + symbolizeInput(IsAddr2Line, StrippedInputString, Symbolizer, Printer); outs().flush(); } } else { for (StringRef Address : ClInputAddresses) - symbolizeInput(Address, Symbolizer, Printer); + symbolizeInput(IsAddr2Line, Address, Symbolizer, Printer); } return 0; diff --git a/llvm/tools/llvm-xray/trie-node.h b/llvm/tools/llvm-xray/trie-node.h index 47d4b8f1e78c8..7bff81473b5df 100644 --- a/llvm/tools/llvm-xray/trie-node.h +++ b/llvm/tools/llvm-xray/trie-node.h @@ -48,7 +48,7 @@ template <typename T, typename Callable> TrieNode<T> * mergeTrieNodes(const TrieNode<T> &Left, const TrieNode<T> &Right, /*Non-deduced pointer type for nullptr compatibility*/ - typename std::remove_reference<TrieNode<T> *>::type NewParent, + std::remove_reference_t<TrieNode<T> *> NewParent, std::forward_list<TrieNode<T>> &NodeStore, Callable &&MergeCallable) { llvm::function_ref<T(const T &, const T &)> MergeFn( diff --git a/llvm/tools/llvm-xray/xray-color-helper.cpp b/llvm/tools/llvm-xray/xray-color-helper.cpp index c09cad3ba7d24..ea7ff357826bf 100644 --- a/llvm/tools/llvm-xray/xray-color-helper.cpp +++ b/llvm/tools/llvm-xray/xray-color-helper.cpp @@ -208,8 +208,8 @@ ColorHelper::getColorTuple(double Point) const { // string. std::string ColorHelper::getColorString(std::tuple<uint8_t, uint8_t, uint8_t> t) { - return llvm::formatv("#{0:X-2}{1:X-2}{2:X-2}", std::get<0>(t), std::get<1>(t), - std::get<2>(t)); + return std::string(llvm::formatv("#{0:X-2}{1:X-2}{2:X-2}", std::get<0>(t), + std::get<1>(t), std::get<2>(t))); } // Gets a color in a gradient given a number in the interval [0,1], it does this diff --git a/llvm/tools/llvm-xray/xray-extract.cpp b/llvm/tools/llvm-xray/xray-extract.cpp index af9255af21c35..8304d2d27afa3 100644 --- a/llvm/tools/llvm-xray/xray-extract.cpp +++ b/llvm/tools/llvm-xray/xray-extract.cpp @@ -45,6 +45,11 @@ static cl::opt<bool> ExtractSymbolize("symbolize", cl::value_desc("symbolize"), cl::sub(Extract)); static cl::alias ExtractSymbolize2("s", cl::aliasopt(ExtractSymbolize), cl::desc("alias for -symbolize")); +static cl::opt<bool> ExtractNoDemangle("no-demangle", + cl::value_desc("no-demangle"), + cl::init(false), + cl::desc("don't demangle symbols"), + cl::sub(Extract)); namespace { @@ -58,9 +63,9 @@ void exportAsYAML(const InstrumentationMap &Map, raw_ostream &OS, auto FuncId = Map.getFunctionId(Sled.Function); if (!FuncId) return; - YAMLSleds.push_back({*FuncId, Sled.Address, Sled.Function, Sled.Kind, - Sled.AlwaysInstrument, - ExtractSymbolize ? FH.SymbolOrNumber(*FuncId) : ""}); + YAMLSleds.push_back( + {*FuncId, Sled.Address, Sled.Function, Sled.Kind, Sled.AlwaysInstrument, + ExtractSymbolize ? FH.SymbolOrNumber(*FuncId) : "", Sled.Version}); } Output Out(OS, nullptr, 0); Out << YAMLSleds; @@ -84,7 +89,10 @@ static CommandRegistration Unused(&Extract, []() -> Error { Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC); const auto &FunctionAddresses = InstrumentationMapOrError->getFunctionAddresses(); - symbolize::LLVMSymbolizer Symbolizer; + symbolize::LLVMSymbolizer::Options opts; + if (ExtractNoDemangle) + opts.Demangle = false; + symbolize::LLVMSymbolizer Symbolizer(opts); llvm::xray::FuncIdConversionHelper FuncIdHelper(ExtractInput, Symbolizer, FunctionAddresses); exportAsYAML(*InstrumentationMapOrError, OS, FuncIdHelper); diff --git a/llvm/tools/llvm-xray/xray-graph-diff.cpp b/llvm/tools/llvm-xray/xray-graph-diff.cpp index a1bca326930e5..11210e2004a7a 100644 --- a/llvm/tools/llvm-xray/xray-graph-diff.cpp +++ b/llvm/tools/llvm-xray/xray-graph-diff.cpp @@ -314,7 +314,7 @@ static std::string getLabel(const GraphDiffRenderer::GraphT::EdgeValueType &E, const auto &RightStat = EdgeAttr.CorrEdgePtr[1]->second.S; double RelDiff = statRelDiff(LeftStat, RightStat, EL); - return formatv(R"({0:P})", RelDiff); + return std::string(formatv(R"({0:P})", RelDiff)); } } @@ -324,17 +324,19 @@ static std::string getLabel(const GraphDiffRenderer::GraphT::VertexValueType &V, const auto &VertexAttr = V.second; switch (VL) { case GraphDiffRenderer::StatType::NONE: - return formatv(R"({0})", truncateString(VertexId, TrunLen).str()); + return std::string( + formatv(R"({0})", truncateString(VertexId, TrunLen).str())); default: if (containsNullptr(VertexAttr.CorrVertexPtr)) - return formatv(R"({0})", truncateString(VertexId, TrunLen).str()); + return std::string( + formatv(R"({0})", truncateString(VertexId, TrunLen).str())); const auto &LeftStat = VertexAttr.CorrVertexPtr[0]->second.S; const auto &RightStat = VertexAttr.CorrVertexPtr[1]->second.S; double RelDiff = statRelDiff(LeftStat, RightStat, VL); - return formatv(R"({{{0}|{1:P}})", truncateString(VertexId, TrunLen).str(), - RelDiff); + return std::string(formatv( + R"({{{0}|{1:P}})", truncateString(VertexId, TrunLen).str(), RelDiff)); } } diff --git a/llvm/tools/llvm-xray/xray-graph.cpp b/llvm/tools/llvm-xray/xray-graph.cpp index f836f9ba54fce..522609b938f2d 100644 --- a/llvm/tools/llvm-xray/xray-graph.cpp +++ b/llvm/tools/llvm-xray/xray-graph.cpp @@ -163,6 +163,30 @@ static void updateStat(GraphRenderer::TimeStat &S, int64_t L) { S.Sum += L; } +// Labels in a DOT graph must be legal XML strings so it's necessary to escape +// certain characters. +static std::string escapeString(StringRef Label) { + std::string Str; + Str.reserve(Label.size()); + for (const auto C : Label) { + switch (C) { + case '&': + Str.append("&"); + break; + case '<': + Str.append("<"); + break; + case '>': + Str.append(">"); + break; + default: + Str.push_back(C); + break; + } + } + return Str; +} + // Evaluates an XRay record and performs accounting on it. // // If the record is an ENTER record it pushes the FuncID and TSC onto a @@ -398,8 +422,9 @@ void GraphRenderer::exportGraphAsDOT(raw_ostream &OS, StatType ET, StatType EC, if (V.first == 0) continue; OS << "F" << V.first << " [label=\"" << (VT != StatType::NONE ? "{" : "") - << (VA.SymbolName.size() > 40 ? VA.SymbolName.substr(0, 40) + "..." - : VA.SymbolName); + << escapeString(VA.SymbolName.size() > 40 + ? VA.SymbolName.substr(0, 40) + "..." + : VA.SymbolName); if (VT != StatType::NONE) OS << "|" << VA.S.getString(VT) << "}\""; else diff --git a/llvm/tools/llvm-xray/xray-stacks.cpp b/llvm/tools/llvm-xray/xray-stacks.cpp index cf292887b6b8a..1e4490289534d 100644 --- a/llvm/tools/llvm-xray/xray-stacks.cpp +++ b/llvm/tools/llvm-xray/xray-stacks.cpp @@ -674,11 +674,12 @@ std::string CreateErrorMessage(StackTrie::AccountRecordStatus Error, const FuncIdConversionHelper &Converter) { switch (Error) { case StackTrie::AccountRecordStatus::ENTRY_NOT_FOUND: - return formatv("Found record {0} with no matching function entry\n", - format_xray_record(Record, Converter)); + return std::string( + formatv("Found record {0} with no matching function entry\n", + format_xray_record(Record, Converter))); default: - return formatv("Unknown error type for record {0}\n", - format_xray_record(Record, Converter)); + return std::string(formatv("Unknown error type for record {0}\n", + format_xray_record(Record, Converter))); } } diff --git a/llvm/tools/opt/AnalysisWrappers.cpp b/llvm/tools/opt/AnalysisWrappers.cpp index b888605a516c7..2ae1da84a9a0f 100644 --- a/llvm/tools/opt/AnalysisWrappers.cpp +++ b/llvm/tools/opt/AnalysisWrappers.cpp @@ -17,7 +17,6 @@ //===----------------------------------------------------------------------===// #include "llvm/Analysis/CallGraph.h" -#include "llvm/IR/CallSite.h" #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Support/raw_ostream.h" @@ -40,11 +39,11 @@ namespace { Instruction *UI = dyn_cast<Instruction>(U); if (!UI) continue; - CallSite CS(cast<Value>(UI)); - if (!CS) continue; + CallBase *CB = dyn_cast<CallBase>(UI); + if (!CB) + continue; - for (CallSite::arg_iterator AI = CS.arg_begin(), - E = CS.arg_end(); AI != E; ++AI) { + for (auto AI = CB->arg_begin(), E = CB->arg_end(); AI != E; ++AI) { if (!isa<Constant>(*AI)) continue; if (!PrintedFn) { diff --git a/llvm/tools/opt/NewPMDriver.cpp b/llvm/tools/opt/NewPMDriver.cpp index ac04a32d93fd5..b94c58decdda2 100644 --- a/llvm/tools/opt/NewPMDriver.cpp +++ b/llvm/tools/opt/NewPMDriver.cpp @@ -14,6 +14,7 @@ #include "NewPMDriver.h" #include "PassPrinters.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/CGSCCPassManager.h" @@ -100,6 +101,9 @@ static cl::opt<std::string> OptimizerLastEPPipeline( "the OptimizerLast extension point into default pipelines"), cl::Hidden); +// Individual pipeline tuning options. +extern cl::opt<bool> DisableLoopUnrolling; + extern cl::opt<PGOKind> PGOKindFlag; extern cl::opt<std::string> ProfileFile; extern cl::opt<CSPGOKind> CSPGOKindFlag; @@ -194,7 +198,7 @@ static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass, }); if (tryParsePipelineText<FunctionPassManager>(PB, OptimizerLastEPPipeline)) PB.registerOptimizerLastEPCallback( - [&PB, VerifyEachPass, DebugLogging](FunctionPassManager &PM, + [&PB, VerifyEachPass, DebugLogging](ModulePassManager &PM, PassBuilder::OptimizationLevel) { ExitOnError Err("Unable to parse OptimizerLastEP pipeline: "); Err(PB.parsePassPipeline(PM, OptimizerLastEPPipeline, VerifyEachPass, @@ -209,57 +213,63 @@ static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass, bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, ToolOutputFile *Out, ToolOutputFile *ThinLTOLinkOut, ToolOutputFile *OptRemarkFile, - StringRef PassPipeline, OutputKind OK, - VerifierKind VK, + StringRef PassPipeline, ArrayRef<StringRef> Passes, + OutputKind OK, VerifierKind VK, bool ShouldPreserveAssemblyUseListOrder, bool ShouldPreserveBitcodeUseListOrder, bool EmitSummaryIndex, bool EmitModuleHash, - bool EnableDebugify) { + bool EnableDebugify, bool Coroutines) { bool VerifyEachPass = VK == VK_VerifyEachPass; Optional<PGOOptions> P; switch (PGOKindFlag) { - case InstrGen: - P = PGOOptions(ProfileFile, "", "", PGOOptions::IRInstr); - break; - case InstrUse: - P = PGOOptions(ProfileFile, "", ProfileRemappingFile, PGOOptions::IRUse); - break; - case SampleUse: - P = PGOOptions(ProfileFile, "", ProfileRemappingFile, - PGOOptions::SampleUse); - break; - case NoPGO: - if (DebugInfoForProfiling) - 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; - } + case InstrGen: + P = PGOOptions(ProfileFile, "", "", PGOOptions::IRInstr); + break; + case InstrUse: + P = PGOOptions(ProfileFile, "", ProfileRemappingFile, PGOOptions::IRUse); + break; + case SampleUse: + P = PGOOptions(ProfileFile, "", ProfileRemappingFile, + PGOOptions::SampleUse); + break; + case NoPGO: + if (DebugInfoForProfiling) + 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, PipelineTuningOptions(), P, &PIC); + PipelineTuningOptions PTO; + // LoopUnrolling defaults on to true and DisableLoopUnrolling is initialized + // to false above so we shouldn't necessarily need to check whether or not the + // option has been enabled. + PTO.LoopUnrolling = !DisableLoopUnrolling; + PTO.Coroutines = Coroutines; + PassBuilder PB(TM, PTO, P, &PIC); registerEPCallbacks(PB, VerifyEachPass, DebugPM); // Load requested pass plugins and let them register pass builder callbacks @@ -295,9 +305,26 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, // Specially handle the alias analysis manager so that we can register // a custom pipeline of AA passes with it. AAManager AA; - if (auto Err = PB.parseAAPipeline(AA, AAPipeline)) { - errs() << Arg0 << ": " << toString(std::move(Err)) << "\n"; - return false; + if (!AAPipeline.empty()) { + assert(Passes.empty() && + "--aa-pipeline and -foo-pass should not both be specified"); + if (auto Err = PB.parseAAPipeline(AA, AAPipeline)) { + errs() << Arg0 << ": " << toString(std::move(Err)) << "\n"; + return false; + } + } + // For compatibility with legacy pass manager. + // Alias analyses are not specially specified when using the legacy PM. + SmallVector<StringRef, 4> NonAAPasses; + for (auto PassName : Passes) { + if (PB.isAAPassName(PassName)) { + if (auto Err = PB.parseAAPipeline(AA, PassName)) { + errs() << Arg0 << ": " << toString(std::move(Err)) << "\n"; + return false; + } + } else { + NonAAPasses.push_back(PassName); + } } LoopAnalysisManager LAM(DebugPM); @@ -321,10 +348,24 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, if (EnableDebugify) MPM.addPass(NewPMDebugifyPass()); - if (auto Err = - PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) { - errs() << Arg0 << ": " << toString(std::move(Err)) << "\n"; - return false; + if (!PassPipeline.empty()) { + assert(Passes.empty() && + "PassPipeline and Passes should not both contain passes"); + if (auto Err = + PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) { + errs() << Arg0 << ": " << toString(std::move(Err)) << "\n"; + return false; + } + } + for (auto PassName : NonAAPasses) { + std::string ModifiedPassName(PassName.begin(), PassName.end()); + if (PB.isAnalysisPassName(PassName)) + ModifiedPassName = "require<" + ModifiedPassName + ">"; + if (auto Err = PB.parsePassPipeline(MPM, ModifiedPassName, VerifyEachPass, + DebugPM)) { + errs() << Arg0 << ": " << toString(std::move(Err)) << "\n"; + return false; + } } if (VK > VK_NoVerifier) diff --git a/llvm/tools/opt/NewPMDriver.h b/llvm/tools/opt/NewPMDriver.h index b672c97c9aa35..7ae273a2c1f46 100644 --- a/llvm/tools/opt/NewPMDriver.h +++ b/llvm/tools/opt/NewPMDriver.h @@ -20,9 +20,10 @@ #ifndef LLVM_TOOLS_OPT_NEWPMDRIVER_H #define LLVM_TOOLS_OPT_NEWPMDRIVER_H +#include "llvm/ADT/ArrayRef.h" + namespace llvm { class StringRef; -class LLVMContext; class Module; class TargetMachine; class ToolOutputFile; @@ -60,11 +61,12 @@ enum CSPGOKind { NoCSPGO, CSInstrGen, CSInstrUse }; bool runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, ToolOutputFile *Out, ToolOutputFile *ThinLinkOut, ToolOutputFile *OptRemarkFile, StringRef PassPipeline, - opt_tool::OutputKind OK, opt_tool::VerifierKind VK, + ArrayRef<StringRef> PassInfos, opt_tool::OutputKind OK, + opt_tool::VerifierKind VK, bool ShouldPreserveAssemblyUseListOrder, bool ShouldPreserveBitcodeUseListOrder, bool EmitSummaryIndex, bool EmitModuleHash, - bool EnableDebugify); + bool EnableDebugify, bool Coroutines); } // namespace llvm #endif diff --git a/llvm/tools/opt/PassPrinters.cpp b/llvm/tools/opt/PassPrinters.cpp index a877d9dc90f48..4e81b5d29c4d6 100644 --- a/llvm/tools/opt/PassPrinters.cpp +++ b/llvm/tools/opt/PassPrinters.cpp @@ -33,18 +33,16 @@ struct FunctionPassPrinter : public FunctionPass { raw_ostream &Out; static char ID; std::string PassName; - bool QuietPass; - FunctionPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet) - : FunctionPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) { - std::string PassToPrintName = PassToPrint->getPassName(); + FunctionPassPrinter(const PassInfo *PI, raw_ostream &out) + : FunctionPass(ID), PassToPrint(PI), Out(out) { + std::string PassToPrintName = std::string(PassToPrint->getPassName()); PassName = "FunctionPass Printer: " + PassToPrintName; } bool runOnFunction(Function &F) override { - if (!QuietPass) - Out << "Printing analysis '" << PassToPrint->getPassName() - << "' for function '" << F.getName() << "':\n"; + Out << "Printing analysis '" << PassToPrint->getPassName() + << "' for function '" << F.getName() << "':\n"; // Get and print pass... getAnalysisID<Pass>(PassToPrint->getTypeInfo()).print(Out, F.getParent()); @@ -66,17 +64,15 @@ struct CallGraphSCCPassPrinter : public CallGraphSCCPass { const PassInfo *PassToPrint; raw_ostream &Out; std::string PassName; - bool QuietPass; - CallGraphSCCPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet) - : CallGraphSCCPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) { - std::string PassToPrintName = PassToPrint->getPassName(); + CallGraphSCCPassPrinter(const PassInfo *PI, raw_ostream &out) + : CallGraphSCCPass(ID), PassToPrint(PI), Out(out) { + std::string PassToPrintName = std::string(PassToPrint->getPassName()); PassName = "CallGraphSCCPass Printer: " + PassToPrintName; } bool runOnSCC(CallGraphSCC &SCC) override { - if (!QuietPass) - Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; + Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; // Get and print pass... for (CallGraphSCC::iterator I = SCC.begin(), E = SCC.end(); I != E; ++I) { @@ -103,17 +99,15 @@ struct ModulePassPrinter : public ModulePass { const PassInfo *PassToPrint; raw_ostream &Out; std::string PassName; - bool QuietPass; - ModulePassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet) - : ModulePass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) { - std::string PassToPrintName = PassToPrint->getPassName(); + ModulePassPrinter(const PassInfo *PI, raw_ostream &out) + : ModulePass(ID), PassToPrint(PI), Out(out) { + std::string PassToPrintName = std::string(PassToPrint->getPassName()); PassName = "ModulePass Printer: " + PassToPrintName; } bool runOnModule(Module &M) override { - if (!QuietPass) - Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; + Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; // Get and print pass... getAnalysisID<Pass>(PassToPrint->getTypeInfo()).print(Out, &M); @@ -135,17 +129,15 @@ struct LoopPassPrinter : public LoopPass { const PassInfo *PassToPrint; raw_ostream &Out; std::string PassName; - bool QuietPass; - LoopPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet) - : LoopPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) { - std::string PassToPrintName = PassToPrint->getPassName(); + LoopPassPrinter(const PassInfo *PI, raw_ostream &out) + : LoopPass(ID), PassToPrint(PI), Out(out) { + std::string PassToPrintName = std::string(PassToPrint->getPassName()); PassName = "LoopPass Printer: " + PassToPrintName; } bool runOnLoop(Loop *L, LPPassManager &LPM) override { - if (!QuietPass) - Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; + Out << "Printing analysis '" << PassToPrint->getPassName() << "':\n"; // Get and print pass... getAnalysisID<Pass>(PassToPrint->getTypeInfo()) @@ -168,20 +160,17 @@ struct RegionPassPrinter : public RegionPass { const PassInfo *PassToPrint; raw_ostream &Out; std::string PassName; - bool QuietPass; - RegionPassPrinter(const PassInfo *PI, raw_ostream &out, bool Quiet) - : RegionPass(ID), PassToPrint(PI), Out(out), QuietPass(Quiet) { - std::string PassToPrintName = PassToPrint->getPassName(); + RegionPassPrinter(const PassInfo *PI, raw_ostream &out) + : RegionPass(ID), PassToPrint(PI), Out(out) { + std::string PassToPrintName = std::string(PassToPrint->getPassName()); PassName = "RegionPass Printer: " + PassToPrintName; } bool runOnRegion(Region *R, RGPassManager &RGM) override { - if (!QuietPass) { - Out << "Printing analysis '" << PassToPrint->getPassName() << "' for " - << "region: '" << R->getNameStr() << "' in function '" - << R->getEntry()->getParent()->getName() << "':\n"; - } + Out << "Printing analysis '" << PassToPrint->getPassName() << "' for " + << "region: '" << R->getNameStr() << "' in function '" + << R->getEntry()->getParent()->getName() << "':\n"; // Get and print pass... getAnalysisID<Pass>(PassToPrint->getTypeInfo()) .print(Out, R->getEntry()->getParent()->getParent()); @@ -201,28 +190,23 @@ char RegionPassPrinter::ID = 0; } // end anonymous namespace FunctionPass *llvm::createFunctionPassPrinter(const PassInfo *PI, - raw_ostream &OS, bool Quiet) { - return new FunctionPassPrinter(PI, OS, Quiet); + raw_ostream &OS) { + return new FunctionPassPrinter(PI, OS); } CallGraphSCCPass *llvm::createCallGraphPassPrinter(const PassInfo *PI, - raw_ostream &OS, - bool Quiet) { - return new CallGraphSCCPassPrinter(PI, OS, Quiet); + raw_ostream &OS) { + return new CallGraphSCCPassPrinter(PI, OS); } -ModulePass *llvm::createModulePassPrinter(const PassInfo *PI, raw_ostream &OS, - bool Quiet) { - return new ModulePassPrinter(PI, OS, Quiet); +ModulePass *llvm::createModulePassPrinter(const PassInfo *PI, raw_ostream &OS) { + return new ModulePassPrinter(PI, OS); } -LoopPass *llvm::createLoopPassPrinter(const PassInfo *PI, raw_ostream &OS, - bool Quiet) { - return new LoopPassPrinter(PI, OS, Quiet); +LoopPass *llvm::createLoopPassPrinter(const PassInfo *PI, raw_ostream &OS) { + return new LoopPassPrinter(PI, OS); } -RegionPass *llvm::createRegionPassPrinter(const PassInfo *PI, raw_ostream &OS, - bool Quiet) { - return new RegionPassPrinter(PI, OS, Quiet); +RegionPass *llvm::createRegionPassPrinter(const PassInfo *PI, raw_ostream &OS) { + return new RegionPassPrinter(PI, OS); } - diff --git a/llvm/tools/opt/PassPrinters.h b/llvm/tools/opt/PassPrinters.h index 692befbdae758..a4e1921399fc6 100644 --- a/llvm/tools/opt/PassPrinters.h +++ b/llvm/tools/opt/PassPrinters.h @@ -14,8 +14,6 @@ #ifndef LLVM_TOOLS_OPT_PASSPRINTERS_H #define LLVM_TOOLS_OPT_PASSPRINTERS_H -#include "llvm/IR/PassManager.h" - namespace llvm { class CallGraphSCCPass; @@ -25,22 +23,17 @@ class LoopPass; class PassInfo; class raw_ostream; class RegionPass; -class Module; -FunctionPass *createFunctionPassPrinter(const PassInfo *PI, raw_ostream &out, - bool Quiet); +FunctionPass *createFunctionPassPrinter(const PassInfo *PI, raw_ostream &out); CallGraphSCCPass *createCallGraphPassPrinter(const PassInfo *PI, - raw_ostream &out, bool Quiet); + raw_ostream &out); -ModulePass *createModulePassPrinter(const PassInfo *PI, raw_ostream &out, - bool Quiet); +ModulePass *createModulePassPrinter(const PassInfo *PI, raw_ostream &out); -LoopPass *createLoopPassPrinter(const PassInfo *PI, raw_ostream &out, - bool Quiet); +LoopPass *createLoopPassPrinter(const PassInfo *PI, raw_ostream &out); -RegionPass *createRegionPassPrinter(const PassInfo *PI, raw_ostream &out, - bool Quiet); +RegionPass *createRegionPassPrinter(const PassInfo *PI, raw_ostream &out); } // end namespace llvm diff --git a/llvm/tools/opt/PrintSCC.cpp b/llvm/tools/opt/PrintSCC.cpp index 419886d6cc60a..1ca52745ff400 100644 --- a/llvm/tools/opt/PrintSCC.cpp +++ b/llvm/tools/opt/PrintSCC.cpp @@ -76,10 +76,11 @@ bool CFGSCC::runOnFunction(Function &F) { for (scc_iterator<Function*> SCCI = scc_begin(&F); !SCCI.isAtEnd(); ++SCCI) { const std::vector<BasicBlock *> &nextSCC = *SCCI; errs() << "\nSCC #" << ++sccNum << " : "; - for (std::vector<BasicBlock*>::const_iterator I = nextSCC.begin(), - E = nextSCC.end(); I != E; ++I) - errs() << (*I)->getName() << ", "; - if (nextSCC.size() == 1 && SCCI.hasLoop()) + for (BasicBlock *BB : nextSCC) { + BB->printAsOperand(errs(), false); + errs() << ", "; + } + if (nextSCC.size() == 1 && SCCI.hasCycle()) errs() << " (Has self-loop)."; } errs() << "\n"; @@ -101,7 +102,7 @@ bool CallGraphSCC::runOnModule(Module &M) { E = nextSCC.end(); I != E; ++I) errs() << ((*I)->getFunction() ? (*I)->getFunction()->getName() : "external node") << ", "; - if (nextSCC.size() == 1 && SCCI.hasLoop()) + if (nextSCC.size() == 1 && SCCI.hasCycle()) errs() << " (Has self-loop)."; } errs() << "\n"; diff --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp index 75a6cdc3892b3..c250eefb8c430 100644 --- a/llvm/tools/opt/opt.cpp +++ b/llvm/tools/opt/opt.cpp @@ -21,18 +21,19 @@ #include "llvm/Analysis/RegionPass.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/AsmParser/Parser.h" #include "llvm/Bitcode/BitcodeWriterPass.h" -#include "llvm/CodeGen/CommandFlags.inc" +#include "llvm/CodeGen/CommandFlags.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/Config/llvm-config.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LLVMRemarkStreamer.h" #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" @@ -54,6 +55,7 @@ #include "llvm/Transforms/Coroutines.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/IPO/WholeProgramDevirt.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/Debugify.h" #include <algorithm> @@ -61,12 +63,17 @@ using namespace llvm; using namespace opt_tool; +static codegen::RegisterCodeGenFlags CFG; + // The OptimizationList is automatically populated with registered Passes by the // PassNameParser. // static cl::list<const PassInfo*, bool, PassNameParser> PassList(cl::desc("Optimizations available:")); +static cl::opt<bool> EnableNewPassManager( + "enable-new-pm", cl::desc("Enable the new pass manager"), cl::init(false)); + // This flag specifies a textual description of the optimization pass pipeline // to run over the module. This flag switches opt to use the new pass manager // infrastructure, completely disabling all of the flags specific to the old @@ -115,8 +122,12 @@ static cl::opt<std::string> ThinLinkBitcodeFile( static cl::opt<bool> NoVerify("disable-verify", cl::desc("Do not run the verifier"), cl::Hidden); -static cl::opt<bool> -VerifyEach("verify-each", cl::desc("Verify after each transform")); +static cl::opt<bool> NoUpgradeDebugInfo("disable-upgrade-debug-info", + cl::desc("Generate invalid output"), + cl::ReallyHidden); + +static cl::opt<bool> VerifyEach("verify-each", + cl::desc("Verify after each transform")); static cl::opt<bool> DisableDITypeMap("disable-debug-info-type-map", @@ -172,15 +183,9 @@ CodeGenOptLevel("codegen-opt-level", static cl::opt<std::string> TargetTriple("mtriple", cl::desc("Override target triple for module")); -static cl::opt<bool> -DisableLoopUnrolling("disable-loop-unrolling", - cl::desc("Disable loop unrolling in all relevant passes"), - cl::init(false)); - -static cl::opt<bool> -DisableSLPVectorization("disable-slp-vectorization", - cl::desc("Disable the slp vectorization pass"), - cl::init(false)); +cl::opt<bool> DisableLoopUnrolling( + "disable-loop-unrolling", + cl::desc("Disable loop unrolling in all relevant passes"), cl::init(false)); static cl::opt<bool> EmitSummaryIndex("module-summary", cl::desc("Emit module summary index"), @@ -198,13 +203,6 @@ DisableBuiltins("disable-builtin", cl::desc("Disable specific target library builtin function"), cl::ZeroOrMore); - -static cl::opt<bool> -Quiet("q", cl::desc("Obsolete option"), cl::Hidden); - -static cl::alias -QuietA("quiet", cl::desc("Alias for -q"), cl::aliasopt(Quiet)); - static cl::opt<bool> AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization")); @@ -257,6 +255,20 @@ static cl::opt<bool> Coroutines( cl::desc("Enable coroutine passes."), cl::init(false), cl::Hidden); +static cl::opt<bool> TimeTrace( + "time-trace", + cl::desc("Record time trace")); + +static cl::opt<unsigned> TimeTraceGranularity( + "time-trace-granularity", + cl::desc("Minimum time granularity (in microseconds) traced by time profiler"), + cl::init(500), cl::Hidden); + +static cl::opt<std::string> + TimeTraceFile("time-trace-file", + cl::desc("Specify time trace file destination"), + cl::value_desc("filename")); + static cl::opt<bool> RemarksWithHotness( "pass-remarks-with-hotness", cl::desc("With PGO, include profile count in optimization remarks"), @@ -389,18 +401,9 @@ static void AddOptimizationPasses(legacy::PassManagerBase &MPM, Builder.DisableUnrollLoops = (DisableLoopUnrolling.getNumOccurrences() > 0) ? DisableLoopUnrolling : OptLevel == 0; - // 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; + Builder.LoopVectorize = OptLevel > 1 && SizeLevel < 2; - // When #pragma vectorize is on for SLP, do the same as above - Builder.SLPVectorize = - DisableSLPVectorization ? false : OptLevel > 1 && SizeLevel < 2; + Builder.SLPVectorize = OptLevel > 1 && SizeLevel < 2; if (TM) TM->adjustPassManager(Builder); @@ -470,16 +473,17 @@ static TargetMachine* GetTargetMachine(Triple TheTriple, StringRef CPUStr, StringRef FeaturesStr, const TargetOptions &Options) { std::string Error; - const Target *TheTarget = TargetRegistry::lookupTarget(MArch, TheTriple, - Error); + const Target *TheTarget = + TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error); // Some modules don't specify a triple, and this is okay. if (!TheTarget) { return nullptr; } - return TheTarget->createTargetMachine(TheTriple.getTriple(), CPUStr, - FeaturesStr, Options, getRelocModel(), - getCodeModel(), GetCodeGenOptLevel()); + return TheTarget->createTargetMachine( + TheTriple.getTriple(), codegen::getCPUStr(), codegen::getFeaturesStr(), + Options, codegen::getExplicitRelocModel(), + codegen::getExplicitCodeModel(), GetCodeGenOptLevel()); } #ifdef BUILD_EXAMPLES @@ -508,6 +512,24 @@ void exportDebugifyStats(llvm::StringRef Path, const DebugifyStatsMap &Map) { } } +struct TimeTracerRAII { + TimeTracerRAII(StringRef ProgramName) { + if (TimeTrace) + timeTraceProfilerInitialize(TimeTraceGranularity, ProgramName); + } + ~TimeTracerRAII() { + if (TimeTrace) { + if (auto E = timeTraceProfilerWrite(TimeTraceFile, OutputFilename)) { + handleAllErrors(std::move(E), [&](const StringError &SE) { + errs() << SE.getMessage() << "\n"; + }); + return; + } + timeTraceProfilerCleanup(); + } + } +}; + //===----------------------------------------------------------------------===// // main for opt // @@ -575,6 +597,8 @@ int main(int argc, char **argv) { return 1; } + TimeTracerRAII TimeTracer(argv[0]); + SMDiagnostic Err; Context.setDiscardValueNames(DiscardValueNames); @@ -582,9 +606,9 @@ int main(int argc, char **argv) { Context.enableDebugTypeODRUniquing(); Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr = - setupOptimizationRemarks(Context, RemarksFilename, RemarksPasses, - RemarksFormat, RemarksWithHotness, - RemarksHotnessThreshold); + setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses, + RemarksFormat, RemarksWithHotness, + RemarksHotnessThreshold); if (Error E = RemarksFileOrErr.takeError()) { errs() << toString(std::move(E)) << '\n'; return 1; @@ -592,8 +616,18 @@ int main(int argc, char **argv) { std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr); // Load the input module... - std::unique_ptr<Module> M = - parseIRFile(InputFilename, Err, Context, !NoVerify, ClDataLayout); + auto SetDataLayout = [](StringRef) -> Optional<std::string> { + if (ClDataLayout.empty()) + return None; + return ClDataLayout; + }; + std::unique_ptr<Module> M; + if (NoUpgradeDebugInfo) + M = parseAssemblyFileWithIndexNoUpgradeDebugInfo( + InputFilename, Err, Context, nullptr, SetDataLayout) + .Mod; + else + M = parseIRFile(InputFilename, Err, Context, SetDataLayout); if (!M) { Err.print(argv[0], errs()); @@ -625,6 +659,13 @@ int main(int argc, char **argv) { return 1; } + // Enable testing of whole program devirtualization on this module by invoking + // the facility for updating public visibility to linkage unit visibility when + // specified by an internal option. This is normally done during LTO which is + // not performed via opt. + updateVCallVisibilityInModule(*M, + /* WholeProgramVisibilityEnabledInLTO */ false); + // Figure out what stream we are supposed to write to... std::unique_ptr<ToolOutputFile> Out; std::unique_ptr<ToolOutputFile> ThinLinkOut; @@ -659,11 +700,11 @@ int main(int argc, char **argv) { Triple ModuleTriple(M->getTargetTriple()); std::string CPUStr, FeaturesStr; TargetMachine *Machine = nullptr; - const TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + const TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(); if (ModuleTriple.getArch()) { - CPUStr = getCPUStr(); - FeaturesStr = getFeaturesStr(); + CPUStr = codegen::getCPUStr(); + FeaturesStr = codegen::getFeaturesStr(); Machine = GetTargetMachine(ModuleTriple, CPUStr, FeaturesStr, Options); } else if (ModuleTriple.getArchName() != "unknown" && ModuleTriple.getArchName() != "") { @@ -676,19 +717,40 @@ int main(int argc, char **argv) { // Override function attributes based on CPUStr, FeaturesStr, and command line // flags. - setFunctionAttributes(CPUStr, FeaturesStr, *M); + codegen::setFunctionAttributes(CPUStr, FeaturesStr, *M); // If the output is set to be emitted to standard out, and standard out is a // console, print out a warning message and refuse to do it. We don't // impress anyone by spewing tons of binary goo to a terminal. if (!Force && !NoOutput && !AnalyzeOnly && !OutputAssembly) - if (CheckBitcodeOutputToConsole(Out->os(), !Quiet)) + if (CheckBitcodeOutputToConsole(Out->os())) NoOutput = true; if (OutputThinLTOBC) M->addModuleFlag(Module::Error, "EnableSplitLTOUnit", SplitLTOUnit); - if (PassPipeline.getNumOccurrences() > 0) { + if (EnableNewPassManager || PassPipeline.getNumOccurrences() > 0) { + if (PassPipeline.getNumOccurrences() > 0 && PassList.size() > 0) { + errs() + << "Cannot specify passes via both -foo-pass and --passes=foo-pass"; + return 1; + } + SmallVector<StringRef, 4> Passes; + for (const auto &P : PassList) { + Passes.push_back(P->getPassArgument()); + } + if (OptLevelO0) + Passes.push_back("default<O0>"); + if (OptLevelO1) + Passes.push_back("default<O1>"); + if (OptLevelO2) + Passes.push_back("default<O2>"); + if (OptLevelO3) + Passes.push_back("default<O3>"); + if (OptLevelOs) + Passes.push_back("default<Os>"); + if (OptLevelOz) + Passes.push_back("default<Oz>"); OutputKind OK = OK_NoOutput; if (!NoOutput) OK = OutputAssembly @@ -705,10 +767,10 @@ 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(), - RemarksFile.get(), PassPipeline, OK, VK, + RemarksFile.get(), PassPipeline, Passes, OK, VK, PreserveAssemblyUseListOrder, PreserveBitcodeUseListOrder, EmitSummaryIndex, - EmitModuleHash, EnableDebugify) + EmitModuleHash, EnableDebugify, Coroutines) ? 0 : 1; } @@ -831,19 +893,19 @@ int main(int argc, char **argv) { if (AnalyzeOnly) { switch (Kind) { case PT_Region: - Passes.add(createRegionPassPrinter(PassInf, Out->os(), Quiet)); + Passes.add(createRegionPassPrinter(PassInf, Out->os())); break; case PT_Loop: - Passes.add(createLoopPassPrinter(PassInf, Out->os(), Quiet)); + Passes.add(createLoopPassPrinter(PassInf, Out->os())); break; case PT_Function: - Passes.add(createFunctionPassPrinter(PassInf, Out->os(), Quiet)); + Passes.add(createFunctionPassPrinter(PassInf, Out->os())); break; case PT_CallGraphSCC: - Passes.add(createCallGraphPassPrinter(PassInf, Out->os(), Quiet)); + Passes.add(createCallGraphPassPrinter(PassInf, Out->os())); break; default: - Passes.add(createModulePassPrinter(PassInf, Out->os(), Quiet)); + Passes.add(createModulePassPrinter(PassInf, Out->os())); break; } } |