diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-10-23 17:51:42 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-10-23 17:51:42 +0000 |
commit | 1d5ae1026e831016fc29fd927877c86af904481f (patch) | |
tree | 2cdfd12620fcfa5d9e4a0389f85368e8e36f63f9 /tools | |
parent | e6d1592492a3a379186bfb02bd0f4eda0669c0d5 (diff) |
Notes
Diffstat (limited to 'tools')
120 files changed, 7234 insertions, 2554 deletions
diff --git a/tools/bugpoint/BugDriver.h b/tools/bugpoint/BugDriver.h index 75f166b21b2c..fe5201eb2e6c 100644 --- a/tools/bugpoint/BugDriver.h +++ b/tools/bugpoint/BugDriver.h @@ -217,8 +217,7 @@ public: /// returning the transformed module on success, or a null pointer on failure. std::unique_ptr<Module> runPassesOn(Module *M, const std::vector<std::string> &Passes, - unsigned NumExtraArgs = 0, - const char *const *ExtraArgs = nullptr); + ArrayRef<std::string> ExtraArgs = {}); /// runPasses - Run the specified passes on Program, outputting a bitcode /// file and writting the filename into OutputFile if successful. If the @@ -231,8 +230,8 @@ public: /// bool runPasses(Module &Program, const std::vector<std::string> &PassesToRun, std::string &OutputFilename, bool DeleteOutput = false, - bool Quiet = false, unsigned NumExtraArgs = 0, - const char *const *ExtraArgs = nullptr) const; + bool Quiet = false, + ArrayRef<std::string> ExtraArgs = {}) const; /// runPasses - Just like the method above, but this just returns true or /// false indicating whether or not the optimizer crashed on the specified diff --git a/tools/bugpoint/ExtractFunction.cpp b/tools/bugpoint/ExtractFunction.cpp index 105702de3f1d..d9047acd30e1 100644 --- a/tools/bugpoint/ExtractFunction.cpp +++ b/tools/bugpoint/ExtractFunction.cpp @@ -407,11 +407,10 @@ BugDriver::extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs, std::string uniqueFN = "--extract-blocks-file="; uniqueFN += Temp->TmpName; - const char *ExtraArg = uniqueFN.c_str(); std::vector<std::string> PI; PI.push_back("extract-blocks"); - std::unique_ptr<Module> Ret = runPassesOn(M, PI, 1, &ExtraArg); + std::unique_ptr<Module> Ret = runPassesOn(M, PI, {uniqueFN}); if (!Ret) { outs() << "*** Basic Block extraction failed, please report a bug!\n"; diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp index 562de7952388..64af81fcc8a1 100644 --- a/tools/bugpoint/OptimizerDriver.cpp +++ b/tools/bugpoint/OptimizerDriver.cpp @@ -79,7 +79,7 @@ bool BugDriver::writeProgramToFile(int FD, const Module &M) const { bool BugDriver::writeProgramToFile(const std::string &Filename, const Module &M) const { std::error_code EC; - ToolOutputFile Out(Filename, EC, sys::fs::F_None); + ToolOutputFile Out(Filename, EC, sys::fs::OF_None); if (!EC) return writeProgramToFileAux(Out, M); return true; @@ -130,8 +130,7 @@ static cl::list<std::string> OptArgs("opt-args", cl::Positional, bool BugDriver::runPasses(Module &Program, const std::vector<std::string> &Passes, std::string &OutputFilename, bool DeleteOutput, - bool Quiet, unsigned NumExtraArgs, - const char *const *ExtraArgs) const { + bool Quiet, ArrayRef<std::string> ExtraArgs) const { // setup the output file name outs().flush(); SmallString<128> UniqueFilename; @@ -223,8 +222,7 @@ bool BugDriver::runPasses(Module &Program, I != E; ++I) Args.push_back(I->c_str()); Args.push_back(Temp->TmpName.c_str()); - for (unsigned i = 0; i < NumExtraArgs; ++i) - Args.push_back(*ExtraArgs); + Args.append(ExtraArgs.begin(), ExtraArgs.end()); LLVM_DEBUG(errs() << "\nAbout to run:\t"; for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) errs() @@ -268,10 +266,10 @@ bool BugDriver::runPasses(Module &Program, std::unique_ptr<Module> BugDriver::runPassesOn(Module *M, const std::vector<std::string> &Passes, - unsigned NumExtraArgs, const char *const *ExtraArgs) { + ArrayRef<std::string> ExtraArgs) { std::string BitcodeResult; if (runPasses(*M, Passes, BitcodeResult, false /*delete*/, true /*quiet*/, - NumExtraArgs, ExtraArgs)) { + ExtraArgs)) { return nullptr; } diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp index da4244345e3b..19b2ea2c0181 100644 --- a/tools/bugpoint/ToolRunner.cpp +++ b/tools/bugpoint/ToolRunner.cpp @@ -170,7 +170,7 @@ Expected<int> LLI::ExecuteProgram(const std::string &Bitcode, const std::vector<std::string> &SharedLibs, unsigned Timeout, unsigned MemoryLimit) { std::vector<StringRef> LLIArgs; - LLIArgs.push_back(LLIPath.c_str()); + LLIArgs.push_back(LLIPath); LLIArgs.push_back("-force-interpreter=true"); for (std::vector<std::string>::const_iterator i = SharedLibs.begin(), @@ -266,15 +266,15 @@ Error CustomCompiler::compileProgram(const std::string &Bitcode, unsigned Timeout, unsigned MemoryLimit) { std::vector<StringRef> ProgramArgs; - ProgramArgs.push_back(CompilerCommand.c_str()); + ProgramArgs.push_back(CompilerCommand); - for (std::size_t i = 0; i < CompilerArgs.size(); ++i) - ProgramArgs.push_back(CompilerArgs.at(i).c_str()); + for (const auto &Arg : CompilerArgs) + ProgramArgs.push_back(Arg); ProgramArgs.push_back(Bitcode); // Add optional parameters to the running program from Argv - for (unsigned i = 0, e = CompilerArgs.size(); i != e; ++i) - ProgramArgs.push_back(CompilerArgs[i].c_str()); + for (const auto &Arg : CompilerArgs) + ProgramArgs.push_back(Arg); if (RunProgramWithTimeout(CompilerCommand, ProgramArgs, "", "", "", Timeout, MemoryLimit)) @@ -559,7 +559,7 @@ Expected<int> JIT::ExecuteProgram(const std::string &Bitcode, unsigned Timeout, unsigned MemoryLimit) { // Construct a vector of parameters, incorporating those from the command-line std::vector<StringRef> JITArgs; - JITArgs.push_back(LLIPath.c_str()); + JITArgs.push_back(LLIPath); JITArgs.push_back("-force-interpreter=false"); // Add any extra LLI args. @@ -570,7 +570,7 @@ Expected<int> JIT::ExecuteProgram(const std::string &Bitcode, JITArgs.push_back("-load"); JITArgs.push_back(SharedLibs[i]); } - JITArgs.push_back(Bitcode.c_str()); + JITArgs.push_back(Bitcode); // Add optional parameters to the running program from Argv for (unsigned i = 0, e = Args.size(); i != e; ++i) JITArgs.push_back(Args[i]); diff --git a/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp index 2d5322a351ad..c7644e75ae4b 100644 --- a/tools/bugpoint/bugpoint.cpp +++ b/tools/bugpoint/bugpoint.cpp @@ -81,6 +81,10 @@ static cl::opt<bool> OptLevelOs( "Like -O2 with extra optimizations for size. Similar to clang -Os")); static cl::opt<bool> +OptLevelOz("Oz", + cl::desc("Like -Os but reduces code size further. Similar to clang -Oz")); + +static cl::opt<bool> OptLevelO3("O3", cl::desc("Optimization level 3. Identical to 'opt -O3'")); static cl::opt<std::string> @@ -109,6 +113,26 @@ public: }; } +// This routine adds optimization passes based on selected optimization level, +// OptLevel. +// +// OptLevel - Optimization Level +static void AddOptimizationPasses(legacy::FunctionPassManager &FPM, + unsigned OptLevel, + unsigned SizeLevel) { + PassManagerBuilder Builder; + Builder.OptLevel = OptLevel; + Builder.SizeLevel = SizeLevel; + + if (OptLevel > 1) + Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false); + else + Builder.Inliner = createAlwaysInlinerLegacyPass(); + + Builder.populateFunctionPassManager(FPM); + Builder.populateModulePassManager(FPM); +} + #ifdef LINK_POLLY_INTO_TOOLS namespace polly { void initializePollyPasses(llvm::PassRegistry &Registry); @@ -189,18 +213,16 @@ int main(int argc, char **argv) { Builder.populateLTOPassManager(PM); } - if (OptLevelO1 || OptLevelO2 || OptLevelO3) { - PassManagerBuilder Builder; - if (OptLevelO1) - Builder.Inliner = createAlwaysInlinerLegacyPass(); - else if (OptLevelOs || OptLevelO2) - Builder.Inliner = createFunctionInliningPass( - 2, OptLevelOs ? 1 : 0, false); - else - Builder.Inliner = createFunctionInliningPass(275); - Builder.populateFunctionPassManager(PM); - Builder.populateModulePassManager(PM); - } + if (OptLevelO1) + AddOptimizationPasses(PM, 1, 0); + else if (OptLevelO2) + AddOptimizationPasses(PM, 2, 0); + else if (OptLevelO3) + AddOptimizationPasses(PM, 3, 0); + else if (OptLevelOs) + AddOptimizationPasses(PM, 2, 1); + else if (OptLevelOz) + AddOptimizationPasses(PM, 2, 2); for (const PassInfo *PI : PassList) D.addPass(PI->getPassArgument()); diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp index 76da843f065e..574b15b399c3 100644 --- a/tools/llc/llc.cpp +++ b/tools/llc/llc.cpp @@ -239,10 +239,10 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName, // Open the file. std::error_code EC; - sys::fs::OpenFlags OpenFlags = sys::fs::F_None; + sys::fs::OpenFlags OpenFlags = sys::fs::OF_None; if (!Binary) - OpenFlags |= sys::fs::F_Text; - auto FDOut = llvm::make_unique<ToolOutputFile>(OutputFilename, EC, OpenFlags); + OpenFlags |= sys::fs::OF_Text; + auto FDOut = std::make_unique<ToolOutputFile>(OutputFilename, EC, OpenFlags); if (EC) { WithColor::error() << EC.message() << '\n'; return nullptr; @@ -329,7 +329,7 @@ int main(int argc, char **argv) { // Set a diagnostic handler that doesn't exit on the first error bool HasError = false; Context.setDiagnosticHandler( - llvm::make_unique<LLCDiagnosticHandler>(&HasError)); + std::make_unique<LLCDiagnosticHandler>(&HasError)); Context.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, &HasError); Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr = @@ -479,8 +479,8 @@ static int compileModule(char **argv, LLVMContext &Context) { std::unique_ptr<ToolOutputFile> DwoOut; if (!SplitDwarfOutputFile.empty()) { std::error_code EC; - DwoOut = llvm::make_unique<ToolOutputFile>(SplitDwarfOutputFile, EC, - sys::fs::F_None); + DwoOut = std::make_unique<ToolOutputFile>(SplitDwarfOutputFile, EC, + sys::fs::OF_None); if (EC) { WithColor::error(errs(), argv[0]) << EC.message() << '\n'; return 1; @@ -533,13 +533,14 @@ static int compileModule(char **argv, LLVMContext &Context) { if ((FileType != TargetMachine::CGFT_AssemblyFile && !Out->os().supportsSeeking()) || CompileTwice) { - BOS = make_unique<raw_svector_ostream>(Buffer); + BOS = std::make_unique<raw_svector_ostream>(Buffer); OS = BOS.get(); } const char *argv0 = argv[0]; - LLVMTargetMachine &LLVMTM = static_cast<LLVMTargetMachine&>(*Target); - MachineModuleInfo *MMI = new MachineModuleInfo(&LLVMTM); + LLVMTargetMachine &LLVMTM = static_cast<LLVMTargetMachine &>(*Target); + MachineModuleInfoWrapperPass *MMIWP = + new MachineModuleInfoWrapperPass(&LLVMTM); // Construct a custom pass pipeline that starts after instruction // selection. @@ -559,7 +560,7 @@ static int compileModule(char **argv, LLVMContext &Context) { TPC.setDisableVerify(NoVerify); PM.add(&TPC); - PM.add(MMI); + PM.add(MMIWP); TPC.printAndVerify(""); for (const std::string &RunPassName : *RunPassNames) { if (addPass(PM, argv0, RunPassName, TPC)) @@ -570,7 +571,7 @@ static int compileModule(char **argv, LLVMContext &Context) { PM.add(createFreeMachineFunctionPass()); } else if (Target->addPassesToEmitFile(PM, *OS, DwoOut ? &DwoOut->os() : nullptr, - FileType, NoVerify, MMI)) { + FileType, NoVerify, MMIWP)) { WithColor::warning(errs(), argv[0]) << "target does not support generation of this" << " file type!\n"; @@ -578,8 +579,8 @@ static int compileModule(char **argv, LLVMContext &Context) { } if (MIR) { - assert(MMI && "Forgot to create MMI?"); - if (MIR->parseMachineFunctions(*M, *MMI)) + assert(MMIWP && "Forgot to create MMIWP?"); + if (MIR->parseMachineFunctions(*M, MMIWP->getMMI())) return 1; } diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index 8c8cd88c9711..ccad06721414 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -251,7 +251,7 @@ public: sys::fs::create_directories(Twine(dir)); } std::error_code EC; - raw_fd_ostream outfile(CacheName, EC, sys::fs::F_None); + raw_fd_ostream outfile(CacheName, EC, sys::fs::OF_None); outfile.write(Obj.getBufferStart(), Obj.getBufferSize()); outfile.close(); } @@ -308,7 +308,7 @@ static void addCygMingExtraModule(ExecutionEngine &EE, LLVMContext &Context, Triple TargetTriple(TargetTripleStr); // Create a new module. - std::unique_ptr<Module> M = make_unique<Module>("CygMingHelper", Context); + std::unique_ptr<Module> M = std::make_unique<Module>("CygMingHelper", Context); M->setTargetTriple(TargetTripleStr); // Create an empty function named "__main". @@ -695,18 +695,16 @@ int main(int argc, char **argv, char * const *envp) { return Result; } -static orc::IRTransformLayer::TransformFunction createDebugDumper() { +static std::function<void(Module &)> createDebugDumper() { switch (OrcDumpKind) { case DumpKind::NoDump: - return [](orc::ThreadSafeModule TSM, - const orc::MaterializationResponsibility &R) { return TSM; }; + return [](Module &M) {}; case DumpKind::DumpFuncsToStdOut: - return [](orc::ThreadSafeModule TSM, - const orc::MaterializationResponsibility &R) { + return [](Module &M) { printf("[ "); - for (const auto &F : *TSM.getModule()) { + for (const auto &F : M) { if (F.isDeclaration()) continue; @@ -718,31 +716,23 @@ static orc::IRTransformLayer::TransformFunction createDebugDumper() { } printf("]\n"); - return TSM; }; case DumpKind::DumpModsToStdOut: - return [](orc::ThreadSafeModule TSM, - const orc::MaterializationResponsibility &R) { - outs() << "----- Module Start -----\n" - << *TSM.getModule() << "----- Module End -----\n"; - - return TSM; + return [](Module &M) { + outs() << "----- Module Start -----\n" << M << "----- Module End -----\n"; }; case DumpKind::DumpModsToDisk: - return [](orc::ThreadSafeModule TSM, - const orc::MaterializationResponsibility &R) { + return [](Module &M) { std::error_code EC; - raw_fd_ostream Out(TSM.getModule()->getModuleIdentifier() + ".ll", EC, - sys::fs::F_Text); + raw_fd_ostream Out(M.getModuleIdentifier() + ".ll", EC, sys::fs::OF_Text); if (EC) { - errs() << "Couldn't open " << TSM.getModule()->getModuleIdentifier() + errs() << "Couldn't open " << M.getModuleIdentifier() << " for dumping.\nError:" << EC.message() << "\n"; exit(1); } - Out << *TSM.getModule(); - return TSM; + Out << M; }; } llvm_unreachable("Unknown DumpKind"); @@ -754,14 +744,13 @@ int runOrcLazyJIT(const char *ProgName) { // Start setting up the JIT environment. // Parse the main module. - orc::ThreadSafeContext TSCtx(llvm::make_unique<LLVMContext>()); + orc::ThreadSafeContext TSCtx(std::make_unique<LLVMContext>()); SMDiagnostic Err; - auto MainModule = orc::ThreadSafeModule( - parseIRFile(InputFile, Err, *TSCtx.getContext()), TSCtx); + auto MainModule = parseIRFile(InputFile, Err, *TSCtx.getContext()); if (!MainModule) reportError(Err, ProgName); - const auto &TT = MainModule.getModule()->getTargetTriple(); + const auto &TT = MainModule->getTargetTriple(); orc::LLLazyJITBuilder Builder; Builder.setJITTargetMachineBuilder( @@ -794,13 +783,16 @@ int runOrcLazyJIT(const char *ProgName) { J->setLazyCompileTransform([&](orc::ThreadSafeModule TSM, const orc::MaterializationResponsibility &R) { - if (verifyModule(*TSM.getModule(), &dbgs())) { - dbgs() << "Bad module: " << *TSM.getModule() << "\n"; - exit(1); - } - return Dump(std::move(TSM), R); + TSM.withModuleDo([&](Module &M) { + if (verifyModule(M, &dbgs())) { + dbgs() << "Bad module: " << &M << "\n"; + exit(1); + } + Dump(M); + }); + return TSM; }); - J->getMainJITDylib().setGenerator( + J->getMainJITDylib().addGenerator( ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( J->getDataLayout().getGlobalPrefix()))); @@ -809,7 +801,8 @@ int runOrcLazyJIT(const char *ProgName) { ExitOnErr(CXXRuntimeOverrides.enable(J->getMainJITDylib(), Mangle)); // Add the main module. - ExitOnErr(J->addLazyIRModule(std::move(MainModule))); + ExitOnErr( + J->addLazyIRModule(orc::ThreadSafeModule(std::move(MainModule), TSCtx))); // Create JITDylibs and add any extra modules. { @@ -839,6 +832,16 @@ int runOrcLazyJIT(const char *ProgName) { ExitOnErr( J->addLazyIRModule(JD, orc::ThreadSafeModule(std::move(M), TSCtx))); } + + for (auto EAItr = ExtraArchives.begin(), EAEnd = ExtraArchives.end(); + EAItr != EAEnd; ++EAItr) { + auto EAIdx = ExtraArchives.getPosition(EAItr - ExtraArchives.begin()); + assert(EAIdx != 0 && "ExtraArchive should have index > 0"); + auto JDItr = std::prev(IdxToDylib.lower_bound(EAIdx)); + auto &JD = *JDItr->second; + JD.addGenerator(ExitOnErr(orc::StaticLibraryDefinitionGenerator::Load( + J->getObjLinkingLayer(), EAItr->c_str()))); + } } // Add the objects. @@ -959,6 +962,6 @@ std::unique_ptr<FDRawChannel> launchRemote() { close(PipeFD[1][1]); // Return an RPC channel connected to our end of the pipes. - return llvm::make_unique<FDRawChannel>(PipeFD[1][0], PipeFD[0][1]); + return std::make_unique<FDRawChannel>(PipeFD[1][0], PipeFD[0][1]); #endif } diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index 91746d0fab37..c9cf217f7688 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -43,6 +43,11 @@ #include <io.h> #endif +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + using namespace llvm; // The name this program was invoked as. @@ -70,14 +75,14 @@ USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] [count] <archive> [f llvm-ar -M [<mri-script] OPTIONS: - --format - Archive format to create + --format - archive format to create =default - default =gnu - gnu =darwin - darwin =bsd - bsd - --plugin=<string> - Ignored for compatibility - --help - Display available options - --version - Display the version of this program + --plugin=<string> - ignored for compatibility + -h --help - display this help and exit + --version - print the version and exit @<file> - read options from <file> OPERATIONS: @@ -95,11 +100,13 @@ MODIFIERS: [b] - put [files] before [relpos] (same as [i]) [c] - do not warn if archive had to be created [D] - use zero for timestamps and uids/gids (default) + [h] - display this help and exit [i] - put [files] before [relpos] (same as [b]) [l] - ignored for compatibility [L] - add archive's contents [N] - use instance [count] of name [o] - preserve original dates + [O] - display member offsets [P] - use full names when matching (implied for thin archives) [s] - create an archive index (cf. ranlib) [S] - do not build a symbol table @@ -107,6 +114,7 @@ MODIFIERS: [u] - update only [files] newer than archive contents [U] - use actual timestamps and uids/gids [v] - be verbose about actions taken + [V] - display the version and exit )"; void printHelpMessage() { @@ -116,10 +124,19 @@ void printHelpMessage() { outs() << ArHelp; } +static unsigned MRILineNumber; +static bool ParsingMRIScript; + // Show the error message and exit. LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) { - WithColor::error(errs(), ToolName) << Error << ".\n"; - printHelpMessage(); + if (ParsingMRIScript) { + WithColor::error(errs(), ToolName) + << "script line " << MRILineNumber << ": " << Error << "\n"; + } else { + WithColor::error(errs(), ToolName) << Error << "\n"; + printHelpMessage(); + } + exit(1); } @@ -171,17 +188,18 @@ enum ArchiveOperation { }; // Modifiers to follow operation to vary behavior -static bool AddAfter = false; ///< 'a' modifier -static bool AddBefore = false; ///< 'b' modifier -static bool Create = false; ///< 'c' modifier -static bool OriginalDates = false; ///< 'o' modifier -static bool CompareFullPath = false; ///< 'P' modifier -static bool OnlyUpdate = false; ///< 'u' modifier -static bool Verbose = false; ///< 'v' modifier -static bool Symtab = true; ///< 's' modifier -static bool Deterministic = true; ///< 'D' and 'U' modifiers -static bool Thin = false; ///< 'T' modifier -static bool AddLibrary = false; ///< 'L' modifier +static bool AddAfter = false; ///< 'a' modifier +static bool AddBefore = false; ///< 'b' modifier +static bool Create = false; ///< 'c' modifier +static bool OriginalDates = false; ///< 'o' modifier +static bool DisplayMemberOffsets = false; ///< 'O' modifier +static bool CompareFullPath = false; ///< 'P' modifier +static bool OnlyUpdate = false; ///< 'u' modifier +static bool Verbose = false; ///< 'v' modifier +static bool Symtab = true; ///< 's' modifier +static bool Deterministic = true; ///< 'D' and 'U' modifiers +static bool Thin = false; ///< 'T' modifier +static bool AddLibrary = false; ///< 'L' modifier // Relative Positional Argument (for insert/move). This variable holds // the name of the archive member to which the 'a', 'b' or 'i' modifier @@ -198,6 +216,9 @@ static int CountParam = 0; // command line. static std::string ArchiveName; +static std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; +static std::vector<std::unique_ptr<object::Archive>> Archives; + // This variable holds the list of member files to proecess, as given // on the command line. static std::vector<StringRef> Members; @@ -209,7 +230,7 @@ static BumpPtrAllocator Alloc; // associated with a, b, and i modifiers static void getRelPos() { if (PositionalArgs.empty()) - fail("Expected [relpos] for a, b, or i modifier"); + fail("expected [relpos] for 'a', 'b', or 'i' modifier"); RelPos = PositionalArgs[0]; PositionalArgs.erase(PositionalArgs.begin()); } @@ -218,40 +239,31 @@ static void getRelPos() { // associated with the N modifier static void getCountParam() { if (PositionalArgs.empty()) - fail("Expected [count] for N modifier"); + fail("expected [count] for 'N' modifier"); auto CountParamArg = StringRef(PositionalArgs[0]); if (CountParamArg.getAsInteger(10, CountParam)) - fail("Value for [count] must be numeric, got: " + CountParamArg); + fail("value for [count] must be numeric, got: " + CountParamArg); if (CountParam < 1) - fail("Value for [count] must be positive, got: " + CountParamArg); + fail("value for [count] must be positive, got: " + CountParamArg); PositionalArgs.erase(PositionalArgs.begin()); } // Get the archive file name from the command line static void getArchive() { if (PositionalArgs.empty()) - fail("An archive name must be specified"); + fail("an archive name must be specified"); ArchiveName = PositionalArgs[0]; PositionalArgs.erase(PositionalArgs.begin()); } -// Copy over remaining items in PositionalArgs to our Members vector -static void getMembers() { - for (auto &Arg : PositionalArgs) - Members.push_back(Arg); -} - -std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; -std::vector<std::unique_ptr<object::Archive>> Archives; - static object::Archive &readLibrary(const Twine &Library) { auto BufOrErr = MemoryBuffer::getFile(Library, -1, false); - failIfError(BufOrErr.getError(), "Could not open library " + Library); + failIfError(BufOrErr.getError(), "could not open library " + Library); ArchiveBuffers.push_back(std::move(*BufOrErr)); auto LibOrErr = object::Archive::create(ArchiveBuffers.back()->getMemBufferRef()); failIfError(errorToErrorCode(LibOrErr.takeError()), - "Could not parse library"); + "could not parse library"); Archives.push_back(std::move(*LibOrErr)); return *Archives.back(); } @@ -264,7 +276,7 @@ static void runMRIScript(); static ArchiveOperation parseCommandLine() { if (MRI) { if (!PositionalArgs.empty() || !Options.empty()) - fail("Cannot mix -M and other options"); + fail("cannot mix -M and other options"); runMRIScript(); } @@ -319,6 +331,9 @@ static ArchiveOperation parseCommandLine() { case 'o': OriginalDates = true; break; + case 'O': + DisplayMemberOffsets = true; + break; case 'P': CompareFullPath = true; break; @@ -367,6 +382,12 @@ static ArchiveOperation parseCommandLine() { case 'L': AddLibrary = true; break; + case 'V': + cl::PrintVersionMessage(); + exit(0); + case 'h': + printHelpMessage(); + exit(0); default: fail(std::string("unknown option ") + Options[i]); } @@ -377,37 +398,37 @@ static ArchiveOperation parseCommandLine() { getArchive(); // Everything on the command line at this point is a member. - getMembers(); + Members.assign(PositionalArgs.begin(), PositionalArgs.end()); if (NumOperations == 0 && MaybeJustCreateSymTab) { NumOperations = 1; Operation = CreateSymTab; if (!Members.empty()) - fail("The s operation takes only an archive as argument"); + fail("the 's' operation takes only an archive as argument"); } // Perform various checks on the operation/modifier specification // to make sure we are dealing with a legal request. if (NumOperations == 0) - fail("You must specify at least one of the operations"); + fail("you must specify at least one of the operations"); if (NumOperations > 1) - fail("Only one operation may be specified"); + fail("only one operation may be specified"); if (NumPositional > 1) - fail("You may only specify one of a, b, and i modifiers"); + fail("you may only specify one of 'a', 'b', and 'i' modifiers"); if (AddAfter || AddBefore) if (Operation != Move && Operation != ReplaceOrInsert) - fail("The 'a', 'b' and 'i' modifiers can only be specified with " + fail("the 'a', 'b' and 'i' modifiers can only be specified with " "the 'm' or 'r' operations"); if (CountParam) if (Operation != Extract && Operation != Delete) - fail("The 'N' modifier can only be specified with the 'x' or 'd' " + fail("the 'N' modifier can only be specified with the 'x' or 'd' " "operations"); if (OriginalDates && Operation != Extract) - fail("The 'o' modifier is only applicable to the 'x' operation"); + fail("the 'o' modifier is only applicable to the 'x' operation"); if (OnlyUpdate && Operation != ReplaceOrInsert) - fail("The 'u' modifier is only applicable to the 'r' operation"); + fail("the 'u' modifier is only applicable to the 'r' operation"); if (AddLibrary && Operation != QuickAppend) - fail("The 'L' modifier is only applicable to the 'q' operation"); + fail("the 'L' modifier is only applicable to the 'q' operation"); // Return the parsed operation to the caller return Operation; @@ -470,12 +491,35 @@ static void doDisplayTable(StringRef Name, const object::Archive::Child &C) { if (!ParentDir.empty()) outs() << sys::path::convert_to_slash(ParentDir) << '/'; } + outs() << Name; + } else { + outs() << Name; + if (DisplayMemberOffsets) + outs() << " 0x" << utohexstr(C.getDataOffset(), true); } - outs() << Name << "\n"; + outs() << '\n'; } -static StringRef normalizePath(StringRef Path) { - return CompareFullPath ? Path : sys::path::filename(Path); +static std::string normalizePath(StringRef Path) { + return CompareFullPath ? sys::path::convert_to_slash(Path) + : std::string(sys::path::filename(Path)); +} + +static bool comparePaths(StringRef Path1, StringRef Path2) { +// When on Windows this function calls CompareStringOrdinal +// 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)); + + return CompareStringOrdinal(WPath1.data(), WPath1.size(), WPath2.data(), + WPath2.size(), true) == CSTR_EQUAL; +#else + return normalizePath(Path1) == normalizePath(Path2); +#endif } // Implement the 'x' operation. This function extracts files back to the file @@ -489,7 +533,7 @@ static void doExtract(StringRef Name, const object::Archive::Child &C) { int FD; failIfError(sys::fs::openFileForWrite(sys::path::filename(Name), FD, sys::fs::CD_CreateAlways, - sys::fs::F_None, Mode), + sys::fs::OF_None, Mode), Name); { @@ -551,7 +595,7 @@ static void performReadOperation(ArchiveOperation Operation, if (Filter) { auto I = find_if(Members, [Name](StringRef Path) { - return Name == normalizePath(Path); + return comparePaths(Name, Path); }); if (I == Members.end()) continue; @@ -588,7 +632,7 @@ static void addChildMember(std::vector<NewArchiveMember> &Members, const object::Archive::Child &M, bool FlattenArchive = false) { if (Thin && !M.getParent()->isThin()) - fail("Cannot convert a regular archive to a thin one"); + fail("cannot convert a regular archive to a thin one"); Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getOldMember(M, Deterministic); failIfError(NMOrErr.takeError()); @@ -681,7 +725,7 @@ static InsertAction computeInsertAction(ArchiveOperation Operation, if (Operation == QuickAppend || Members.empty()) return IA_AddOldMember; auto MI = find_if( - Members, [Name](StringRef Path) { return Name == normalizePath(Path); }); + Members, [Name](StringRef Path) { return comparePaths(Name, Path); }); if (MI == Members.end()) return IA_AddOldMember; @@ -698,9 +742,8 @@ static InsertAction computeInsertAction(ArchiveOperation Operation, return IA_MoveOldMember; if (Operation == ReplaceOrInsert) { - StringRef PosName = normalizePath(RelPos); if (!OnlyUpdate) { - if (PosName.empty()) + if (RelPos.empty()) return IA_AddNewMember; return IA_MoveNewMember; } @@ -712,12 +755,12 @@ static InsertAction computeInsertAction(ArchiveOperation Operation, auto ModTimeOrErr = Member.getLastModified(); failIfError(ModTimeOrErr.takeError()); if (Status.getLastModificationTime() < ModTimeOrErr.get()) { - if (PosName.empty()) + if (RelPos.empty()) return IA_AddOldMember; return IA_MoveOldMember; } - if (PosName.empty()) + if (RelPos.empty()) return IA_AddNewMember; return IA_MoveNewMember; } @@ -732,7 +775,6 @@ computeNewArchiveMembers(ArchiveOperation Operation, std::vector<NewArchiveMember> Ret; std::vector<NewArchiveMember> Moved; int InsertPos = -1; - StringRef PosName = normalizePath(RelPos); if (OldArchive) { Error Err = Error::success(); StringMap<int> MemberCount; @@ -740,8 +782,8 @@ computeNewArchiveMembers(ArchiveOperation Operation, int Pos = Ret.size(); Expected<StringRef> NameOrErr = Child.getName(); failIfError(NameOrErr.takeError()); - StringRef Name = NameOrErr.get(); - if (Name == PosName) { + std::string Name = NameOrErr.get(); + if (comparePaths(Name, RelPos)) { assert(AddAfter || AddBefore); if (AddBefore) InsertPos = Pos; @@ -783,7 +825,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, return Ret; if (!RelPos.empty() && InsertPos == -1) - fail("Insertion point not found"); + fail("insertion point not found"); if (RelPos.empty()) InsertPos = Ret.size(); @@ -859,12 +901,12 @@ static void performWriteOperation(ArchiveOperation Operation, break; case BSD: if (Thin) - fail("Only the gnu format has a thin mode"); + fail("only the gnu format has a thin mode"); Kind = object::Archive::K_BSD; break; case DARWIN: if (Thin) - fail("Only the gnu format has a thin mode"); + fail("only the gnu format has a thin mode"); Kind = object::Archive::K_DARWIN; break; case Unknown: @@ -922,14 +964,12 @@ 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("error opening '" + ArchiveName + "': " + EC.message()); if (!EC) { Error Err = Error::success(); object::Archive Archive(Buf.get()->getMemBufferRef(), Err); - EC = errorToErrorCode(std::move(Err)); - failIfError(EC, - "error loading '" + ArchiveName + "': " + EC.message() + "!"); + failIfError(std::move(Err), "unable to load '" + ArchiveName + "'"); if (Archive.isThin()) CompareFullPath = true; performOperation(Operation, &Archive, std::move(Buf.get()), NewMembers); @@ -960,8 +1000,10 @@ static void runMRIScript() { const MemoryBuffer &Ref = *Buf.get(); bool Saved = false; std::vector<NewArchiveMember> NewMembers; + ParsingMRIScript = true; for (line_iterator I(Ref, /*SkipBlanks*/ false), E; I != E; ++I) { + ++MRILineNumber; StringRef Line = *I; Line = Line.split(';').first; Line = Line.split('*').first; @@ -1003,15 +1045,15 @@ static void runMRIScript() { case MRICommand::Create: Create = true; if (!ArchiveName.empty()) - fail("Editing multiple archives not supported"); + fail("editing multiple archives not supported"); if (Saved) - fail("File already saved"); + fail("file already saved"); ArchiveName = Rest; break; case MRICommand::Delete: { - StringRef Name = normalizePath(Rest); - llvm::erase_if(NewMembers, - [=](NewArchiveMember &M) { return M.MemberName == Name; }); + llvm::erase_if(NewMembers, [=](NewArchiveMember &M) { + return comparePaths(M.MemberName, Rest); + }); break; } case MRICommand::Save: @@ -1020,10 +1062,12 @@ static void runMRIScript() { case MRICommand::End: break; case MRICommand::Invalid: - fail("Unknown command: " + CommandStr); + fail("unknown command: " + CommandStr); } } - + + ParsingMRIScript = false; + // Nothing to do if not saved. if (Saved) performOperation(ReplaceOrInsert, &NewMembers); @@ -1108,7 +1152,7 @@ static int ranlib_main(int argc, char **argv) { return 0; } else { if (ArchiveSpecified) - fail("Exactly one archive should be specified"); + fail("exactly one archive should be specified"); ArchiveSpecified = true; ArchiveName = argv[i]; } @@ -1136,5 +1180,5 @@ int main(int argc, char **argv) { if (Stem.contains_lower("ar")) return ar_main(argc, argv); - fail("Not ranlib, ar, lib or dlltool!"); + fail("not ranlib, ar, lib or dlltool"); } diff --git a/tools/llvm-as/llvm-as.cpp b/tools/llvm-as/llvm-as.cpp index 234fef907a38..c9f50e38fc61 100644 --- a/tools/llvm-as/llvm-as.cpp +++ b/tools/llvm-as/llvm-as.cpp @@ -82,7 +82,7 @@ static void WriteOutputFile(const Module *M, const ModuleSummaryIndex *Index) { std::error_code EC; std::unique_ptr<ToolOutputFile> Out( - new ToolOutputFile(OutputFilename, EC, sys::fs::F_None)); + new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None)); if (EC) { errs() << EC.message() << '\n'; exit(1); diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp index f707e3c7ab53..7151cfb032f3 100644 --- a/tools/llvm-cov/CodeCoverage.cpp +++ b/tools/llvm-cov/CodeCoverage.cpp @@ -712,15 +712,15 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { // Create the function filters if (!NameFilters.empty() || NameWhitelist || !NameRegexFilters.empty()) { - auto NameFilterer = llvm::make_unique<CoverageFilters>(); + auto NameFilterer = std::make_unique<CoverageFilters>(); for (const auto &Name : NameFilters) - NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); + NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name)); if (NameWhitelist) NameFilterer->push_back( - llvm::make_unique<NameWhitelistCoverageFilter>(*NameWhitelist)); + std::make_unique<NameWhitelistCoverageFilter>(*NameWhitelist)); for (const auto &Regex : NameRegexFilters) NameFilterer->push_back( - llvm::make_unique<NameRegexCoverageFilter>(Regex)); + std::make_unique<NameRegexCoverageFilter>(Regex)); Filters.push_back(std::move(NameFilterer)); } @@ -728,18 +728,18 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { RegionCoverageGtFilter.getNumOccurrences() || LineCoverageLtFilter.getNumOccurrences() || LineCoverageGtFilter.getNumOccurrences()) { - auto StatFilterer = llvm::make_unique<CoverageFilters>(); + auto StatFilterer = std::make_unique<CoverageFilters>(); if (RegionCoverageLtFilter.getNumOccurrences()) - StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( + StatFilterer->push_back(std::make_unique<RegionCoverageFilter>( RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); if (RegionCoverageGtFilter.getNumOccurrences()) - StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( + StatFilterer->push_back(std::make_unique<RegionCoverageFilter>( RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); if (LineCoverageLtFilter.getNumOccurrences()) - StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( + StatFilterer->push_back(std::make_unique<LineCoverageFilter>( LineCoverageFilter::LessThan, LineCoverageLtFilter)); if (LineCoverageGtFilter.getNumOccurrences()) - StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( + StatFilterer->push_back(std::make_unique<LineCoverageFilter>( RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); Filters.push_back(std::move(StatFilterer)); } @@ -747,7 +747,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { // Create the ignore filename filters. for (const auto &RE : IgnoreFilenameRegexFilters) IgnoreFilenameFilters.push_back( - llvm::make_unique<NameRegexCoverageFilter>(RE)); + std::make_unique<NameRegexCoverageFilter>(RE)); if (!Arches.empty()) { for (const std::string &Arch : Arches) { @@ -1040,7 +1040,7 @@ int CodeCoverageTool::doExport(int argc, const char **argv, switch (ViewOpts.Format) { case CoverageViewOptions::OutputFormat::Text: - Exporter = llvm::make_unique<CoverageExporterJson>(*Coverage.get(), + Exporter = std::make_unique<CoverageExporterJson>(*Coverage.get(), ViewOpts, outs()); break; case CoverageViewOptions::OutputFormat::HTML: @@ -1048,7 +1048,7 @@ int CodeCoverageTool::doExport(int argc, const char **argv, // above. llvm_unreachable("Export in HTML is not supported!"); case CoverageViewOptions::OutputFormat::Lcov: - Exporter = llvm::make_unique<CoverageExporterLcov>(*Coverage.get(), + Exporter = std::make_unique<CoverageExporterLcov>(*Coverage.get(), ViewOpts, outs()); break; } diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp index 616f667e2c84..0e20ea63cd6f 100644 --- a/tools/llvm-cov/SourceCoverageView.cpp +++ b/tools/llvm-cov/SourceCoverageView.cpp @@ -76,9 +76,9 @@ std::unique_ptr<CoveragePrinter> CoveragePrinter::create(const CoverageViewOptions &Opts) { switch (Opts.Format) { case CoverageViewOptions::OutputFormat::Text: - return llvm::make_unique<CoveragePrinterText>(Opts); + return std::make_unique<CoveragePrinterText>(Opts); case CoverageViewOptions::OutputFormat::HTML: - return llvm::make_unique<CoveragePrinterHTML>(Opts); + return std::make_unique<CoveragePrinterHTML>(Opts); case CoverageViewOptions::OutputFormat::Lcov: // Unreachable because CodeCoverage.cpp should terminate with an error // before we get here. @@ -141,10 +141,10 @@ SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File, CoverageData &&CoverageInfo) { switch (Options.Format) { case CoverageViewOptions::OutputFormat::Text: - return llvm::make_unique<SourceCoverageViewText>( + return std::make_unique<SourceCoverageViewText>( SourceName, File, Options, std::move(CoverageInfo)); case CoverageViewOptions::OutputFormat::HTML: - return llvm::make_unique<SourceCoverageViewHTML>( + return std::make_unique<SourceCoverageViewHTML>( SourceName, File, Options, std::move(CoverageInfo)); case CoverageViewOptions::OutputFormat::Lcov: // Unreachable because CodeCoverage.cpp should terminate with an error diff --git a/tools/llvm-cov/TestingSupport.cpp b/tools/llvm-cov/TestingSupport.cpp index 3ee318c9c640..b99bd83157d0 100644 --- a/tools/llvm-cov/TestingSupport.cpp +++ b/tools/llvm-cov/TestingSupport.cpp @@ -8,6 +8,7 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/ProfileData/InstrProf.h" +#include "llvm/Support/Alignment.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/raw_ostream.h" @@ -50,8 +51,13 @@ int convertForTestingMain(int argc, const char *argv[]) { auto ObjFormat = OF->getTripleObjectFormat(); for (const auto &Section : OF->sections()) { StringRef Name; - if (Section.getName(Name)) + if (Expected<StringRef> NameOrErr = Section.getName()) { + Name = *NameOrErr; + } else { + consumeError(NameOrErr.takeError()); return 1; + } + if (Name == llvm::getInstrProfSectionName(IPSK_name, ObjFormat, /*AddSegmentInfo=*/false)) { ProfileNames = Section; @@ -94,7 +100,7 @@ int convertForTestingMain(int argc, const char *argv[]) { encodeULEB128(ProfileNamesAddress, OS); OS << ProfileNamesData; // Coverage mapping data is expected to have an alignment of 8. - for (unsigned Pad = OffsetToAlignment(OS.tell(), 8); Pad; --Pad) + for (unsigned Pad = offsetToAlignment(OS.tell(), Align(8)); Pad; --Pad) OS.write(uint8_t(0)); OS << CoverageMappingData; diff --git a/tools/llvm-cxxdump/llvm-cxxdump.cpp b/tools/llvm-cxxdump/llvm-cxxdump.cpp index 833312655788..03e1bab9417e 100644 --- a/tools/llvm-cxxdump/llvm-cxxdump.cpp +++ b/tools/llvm-cxxdump/llvm-cxxdump.cpp @@ -174,7 +174,11 @@ static void dumpCXXData(const ObjectFile *Obj) { SectionRelocMap.clear(); for (const SectionRef &Section : Obj->sections()) { - section_iterator Sec2 = Section.getRelocatedSection(); + Expected<section_iterator> ErrOrSec = Section.getRelocatedSection(); + if (!ErrOrSec) + error(ErrOrSec.takeError()); + + section_iterator Sec2 = *ErrOrSec; if (Sec2 != Obj->section_end()) SectionRelocMap[*Sec2].push_back(Section); } diff --git a/tools/llvm-cxxmap/llvm-cxxmap.cpp b/tools/llvm-cxxmap/llvm-cxxmap.cpp index 87d4d06bbc96..b53a6364c89e 100644 --- a/tools/llvm-cxxmap/llvm-cxxmap.cpp +++ b/tools/llvm-cxxmap/llvm-cxxmap.cpp @@ -145,7 +145,7 @@ int main(int argc, const char *argv[]) { exitWithErrorCode(RemappingBufOrError.getError(), RemappingFile); std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text); if (EC) exitWithErrorCode(EC, OutputFilename); diff --git a/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp index 3f337b874b16..d66299cbf767 100644 --- a/tools/llvm-dis/llvm-dis.cpp +++ b/tools/llvm-dis/llvm-dis.cpp @@ -153,7 +153,7 @@ int main(int argc, char **argv) { LLVMContext Context; Context.setDiagnosticHandler( - llvm::make_unique<LLVMDisDiagnosticHandler>(argv[0])); + std::make_unique<LLVMDisDiagnosticHandler>(argv[0])); cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n"); std::unique_ptr<MemoryBuffer> MB = @@ -186,7 +186,7 @@ int main(int argc, char **argv) { std::error_code EC; std::unique_ptr<ToolOutputFile> Out( - new ToolOutputFile(OutputFilename, EC, sys::fs::F_None)); + new ToolOutputFile(OutputFilename, EC, sys::fs::OF_Text)); if (EC) { errs() << EC.message() << '\n'; return 1; diff --git a/tools/llvm-dwarfdump/Statistics.cpp b/tools/llvm-dwarfdump/Statistics.cpp index f26369b935cb..c29ad783a9e6 100644 --- a/tools/llvm-dwarfdump/Statistics.cpp +++ b/tools/llvm-dwarfdump/Statistics.cpp @@ -5,11 +5,18 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Support/JSON.h" #define DEBUG_TYPE "dwarfdump" using namespace llvm; using namespace object; +/// This represents the number of categories of debug location coverage being +/// calculated. The first category is the number of variables with 0% location +/// coverage, but the last category is the number of variables with 100% +/// location coverage. +constexpr int NumOfCoverageCategories = 12; + /// Holds statistics for one function (or other entity that has a PC range and /// contains variables, such as a compile unit). struct PerFunctionStats { @@ -43,9 +50,9 @@ struct PerFunctionStats { unsigned NumVars = 0; /// Number of variables with source location. unsigned NumVarSourceLocations = 0; - /// Number of variables wtih type. + /// Number of variables with type. unsigned NumVarTypes = 0; - /// Number of variables wtih DW_AT_location. + /// Number of variables with DW_AT_location. unsigned NumVarLocations = 0; }; @@ -56,16 +63,74 @@ struct GlobalStats { /// Total number of PC range bytes in each variable's enclosing scope, /// starting from the first definition of the variable. unsigned ScopeBytesFromFirstDefinition = 0; - /// Total number of call site entries (DW_TAG_call_site) or - /// (DW_AT_call_file & DW_AT_call_line). + /// Total number of PC range bytes covered by DW_AT_locations with + /// the debug entry values (DW_OP_entry_value). + unsigned ScopeEntryValueBytesCovered = 0; + /// 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, + /// starting from the first definition of the variable (only for parameters). + unsigned ParamScopeBytesFromFirstDefinition = 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, + /// starting from the first definition of the variable (only for local + /// variables). + unsigned VarScopeBytesFromFirstDefinition = 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; + /// 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). + unsigned CallSiteDIEs = 0; + /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter). + unsigned CallSiteParamDIEs = 0; /// Total byte size of concrete functions. This byte size includes /// inline functions contained in the concrete functions. - uint64_t FunctionSize = 0; + unsigned FunctionSize = 0; /// Total byte size of inlined functions. This is the total number of bytes /// for the top inline functions within concrete functions. This can help /// tune the inline settings when compiling to match user expectations. - uint64_t InlineFunctionSize = 0; + unsigned InlineFunctionSize = 0; +}; + +/// Holds accumulated debug location statistics about local variables and +/// formal parameters. +struct LocationStats { + /// Map the scope coverage decile to the number of variables in the decile. + /// The first element of the array (at the index zero) represents the number + /// of variables with the no debug location at all, but the last element + /// in the vector represents the number of fully covered variables within + /// its scope. + std::vector<unsigned> VarParamLocStats{ + std::vector<unsigned>(NumOfCoverageCategories, 0)}; + /// Map non debug entry values coverage. + std::vector<unsigned> VarParamNonEntryValLocStats{ + std::vector<unsigned>(NumOfCoverageCategories, 0)}; + /// The debug location statistics for formal parameters. + std::vector<unsigned> ParamLocStats{ + std::vector<unsigned>(NumOfCoverageCategories, 0)}; + /// Map non debug entry values coverage for formal parameters. + std::vector<unsigned> ParamNonEntryValLocStats{ + std::vector<unsigned>(NumOfCoverageCategories, 0)}; + /// The debug location statistics for local variables. + std::vector<unsigned> VarLocStats{ + std::vector<unsigned>(NumOfCoverageCategories, 0)}; + /// Map non debug entry values coverage for local variables. + std::vector<unsigned> VarNonEntryValLocStats{ + std::vector<unsigned>(NumOfCoverageCategories, 0)}; + /// Total number of local variables and function parameters processed. + unsigned NumVarParam = 0; + /// Total number of formal parameters processed. + unsigned NumParam = 0; + /// Total number of local variables processed. + unsigned NumVar = 0; }; /// Extract the low pc from a Die. @@ -81,27 +146,66 @@ static uint64_t getLowPC(DWARFDie Die) { return dwarf::toAddress(Die.find(dwarf::DW_AT_low_pc), 0); } +/// 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) { + auto getCoverageBucket = [BytesCovered, BytesInScope]() -> unsigned { + unsigned LocBucket = 100 * (double)BytesCovered / BytesInScope; + if (LocBucket == 0) { + // No debug location at all for the variable. + return 0; + } else if (LocBucket == 100 || BytesCovered > BytesInScope) { + // Fully covered variable within its scope. + return NumOfCoverageCategories - 1; + } else { + // Get covered range (e.g. 20%-29%). + LocBucket /= 10; + return LocBucket + 1; + } + }; + + unsigned CoverageBucket = getCoverageBucket(); + VarParamLocStats[CoverageBucket]++; + if (IsParam) + ParamLocStats[CoverageBucket]++; + else if (IsLocalVar) + VarLocStats[CoverageBucket]++; +} + /// Collect debug info quality metrics for one DIE. -static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, +static void collectStatsForDie(DWARFDie Die, uint64_t UnitLowPC, std::string FnPrefix, std::string VarPrefix, uint64_t ScopeLowPC, uint64_t BytesInScope, uint32_t InlineDepth, StringMap<PerFunctionStats> &FnStatMap, - GlobalStats &GlobalStats) { + GlobalStats &GlobalStats, + LocationStats &LocStats) { bool HasLoc = false; bool HasSrcLoc = false; bool HasType = false; bool IsArtificial = false; uint64_t BytesCovered = 0; + uint64_t BytesEntryValuesCovered = 0; uint64_t OffsetToFirstDefinition = 0; + auto &FnStats = FnStatMap[FnPrefix]; + bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter; + bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable; + + if (Die.getTag() == dwarf::DW_TAG_call_site || + Die.getTag() == dwarf::DW_TAG_GNU_call_site) { + GlobalStats.CallSiteDIEs++; + return; + } - if (Die.getTag() == dwarf::DW_TAG_call_site) { - GlobalStats.CallSiteEntries++; + if (Die.getTag() == dwarf::DW_TAG_call_site_parameter || + Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) { + GlobalStats.CallSiteParamDIEs++; return; } - if (Die.getTag() != dwarf::DW_TAG_formal_parameter && - Die.getTag() != dwarf::DW_TAG_variable && - Die.getTag() != dwarf::DW_TAG_member) { + if (!IsParam && !IsLocalVar && Die.getTag() != dwarf::DW_TAG_member) { // Not a variable or constant member. return; } @@ -116,6 +220,19 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, 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()); + // Consider the expression containing the DW_OP_entry_value as + // an entry value. + return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { + return Op.getCode() == dwarf::DW_OP_entry_value || + Op.getCode() == dwarf::DW_OP_GNU_entry_value; + }); + }; + if (Die.find(dwarf::DW_AT_const_value)) { // This catches constant members *and* variables. HasLoc = true; @@ -133,11 +250,15 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, if (auto DebugLocOffset = FormValue->getAsSectionOffset()) { auto *DebugLoc = Die.getDwarfUnit()->getContext().getDebugLoc(); if (auto List = DebugLoc->getLocationListAtOffset(*DebugLocOffset)) { - for (auto Entry : List->Entries) - BytesCovered += Entry.End - Entry.Begin; + for (auto Entry : List->Entries) { + uint64_t BytesEntryCovered = Entry.End - Entry.Begin; + BytesCovered += BytesEntryCovered; + if (IsEntryValue(Entry.Loc)) + BytesEntryValuesCovered += BytesEntryCovered; + } if (List->Entries.size()) { uint64_t FirstDef = List->Entries[0].Begin; - uint64_t UnitOfs = getLowPC(Die.getDwarfUnit()->getUnitDIE()); + uint64_t UnitOfs = UnitLowPC; // Ranges sometimes start before the lexical scope. if (UnitOfs + FirstDef >= ScopeLowPC) OffsetToFirstDefinition = UnitOfs + FirstDef - ScopeLowPC; @@ -154,8 +275,25 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, } } + // Calculate the debug location statistics. + if (BytesInScope) { + LocStats.NumVarParam++; + if (IsParam) + LocStats.NumParam++; + else if (IsLocalVar) + LocStats.NumVar++; + + collectLocStats(BytesCovered, BytesInScope, LocStats.VarParamLocStats, + LocStats.ParamLocStats, LocStats.VarLocStats, IsParam, + IsLocalVar); + // Non debug entry values coverage statistics. + collectLocStats(BytesCovered - BytesEntryValuesCovered, BytesInScope, + LocStats.VarParamNonEntryValLocStats, + LocStats.ParamNonEntryValLocStats, + LocStats.VarNonEntryValLocStats, IsParam, IsLocalVar); + } + // Collect PC range coverage data. - auto &FnStats = FnStatMap[FnPrefix]; if (DWARFDie D = Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) Die = D; @@ -171,6 +309,17 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, // Turns out we have a lot of ranges that extend past the lexical scope. GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered); GlobalStats.ScopeBytesFromFirstDefinition += BytesInScope; + GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered; + if (IsParam) { + GlobalStats.ParamScopeBytesCovered += + std::min(BytesInScope, BytesCovered); + GlobalStats.ParamScopeBytesFromFirstDefinition += BytesInScope; + GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered; + } else if (IsLocalVar) { + GlobalStats.VarScopeBytesCovered += std::min(BytesInScope, BytesCovered); + GlobalStats.VarScopeBytesFromFirstDefinition += BytesInScope; + GlobalStats.VarScopeEntryValueBytesCovered += BytesEntryValuesCovered; + } assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytesFromFirstDefinition); } else if (Die.getTag() == dwarf::DW_TAG_member) { @@ -179,7 +328,7 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, FnStats.TotalVarWithLoc += (unsigned)HasLoc; } if (!IsArtificial) { - if (Die.getTag() == dwarf::DW_TAG_formal_parameter) { + if (IsParam) { FnStats.NumParams++; if (HasType) FnStats.NumParamTypes++; @@ -187,7 +336,7 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, FnStats.NumParamSourceLocations++; if (HasLoc) FnStats.NumParamLocations++; - } else if (Die.getTag() == dwarf::DW_TAG_variable) { + } else if (IsLocalVar) { FnStats.NumVars++; if (HasType) FnStats.NumVarTypes++; @@ -200,11 +349,12 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, } /// Recursively collect debug info quality metrics. -static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, +static void collectStatsRecursive(DWARFDie Die, uint64_t UnitLowPC, std::string FnPrefix, std::string VarPrefix, uint64_t ScopeLowPC, uint64_t BytesInScope, uint32_t InlineDepth, StringMap<PerFunctionStats> &FnStatMap, - GlobalStats &GlobalStats) { + GlobalStats &GlobalStats, + LocationStats &LocStats) { // Handle any kind of lexical scope. const dwarf::Tag Tag = Die.getTag(); const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; @@ -272,8 +422,8 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, } } else { // Not a scope, visit the Die itself. It could be a variable. - collectStatsForDie(Die, FnPrefix, VarPrefix, ScopeLowPC, BytesInScope, - InlineDepth, FnStatMap, GlobalStats); + collectStatsForDie(Die, UnitLowPC, FnPrefix, VarPrefix, ScopeLowPC, BytesInScope, + InlineDepth, FnStatMap, GlobalStats, LocStats); } // Set InlineDepth correctly for child recursion @@ -290,8 +440,9 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, if (Child.getTag() == dwarf::DW_TAG_lexical_block) ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; - collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, ScopeLowPC, - BytesInScope, InlineDepth, FnStatMap, GlobalStats); + collectStatsRecursive(Child, UnitLowPC, FnPrefix, ChildVarPrefix, ScopeLowPC, + BytesInScope, InlineDepth, FnStatMap, GlobalStats, + LocStats); Child = Child.getSibling(); } } @@ -299,14 +450,33 @@ static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, /// Print machine-readable output. /// The machine-readable format is single-line JSON output. /// \{ -static void printDatum(raw_ostream &OS, const char *Key, StringRef Value) { - OS << ",\"" << Key << "\":\"" << Value << '"'; - LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); -} -static void printDatum(raw_ostream &OS, const char *Key, uint64_t Value) { +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, + std::vector<unsigned> &LocationStats) { + OS << ",\"" << Key << " with 0% of its scope covered\":" + << LocationStats[0]; + LLVM_DEBUG(llvm::dbgs() << Key << " with 0% of its scope covered: " + << LocationStats[0] << '\n'); + OS << ",\"" << Key << " with 1-9% of its scope covered\":" + << LocationStats[1]; + LLVM_DEBUG(llvm::dbgs() << Key << " with 1-9% of its scope covered: " + << LocationStats[1] << '\n'); + for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { + OS << ",\"" << Key << " with " << (i - 1) * 10 << "-" << i * 10 - 1 + << "% of its scope covered\":" << LocationStats[i]; + LLVM_DEBUG(llvm::dbgs() + << Key << " with " << (i - 1) * 10 << "-" << i * 10 - 1 + << "% of its scope covered: " << LocationStats[i]); + } + OS << ",\"" << Key << " with 100% of its scope covered\":" + << LocationStats[NumOfCoverageCategories - 1]; + LLVM_DEBUG(llvm::dbgs() << Key << " with 100% of its scope covered: " + << LocationStats[NumOfCoverageCategories - 1]); +} /// \} /// Collect debug info quality metrics for an entire DIContext. @@ -321,10 +491,12 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename, raw_ostream &OS) { StringRef FormatName = Obj.getFileFormatName(); GlobalStats GlobalStats; + LocationStats LocStats; StringMap<PerFunctionStats> Statistics; for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) - collectStatsRecursive(CUDie, "/", "g", 0, 0, 0, Statistics, GlobalStats); + collectStatsRecursive(CUDie, getLowPC(CUDie), "/", "g", 0, 0, 0, + Statistics, GlobalStats, LocStats); /// The version number should be increased every time the algorithm is changed /// (including bug fixes). New metrics may be added without increasing the @@ -387,9 +559,24 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 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.ScopeBytesFromFirstDefinition); printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); + printDatum(OS, "entry value scope bytes covered", + GlobalStats.ScopeEntryValueBytesCovered); + printDatum(OS, "formal params scope bytes total", + GlobalStats.ParamScopeBytesFromFirstDefinition); + printDatum(OS, "formal params scope bytes covered", + GlobalStats.ParamScopeBytesCovered); + printDatum(OS, "formal params entry value scope bytes covered", + GlobalStats.ParamScopeEntryValueBytesCovered); + printDatum(OS, "vars scope bytes total", + GlobalStats.VarScopeBytesFromFirstDefinition); + 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); @@ -400,6 +587,20 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 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", + LocStats.NumVarParam); + printLocationStats(OS, "variables", LocStats.VarParamLocStats); + printLocationStats(OS, "variables (excluding the debug 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)", + 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); OS << "}\n"; LLVM_DEBUG( llvm::dbgs() << "Total Availability: " diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index 05a7aef67ece..e20f6041f98d 100644 --- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -584,7 +584,7 @@ int main(int argc, char **argv) { } std::error_code EC; - ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_None); + ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_Text); error("Unable to open output file" + OutputFilename, EC); // Don't remove output file if we exit with an error. OutputFile.keep(); diff --git a/tools/llvm-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp index 300bc0b4bd52..dddc0d9baa08 100644 --- a/tools/llvm-extract/llvm-extract.cpp +++ b/tools/llvm-extract/llvm-extract.cpp @@ -74,8 +74,18 @@ static cl::list<std::string> // ExtractBlocks - The blocks to extract from the module. static cl::list<std::string> ExtractBlocks( - "bb", cl::desc("Specify <function, basic block> pairs to extract"), - cl::ZeroOrMore, cl::value_desc("function:bb"), cl::cat(ExtractCat)); + "bb", + cl::desc( + "Specify <function, basic block1[;basic block2...]> pairs to extract.\n" + "Each pair will create a function.\n" + "If multiple basic blocks are specified in one pair,\n" + "the first block in the sequence should dominate the rest.\n" + "eg:\n" + " --bb=f:bb1;bb2 will extract one function with both bb1 and bb2;\n" + " --bb=f:bb1 --bb=f:bb2 will extract two functions, one with bb1, one " + "with bb2."), + cl::ZeroOrMore, cl::value_desc("function:bb1[;bb2...]"), + cl::cat(ExtractCat)); // ExtractAlias - The alias to extract from the module. static cl::list<std::string> @@ -350,7 +360,7 @@ int main(int argc, char **argv) { Passes.add(createStripDeadPrototypesPass()); // Remove dead func decls std::error_code EC; - ToolOutputFile Out(OutputFilename, EC, sys::fs::F_None); + ToolOutputFile Out(OutputFilename, EC, sys::fs::OF_None); if (EC) { errs() << EC.message() << '\n'; return 1; diff --git a/tools/llvm-ifs/CMakeLists.txt b/tools/llvm-ifs/CMakeLists.txt new file mode 100644 index 000000000000..544b0e41a5ed --- /dev/null +++ b/tools/llvm-ifs/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + Object + Support + TextAPI + ObjectYAML + ) + +add_llvm_tool(llvm-ifs + llvm-ifs.cpp + ) diff --git a/tools/llvm-ifs/LLVMBuild.txt b/tools/llvm-ifs/LLVMBuild.txt new file mode 100644 index 000000000000..10dc6bd8f550 --- /dev/null +++ b/tools/llvm-ifs/LLVMBuild.txt @@ -0,0 +1,21 @@ +;===- ./tools/llvm-ifs/LLVMBuild.txt ---------------------------*- Conf -*--===; +; +; Part of the LLVM Project, under the Apache 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 is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-ifs +parent = Tools +required_libraries = Object Support TextAPI diff --git a/tools/llvm-ifs/llvm-ifs.cpp b/tools/llvm-ifs/llvm-ifs.cpp new file mode 100644 index 000000000000..f329b4633632 --- /dev/null +++ b/tools/llvm-ifs/llvm-ifs.cpp @@ -0,0 +1,532 @@ +//===- llvm-ifs.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 "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/VersionTuple.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TextAPI/MachO/InterfaceFile.h" +#include "llvm/TextAPI/MachO/TextAPIReader.h" +#include "llvm/TextAPI/MachO/TextAPIWriter.h" +#include <set> +#include <string> + +using namespace llvm; +using namespace llvm::yaml; +using namespace llvm::MachO; + +#define DEBUG_TYPE "llvm-ifs" + +namespace { +const VersionTuple IFSVersionCurrent(1, 2); +} + +static cl::opt<std::string> Action("action", cl::desc("<llvm-ifs action>"), + cl::value_desc("write-ifs | write-bin"), + cl::init("write-ifs")); + +static cl::opt<std::string> ForceFormat("force-format", + cl::desc("<force object format>"), + cl::value_desc("ELF | TBD"), + cl::init("")); + +static cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input ifs files>"), + cl::ZeroOrMore); + +static cl::opt<std::string> OutputFilename("o", cl::desc("<output file>"), + cl::value_desc("path")); + +enum class IFSSymbolType { + NoType = 0, + Object, + Func, + // Type information is 4 bits, so 16 is safely out of range. + Unknown = 16, +}; + +std::string getTypeName(IFSSymbolType Type) { + switch (Type) { + case IFSSymbolType::NoType: + return "NoType"; + case IFSSymbolType::Func: + return "Func"; + case IFSSymbolType::Object: + return "Object"; + case IFSSymbolType::Unknown: + return "Unknown"; + } + llvm_unreachable("Unexpected ifs symbol type."); +} + +struct IFSSymbol { + IFSSymbol(std::string SymbolName) : Name(SymbolName) {} + std::string Name; + uint64_t Size; + IFSSymbolType Type; + bool Weak; + Optional<std::string> Warning; + bool operator<(const IFSSymbol &RHS) const { return Name < RHS.Name; } +}; + +namespace llvm { +namespace yaml { +/// YAML traits for IFSSymbolType. +template <> struct ScalarEnumerationTraits<IFSSymbolType> { + static void enumeration(IO &IO, IFSSymbolType &SymbolType) { + IO.enumCase(SymbolType, "NoType", IFSSymbolType::NoType); + IO.enumCase(SymbolType, "Func", IFSSymbolType::Func); + IO.enumCase(SymbolType, "Object", IFSSymbolType::Object); + IO.enumCase(SymbolType, "Unknown", IFSSymbolType::Unknown); + // Treat other symbol types as noise, and map to Unknown. + if (!IO.outputting() && IO.matchEnumFallback()) + SymbolType = IFSSymbolType::Unknown; + } +}; + +template <> struct ScalarTraits<VersionTuple> { + static void output(const VersionTuple &Value, void *, + llvm::raw_ostream &Out) { + Out << Value.getAsString(); + } + + static StringRef input(StringRef Scalar, void *, VersionTuple &Value) { + if (Value.tryParse(Scalar)) + return StringRef("Can't parse version: invalid version format."); + + if (Value > IFSVersionCurrent) + return StringRef("Unsupported IFS version."); + + // Returning empty StringRef indicates successful parse. + return StringRef(); + } + + // Don't place quotation marks around version value. + static QuotingType mustQuote(StringRef) { return QuotingType::None; } +}; + +/// YAML traits for IFSSymbol. +template <> struct MappingTraits<IFSSymbol> { + static void mapping(IO &IO, IFSSymbol &Symbol) { + IO.mapRequired("Type", Symbol.Type); + // The need for symbol size depends on the symbol type. + if (Symbol.Type == IFSSymbolType::NoType) + IO.mapOptional("Size", Symbol.Size, (uint64_t)0); + else if (Symbol.Type == IFSSymbolType::Func) + Symbol.Size = 0; + else + IO.mapRequired("Size", Symbol.Size); + IO.mapOptional("Weak", Symbol.Weak, false); + IO.mapOptional("Warning", Symbol.Warning); + } + + // Compacts symbol information into a single line. + static const bool flow = true; +}; + +/// YAML traits for set of IFSSymbols. +template <> struct CustomMappingTraits<std::set<IFSSymbol>> { + static void inputOne(IO &IO, StringRef Key, std::set<IFSSymbol> &Set) { + std::string Name = Key.str(); + IFSSymbol Sym(Name); + IO.mapRequired(Name.c_str(), Sym); + Set.insert(Sym); + } + + static void output(IO &IO, std::set<IFSSymbol> &Set) { + for (auto &Sym : Set) + IO.mapRequired(Sym.Name.c_str(), const_cast<IFSSymbol &>(Sym)); + } +}; +} // namespace yaml +} // namespace llvm + +// A cumulative representation of ELF stubs. +// Both textual and binary stubs will read into and write from this object. +class IFSStub { + // TODO: Add support for symbol versioning. +public: + VersionTuple IfsVersion; + std::string Triple; + std::string ObjectFileFormat; + Optional<std::string> SOName; + std::vector<std::string> NeededLibs; + std::set<IFSSymbol> Symbols; + + IFSStub() = default; + IFSStub(const IFSStub &Stub) + : IfsVersion(Stub.IfsVersion), Triple(Stub.Triple), + ObjectFileFormat(Stub.ObjectFileFormat), SOName(Stub.SOName), + NeededLibs(Stub.NeededLibs), Symbols(Stub.Symbols) {} + IFSStub(IFSStub &&Stub) + : IfsVersion(std::move(Stub.IfsVersion)), Triple(std::move(Stub.Triple)), + ObjectFileFormat(std::move(Stub.ObjectFileFormat)), + SOName(std::move(Stub.SOName)), NeededLibs(std::move(Stub.NeededLibs)), + Symbols(std::move(Stub.Symbols)) {} +}; + +namespace llvm { +namespace yaml { +/// YAML traits for IFSStub objects. +template <> struct MappingTraits<IFSStub> { + static void mapping(IO &IO, IFSStub &Stub) { + if (!IO.mapTag("!experimental-ifs-v1", true)) + IO.setError("Not a .ifs YAML file."); + IO.mapRequired("IfsVersion", Stub.IfsVersion); + IO.mapOptional("Triple", Stub.Triple); + IO.mapOptional("ObjectFileFormat", Stub.ObjectFileFormat); + IO.mapOptional("SOName", Stub.SOName); + IO.mapOptional("NeededLibs", Stub.NeededLibs); + IO.mapRequired("Symbols", Stub.Symbols); + } +}; +} // namespace yaml +} // namespace llvm + +static Expected<std::unique_ptr<IFSStub>> readInputFile(StringRef FilePath) { + // Read in file. + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError = + MemoryBuffer::getFileOrSTDIN(FilePath); + if (!BufOrError) + return createStringError(BufOrError.getError(), "Could not open `%s`", + FilePath.data()); + + std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError); + yaml::Input YamlIn(FileReadBuffer->getBuffer()); + std::unique_ptr<IFSStub> Stub(new IFSStub()); + YamlIn >> *Stub; + + if (std::error_code Err = YamlIn.error()) + return createStringError(Err, "Failed reading Interface Stub File."); + + return std::move(Stub); +} + +int writeTbdStub(const llvm::Triple &T, const std::set<IFSSymbol> &Symbols, + const StringRef Format, raw_ostream &Out) { + + auto PlatformKindOrError = + [](const llvm::Triple &T) -> llvm::Expected<llvm::MachO::PlatformKind> { + if (T.isMacOSX()) + return llvm::MachO::PlatformKind::macOS; + if (T.isTvOS()) + return llvm::MachO::PlatformKind::tvOS; + if (T.isWatchOS()) + return llvm::MachO::PlatformKind::watchOS; + // Note: put isiOS last because tvOS and watchOS are also iOS according + // to the Triple. + if (T.isiOS()) + return llvm::MachO::PlatformKind::iOS; + + // TODO: Add an option for ForceTriple, but keep ForceFormat for now. + if (ForceFormat == "TBD") + return llvm::MachO::PlatformKind::macOS; + + return createStringError(errc::not_supported, "Invalid Platform.\n"); + }(T); + + if (!PlatformKindOrError) + return -1; + + PlatformKind Plat = PlatformKindOrError.get(); + TargetList Targets({Target(llvm::MachO::mapToArchitecture(T), Plat)}); + + InterfaceFile File; + File.setFileType(FileType::TBD_V3); // Only supporting v3 for now. + File.addTargets(Targets); + + for (const auto &Symbol : Symbols) { + auto Name = Symbol.Name; + auto Kind = SymbolKind::GlobalSymbol; + switch (Symbol.Type) { + default: + case IFSSymbolType::NoType: + Kind = SymbolKind::GlobalSymbol; + break; + case IFSSymbolType::Object: + Kind = SymbolKind::GlobalSymbol; + break; + case IFSSymbolType::Func: + Kind = SymbolKind::GlobalSymbol; + break; + } + if (Symbol.Weak) + File.addSymbol(Kind, Name, Targets, SymbolFlags::WeakDefined); + else + File.addSymbol(Kind, Name, Targets); + } + + SmallString<4096> Buffer; + raw_svector_ostream OS(Buffer); + if (Error Result = TextAPIWriter::writeToStream(OS, File)) + return -1; + Out << OS.str(); + return 0; +} + +int writeElfStub(const llvm::Triple &T, const std::set<IFSSymbol> &Symbols, + const StringRef Format, raw_ostream &Out) { + SmallString<0> Storage; + Storage.clear(); + raw_svector_ostream OS(Storage); + + OS << "--- !ELF\n"; + OS << "FileHeader:\n"; + OS << " Class: ELFCLASS"; + OS << (T.isArch64Bit() ? "64" : "32"); + OS << "\n"; + OS << " Data: ELFDATA2"; + OS << (T.isLittleEndian() ? "LSB" : "MSB"); + OS << "\n"; + OS << " Type: ET_DYN\n"; + OS << " Machine: " + << llvm::StringSwitch<llvm::StringRef>(T.getArchName()) + .Case("x86_64", "EM_X86_64") + .Case("i386", "EM_386") + .Case("i686", "EM_386") + .Case("aarch64", "EM_AARCH64") + .Case("amdgcn", "EM_AMDGPU") + .Case("r600", "EM_AMDGPU") + .Case("arm", "EM_ARM") + .Case("thumb", "EM_ARM") + .Case("avr", "EM_AVR") + .Case("mips", "EM_MIPS") + .Case("mipsel", "EM_MIPS") + .Case("mips64", "EM_MIPS") + .Case("mips64el", "EM_MIPS") + .Case("msp430", "EM_MSP430") + .Case("ppc", "EM_PPC") + .Case("ppc64", "EM_PPC64") + .Case("ppc64le", "EM_PPC64") + .Case("x86", T.isOSIAMCU() ? "EM_IAMCU" : "EM_386") + .Case("x86_64", "EM_X86_64") + .Default("EM_NONE") + << "\nSections:" + << "\n - Name: .text" + << "\n Type: SHT_PROGBITS" + << "\n - Name: .data" + << "\n Type: SHT_PROGBITS" + << "\n - Name: .rodata" + << "\n Type: SHT_PROGBITS" + << "\nSymbols:\n"; + for (const auto &Symbol : Symbols) { + OS << " - Name: " << Symbol.Name << "\n" + << " Type: STT_"; + switch (Symbol.Type) { + default: + case IFSSymbolType::NoType: + OS << "NOTYPE"; + break; + case IFSSymbolType::Object: + OS << "OBJECT"; + break; + case IFSSymbolType::Func: + OS << "FUNC"; + break; + } + OS << "\n Section: .text" + << "\n Binding: STB_" << (Symbol.Weak ? "WEAK" : "GLOBAL") + << "\n"; + } + OS << "...\n"; + + std::string YamlStr = OS.str(); + + // Only or debugging. Not an offical format. + LLVM_DEBUG({ + if (ForceFormat == "ELFOBJYAML") { + Out << YamlStr; + return 0; + } + }); + + yaml::Input YIn(YamlStr); + auto ErrHandler = [](const Twine &Msg) { + WithColor::error(errs(), "llvm-ifs") << Msg << "\n"; + }; + return convertYAML(YIn, Out, ErrHandler) ? 0 : 1; +} + +int writeIfso(const IFSStub &Stub, bool IsWriteIfs, raw_ostream &Out) { + if (IsWriteIfs) { + yaml::Output YamlOut(Out, NULL, /*WrapColumn =*/0); + YamlOut << const_cast<IFSStub &>(Stub); + return 0; + } + + std::string ObjectFileFormat = + ForceFormat.empty() ? Stub.ObjectFileFormat : ForceFormat; + + if (ObjectFileFormat == "ELF" || ForceFormat == "ELFOBJYAML") + return writeElfStub(llvm::Triple(Stub.Triple), Stub.Symbols, + Stub.ObjectFileFormat, Out); + if (ObjectFileFormat == "TBD") + return writeTbdStub(llvm::Triple(Stub.Triple), Stub.Symbols, + Stub.ObjectFileFormat, Out); + + WithColor::error() + << "Invalid ObjectFileFormat: Only ELF and TBD are supported.\n"; + return -1; +} + +// New Interface Stubs Yaml Format: +// --- !experimental-ifs-v1 +// IfsVersion: 1.0 +// Triple: <llvm triple> +// ObjectFileFormat: <ELF | others not yet supported> +// Symbols: +// _ZSymbolName: { Type: <type> } +// ... + +int main(int argc, char *argv[]) { + // Parse arguments. + cl::ParseCommandLineOptions(argc, argv); + + if (InputFilenames.empty()) + InputFilenames.push_back("-"); + + IFSStub Stub; + std::map<std::string, IFSSymbol> SymbolMap; + + std::string PreviousInputFilePath = ""; + for (const std::string &InputFilePath : InputFilenames) { + Expected<std::unique_ptr<IFSStub>> StubOrErr = readInputFile(InputFilePath); + if (!StubOrErr) { + WithColor::error() << StubOrErr.takeError() << "\n"; + return -1; + } + std::unique_ptr<IFSStub> TargetStub = std::move(StubOrErr.get()); + + if (Stub.Triple.empty()) { + PreviousInputFilePath = InputFilePath; + Stub.IfsVersion = TargetStub->IfsVersion; + Stub.Triple = TargetStub->Triple; + Stub.ObjectFileFormat = TargetStub->ObjectFileFormat; + Stub.SOName = TargetStub->SOName; + Stub.NeededLibs = TargetStub->NeededLibs; + } else { + if (Stub.IfsVersion != TargetStub->IfsVersion) { + if (Stub.IfsVersion.getMajor() != IFSVersionCurrent.getMajor()) { + WithColor::error() + << "Interface Stub: IfsVersion Mismatch." + << "\nFilenames: " << PreviousInputFilePath << " " + << InputFilePath << "\nIfsVersion Values: " << Stub.IfsVersion + << " " << TargetStub->IfsVersion << "\n"; + return -1; + } + if (TargetStub->IfsVersion > Stub.IfsVersion) + Stub.IfsVersion = TargetStub->IfsVersion; + } + if (Stub.ObjectFileFormat != TargetStub->ObjectFileFormat) { + WithColor::error() << "Interface Stub: ObjectFileFormat Mismatch." + << "\nFilenames: " << PreviousInputFilePath << " " + << InputFilePath << "\nObjectFileFormat Values: " + << Stub.ObjectFileFormat << " " + << TargetStub->ObjectFileFormat << "\n"; + return -1; + } + if (Stub.Triple != TargetStub->Triple) { + WithColor::error() << "Interface Stub: Triple Mismatch." + << "\nFilenames: " << PreviousInputFilePath << " " + << InputFilePath + << "\nTriple Values: " << Stub.Triple << " " + << TargetStub->Triple << "\n"; + return -1; + } + if (Stub.SOName != TargetStub->SOName) { + WithColor::error() << "Interface Stub: SOName Mismatch." + << "\nFilenames: " << PreviousInputFilePath << " " + << InputFilePath + << "\nSOName Values: " << Stub.SOName << " " + << TargetStub->SOName << "\n"; + return -1; + } + if (Stub.NeededLibs != TargetStub->NeededLibs) { + WithColor::error() << "Interface Stub: NeededLibs Mismatch." + << "\nFilenames: " << PreviousInputFilePath << " " + << InputFilePath << "\n"; + return -1; + } + } + + for (auto Symbol : TargetStub->Symbols) { + auto SI = SymbolMap.find(Symbol.Name); + if (SI == SymbolMap.end()) { + SymbolMap.insert( + std::pair<std::string, IFSSymbol>(Symbol.Name, Symbol)); + continue; + } + + assert(Symbol.Name == SI->second.Name && "Symbol Names Must Match."); + + // Check conflicts: + if (Symbol.Type != SI->second.Type) { + WithColor::error() << "Interface Stub: Type Mismatch for " + << Symbol.Name << ".\nFilename: " << InputFilePath + << "\nType Values: " << getTypeName(SI->second.Type) + << " " << getTypeName(Symbol.Type) << "\n"; + + return -1; + } + if (Symbol.Size != SI->second.Size) { + WithColor::error() << "Interface Stub: Size Mismatch for " + << Symbol.Name << ".\nFilename: " << InputFilePath + << "\nSize Values: " << SI->second.Size << " " + << Symbol.Size << "\n"; + + return -1; + } + if (Symbol.Weak != SI->second.Weak) { + // TODO: Add conflict resolution for Weak vs non-Weak. + WithColor::error() << "Interface Stub: Weak Mismatch for " + << Symbol.Name << ".\nFilename: " << InputFilePath + << "\nWeak Values: " << SI->second.Weak << " " + << Symbol.Weak << "\n"; + + return -1; + } + // TODO: Not checking Warning. Will be dropped. + } + + PreviousInputFilePath = InputFilePath; + } + + if (Stub.IfsVersion != IFSVersionCurrent) + if (Stub.IfsVersion.getMajor() != IFSVersionCurrent.getMajor()) { + WithColor::error() << "Interface Stub: Bad IfsVersion: " + << Stub.IfsVersion << ", llvm-ifs supported version: " + << IFSVersionCurrent << ".\n"; + return -1; + } + + for (auto &Entry : SymbolMap) + Stub.Symbols.insert(Entry.second); + + std::error_code SysErr; + + // Open file for writing. + raw_fd_ostream Out(OutputFilename, SysErr); + if (SysErr) { + WithColor::error() << "Couldn't open " << OutputFilename + << " for writing.\n"; + return -1; + } + + return writeIfso(Stub, (Action == "write-ifs"), Out); +} diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp index 50ba57178d02..fa36e083b6f8 100644 --- a/tools/llvm-link/llvm-link.cpp +++ b/tools/llvm-link/llvm-link.cpp @@ -351,13 +351,13 @@ int main(int argc, char **argv) { LLVMContext Context; Context.setDiagnosticHandler( - llvm::make_unique<LLVMLinkDiagnosticHandler>(), true); + std::make_unique<LLVMLinkDiagnosticHandler>(), true); cl::ParseCommandLineOptions(argc, argv, "llvm linker\n"); if (!DisableDITypeMap) Context.enableDebugTypeODRUniquing(); - auto Composite = make_unique<Module>("llvm-link", Context); + auto Composite = std::make_unique<Module>("llvm-link", Context); Linker L(*Composite); unsigned Flags = Linker::Flags::None; @@ -381,7 +381,7 @@ int main(int argc, char **argv) { errs() << "Here's the assembly:\n" << *Composite; std::error_code EC; - ToolOutputFile Out(OutputFilename, EC, sys::fs::F_None); + ToolOutputFile Out(OutputFilename, EC, sys::fs::OF_None); if (EC) { WithColor::error() << EC.message() << '\n'; return 1; diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp index 585207b25185..b47e68e82850 100644 --- a/tools/llvm-lto/llvm-lto.cpp +++ b/tools/llvm-lto/llvm-lto.cpp @@ -315,8 +315,8 @@ getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer, error(BufferOrErr, "error loading file '" + Path + "'"); Buffer = std::move(BufferOrErr.get()); CurrentActivity = ("loading file '" + Path + "'").str(); - std::unique_ptr<LLVMContext> Context = llvm::make_unique<LLVMContext>(); - Context->setDiagnosticHandler(llvm::make_unique<LLVMLTODiagnosticHandler>(), + std::unique_ptr<LLVMContext> Context = std::make_unique<LLVMContext>(); + Context->setDiagnosticHandler(std::make_unique<LLVMLTODiagnosticHandler>(), true); ErrorOr<std::unique_ptr<LTOModule>> Ret = LTOModule::createInLocalContext( std::move(Context), Buffer->getBufferStart(), Buffer->getBufferSize(), @@ -420,7 +420,7 @@ static void createCombinedModuleSummaryIndex() { std::error_code EC; assert(!OutputFilename.empty()); raw_fd_ostream OS(OutputFilename + ".thinlto.bc", EC, - sys::fs::OpenFlags::F_None); + sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + OutputFilename + ".thinlto.bc'"); WriteIndexToFile(CombinedIndex, OS); OS.close(); @@ -510,7 +510,7 @@ static std::unique_ptr<Module> loadModuleFromInput(lto::InputFile &File, static void writeModuleToFile(Module &TheModule, StringRef Filename) { std::error_code EC; - raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::F_None); + raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + Filename + "'"); maybeVerifyModule(TheModule); WriteBitcodeToFile(TheModule, OS, /* ShouldPreserveUseListOrder */ true); @@ -581,7 +581,7 @@ private: if (!CombinedIndex) report_fatal_error("ThinLink didn't create an index"); std::error_code EC; - raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::F_None); + raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + OutputFilename + "'"); WriteIndexToFile(*CombinedIndex, OS); } @@ -619,7 +619,7 @@ private: } OutputName = getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix); std::error_code EC; - raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::F_None); + raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + OutputName + "'"); WriteIndexToFile(*Index, OS, &ModuleToSummariesForIndex); } @@ -802,7 +802,7 @@ private: } std::error_code EC; - raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::F_None); + raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + OutputName + "'"); OS << std::get<0>(BinName)->getBuffer(); } @@ -848,7 +848,7 @@ private: for (unsigned BufID = 0; BufID < Binaries.size(); ++BufID) { auto OutputName = InputFilenames[BufID] + ".thinlto.o"; std::error_code EC; - raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::F_None); + raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + OutputName + "'"); OS << Binaries[BufID]->getBuffer(); } @@ -921,7 +921,7 @@ int main(int argc, char **argv) { unsigned BaseArg = 0; LLVMContext Context; - Context.setDiagnosticHandler(llvm::make_unique<LLVMLTODiagnosticHandler>(), + Context.setDiagnosticHandler(std::make_unique<LLVMLTODiagnosticHandler>(), true); LTOCodeGenerator CodeGen(Context); @@ -1020,7 +1020,7 @@ int main(int argc, char **argv) { if (Parallelism != 1) PartFilename += "." + utostr(I); std::error_code EC; - OSs.emplace_back(PartFilename, EC, sys::fs::F_None); + OSs.emplace_back(PartFilename, EC, sys::fs::OF_None); if (EC) error("error opening the file '" + PartFilename + "': " + EC.message()); OSPtrs.push_back(&OSs.back().os()); diff --git a/tools/llvm-lto2/llvm-lto2.cpp b/tools/llvm-lto2/llvm-lto2.cpp index 0bd9289dc938..5e3b3dcb6c31 100644 --- a/tools/llvm-lto2/llvm-lto2.cpp +++ b/tools/llvm-lto2/llvm-lto2.cpp @@ -291,6 +291,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()}); + // 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}); + } if (I == CommandLineResolutions.end()) { llvm::errs() << argv[0] << ": missing symbol resolution for " << F << ',' << Sym.getName() << '\n'; @@ -325,9 +333,9 @@ static int run(int argc, char **argv) { std::string Path = OutputFilename + "." + utostr(Task); std::error_code EC; - auto S = llvm::make_unique<raw_fd_ostream>(Path, EC, sys::fs::F_None); + auto S = std::make_unique<raw_fd_ostream>(Path, EC, sys::fs::OF_None); check(EC, Path); - return llvm::make_unique<lto::NativeObjectStream>(std::move(S)); + return std::make_unique<lto::NativeObjectStream>(std::move(S)); }; auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) { diff --git a/tools/llvm-mc/Disassembler.cpp b/tools/llvm-mc/Disassembler.cpp index e2af2e7f2e32..1ddbddfa1846 100644 --- a/tools/llvm-mc/Disassembler.cpp +++ b/tools/llvm-mc/Disassembler.cpp @@ -17,6 +17,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" @@ -129,13 +130,10 @@ static bool ByteArrayFromString(ByteArrayTy &ByteArray, return false; } -int Disassembler::disassemble(const Target &T, - const std::string &Triple, - MCSubtargetInfo &STI, - MCStreamer &Streamer, - MemoryBuffer &Buffer, - SourceMgr &SM, - raw_ostream &Out) { +int Disassembler::disassemble(const Target &T, const std::string &Triple, + MCSubtargetInfo &STI, MCStreamer &Streamer, + MemoryBuffer &Buffer, SourceMgr &SM, + MCContext &Ctx, raw_ostream &Out) { std::unique_ptr<const MCRegisterInfo> MRI(T.createMCRegInfo(Triple)); if (!MRI) { @@ -149,9 +147,6 @@ int Disassembler::disassemble(const Target &T, return -1; } - // Set up the MCContext for creating symbols and MCExpr's. - MCContext Ctx(MAI.get(), MRI.get(), nullptr); - std::unique_ptr<const MCDisassembler> DisAsm( T.createMCDisassembler(STI, Ctx)); if (!DisAsm) { diff --git a/tools/llvm-mc/Disassembler.h b/tools/llvm-mc/Disassembler.h index 11b685233abc..dcd8c279c91a 100644 --- a/tools/llvm-mc/Disassembler.h +++ b/tools/llvm-mc/Disassembler.h @@ -22,17 +22,15 @@ class MemoryBuffer; class Target; class raw_ostream; class SourceMgr; +class MCContext; class MCSubtargetInfo; class MCStreamer; class Disassembler { public: - static int disassemble(const Target &T, - const std::string &Triple, - MCSubtargetInfo &STI, - MCStreamer &Streamer, - MemoryBuffer &Buffer, - SourceMgr &SM, + static int disassemble(const Target &T, const std::string &Triple, + MCSubtargetInfo &STI, MCStreamer &Streamer, + MemoryBuffer &Buffer, SourceMgr &SM, MCContext &Ctx, raw_ostream &Out); }; diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index ec189c297860..c23740a3094d 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/tools/llvm-mc/llvm-mc.cpp @@ -209,9 +209,10 @@ static const Target *GetTarget(const char *ProgName) { return TheTarget; } -static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path) { +static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path, + sys::fs::OpenFlags Flags) { std::error_code EC; - auto Out = llvm::make_unique<ToolOutputFile>(Path, EC, sys::fs::F_None); + auto Out = std::make_unique<ToolOutputFile>(Path, EC, Flags); if (EC) { WithColor::error() << EC.message() << '\n'; return nullptr; @@ -279,7 +280,7 @@ static int fillCommandLineSymbols(MCAsmParser &Parser) { static int AssembleInput(const char *ProgName, const Target *TheTarget, SourceMgr &SrcMgr, MCContext &Ctx, MCStreamer &Str, MCAsmInfo &MAI, MCSubtargetInfo &STI, - MCInstrInfo &MCII, MCTargetOptions &MCOptions) { + MCInstrInfo &MCII, MCTargetOptions const &MCOptions) { std::unique_ptr<MCAsmParser> Parser( createMCAsmParser(SrcMgr, Ctx, Str, MAI)); std::unique_ptr<MCTargetAsmParser> TAP( @@ -316,7 +317,7 @@ int main(int argc, char **argv) { cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n"); - MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + const MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); setDwarfDebugFlags(argc, argv); setDwarfDebugProducer(); @@ -368,7 +369,7 @@ int main(int argc, char **argv) { // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and // MCObjectFileInfo needs a MCContext reference in order to initialize itself. MCObjectFileInfo MOFI; - MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); + MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr, &MCOptions); MOFI.InitMCObjectFileInfo(TheTriple, PIC, Ctx, LargeCodeModel); if (SaveTempLabels) @@ -413,7 +414,9 @@ int main(int argc, char **argv) { FeaturesStr = Features.getString(); } - std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename); + sys::fs::OpenFlags Flags = (FileType == OFT_AssemblyFile) ? sys::fs::OF_Text + : sys::fs::OF_None; + std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename, Flags); if (!Out) return 1; @@ -423,7 +426,7 @@ int main(int argc, char **argv) { WithColor::error() << "dwo output only supported with object files\n"; return 1; } - DwoOut = GetOutputStream(SplitDwarfFile); + DwoOut = GetOutputStream(SplitDwarfFile, sys::fs::OF_None); if (!DwoOut) return 1; } @@ -459,7 +462,7 @@ int main(int argc, char **argv) { std::unique_ptr<MCAsmBackend> MAB( TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); - auto FOut = llvm::make_unique<formatted_raw_ostream>(*OS); + auto FOut = std::make_unique<formatted_raw_ostream>(*OS); Str.reset( TheTarget->createAsmStreamer(Ctx, std::move(FOut), /*asmverbose*/ true, /*useDwarfDirectory*/ true, IP, @@ -474,7 +477,7 @@ int main(int argc, char **argv) { Ctx.setUseNamesOnTempLabels(false); if (!Out->os().supportsSeeking()) { - BOS = make_unique<buffer_ostream>(Out->os()); + BOS = std::make_unique<buffer_ostream>(Out->os()); OS = BOS.get(); } @@ -506,7 +509,7 @@ int main(int argc, char **argv) { break; case AC_MDisassemble: assert(IP && "Expected assembly output"); - IP->setUseMarkup(1); + IP->setUseMarkup(true); disassemble = true; break; case AC_Disassemble: @@ -514,8 +517,8 @@ int main(int argc, char **argv) { break; } if (disassemble) - Res = Disassembler::disassemble(*TheTarget, TripleName, *STI, *Str, - *Buffer, SrcMgr, Out->os()); + Res = Disassembler::disassemble(*TheTarget, TripleName, *STI, *Str, *Buffer, + SrcMgr, Ctx, Out->os()); // Keep output if no errors. if (Res == 0) { diff --git a/tools/llvm-mca/CodeRegion.cpp b/tools/llvm-mca/CodeRegion.cpp index bf592f67245e..e05517c1ac95 100644 --- a/tools/llvm-mca/CodeRegion.cpp +++ b/tools/llvm-mca/CodeRegion.cpp @@ -18,7 +18,7 @@ namespace mca { CodeRegions::CodeRegions(llvm::SourceMgr &S) : SM(S), FoundErrors(false) { // Create a default region for the input code sequence. - Regions.emplace_back(make_unique<CodeRegion>("", SMLoc())); + Regions.emplace_back(std::make_unique<CodeRegion>("", SMLoc())); } bool CodeRegion::isLocInRange(SMLoc Loc) const { @@ -36,7 +36,7 @@ void CodeRegions::beginRegion(StringRef Description, SMLoc Loc) { if (Regions.size() == 1 && !Regions[0]->startLoc().isValid() && !Regions[0]->endLoc().isValid()) { ActiveRegions[Description] = 0; - Regions[0] = make_unique<CodeRegion>(Description, Loc); + Regions[0] = std::make_unique<CodeRegion>(Description, Loc); return; } } else { @@ -62,7 +62,7 @@ void CodeRegions::beginRegion(StringRef Description, SMLoc Loc) { } ActiveRegions[Description] = Regions.size(); - Regions.emplace_back(make_unique<CodeRegion>(Description, Loc)); + Regions.emplace_back(std::make_unique<CodeRegion>(Description, Loc)); return; } diff --git a/tools/llvm-mca/CodeRegionGenerator.cpp b/tools/llvm-mca/CodeRegionGenerator.cpp index c793169e64e0..8ddcd2f4abe2 100644 --- a/tools/llvm-mca/CodeRegionGenerator.cpp +++ b/tools/llvm-mca/CodeRegionGenerator.cpp @@ -118,6 +118,8 @@ Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions() { MCAsmLexer &Lexer = Parser->getLexer(); MCACommentConsumer CC(Regions); Lexer.setCommentConsumer(&CC); + // Enable support for MASM literal numbers (example: 05h, 101b). + Lexer.setLexMasmIntegers(true); std::unique_ptr<MCTargetAsmParser> TAP( TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts)); diff --git a/tools/llvm-mca/Views/BottleneckAnalysis.cpp b/tools/llvm-mca/Views/BottleneckAnalysis.cpp index 560c6c6e8a33..feff0cd6d524 100644 --- a/tools/llvm-mca/Views/BottleneckAnalysis.cpp +++ b/tools/llvm-mca/Views/BottleneckAnalysis.cpp @@ -165,10 +165,33 @@ void DependencyGraph::dumpDependencyEdge(raw_ostream &OS, "Unsupported dependency type!"); OS << " - RESOURCE MASK: " << DE.ResourceOrRegID; } - OS << " - CYCLES: " << DE.Cost << '\n'; + OS << " - COST: " << DE.Cost << '\n'; } #endif // NDEBUG +void DependencyGraph::pruneEdges(unsigned Iterations) { + for (DGNode &N : Nodes) { + unsigned NumPruned = 0; + const unsigned Size = N.OutgoingEdges.size(); + // Use a cut-off threshold to prune edges with a low frequency. + for (unsigned I = 0, E = Size; I < E; ++I) { + DependencyEdge &Edge = N.OutgoingEdges[I]; + if (Edge.Frequency == Iterations) + continue; + double Factor = (double)Edge.Frequency / Iterations; + if (0.10 < Factor) + continue; + Nodes[Edge.ToIID].NumPredecessors--; + std::swap(Edge, N.OutgoingEdges[E - 1]); + --E; + ++NumPruned; + } + + if (NumPruned) + N.OutgoingEdges.resize(Size - NumPruned); + } +} + void DependencyGraph::initializeRootSet( SmallVectorImpl<unsigned> &RootSet) const { for (unsigned I = 0, E = Nodes.size(); I < E; ++I) { @@ -179,7 +202,7 @@ void DependencyGraph::initializeRootSet( } void DependencyGraph::propagateThroughEdges( - SmallVectorImpl<unsigned> &RootSet) { + SmallVectorImpl<unsigned> &RootSet, unsigned Iterations) { SmallVector<unsigned, 8> ToVisit; // A critical sequence is computed as the longest path from a node of the @@ -189,6 +212,10 @@ void DependencyGraph::propagateThroughEdges( // Each node of the graph starts with an initial default cost of zero. The // cost of a node is a measure of criticality: the higher the cost, the bigger // is the performance impact. + // For register and memory dependencies, the cost is a function of the write + // latency as well as the actual delay (in cycles) caused to users. + // For processor resource dependencies, the cost is a function of the resource + // pressure. Resource interferences with low frequency values are ignored. // // This algorithm is very similar to a (reverse) Dijkstra. Every iteration of // the inner loop selects (i.e. visits) a node N from a set of `unvisited @@ -277,6 +304,10 @@ static void printInstruction(formatted_raw_ostream &FOS, } void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const { + // Early exit if no bottlenecks were found during the simulation. + if (!SeenStallCycles || !BPI.PressureIncreaseCycles) + return; + SmallVector<const DependencyEdge *, 16> Seq; DG.getCriticalSequence(Seq); if (Seq.empty()) @@ -432,7 +463,6 @@ void BottleneckAnalysis::addRegisterDep(unsigned From, unsigned To, bool IsLoopCarried = From >= To; unsigned SourceSize = Source.size(); if (IsLoopCarried) { - Cost *= Iterations / 2; DG.addRegisterDep(From, To + SourceSize, RegID, Cost); DG.addRegisterDep(From + SourceSize, To + (SourceSize * 2), RegID, Cost); return; @@ -445,7 +475,6 @@ void BottleneckAnalysis::addMemoryDep(unsigned From, unsigned To, bool IsLoopCarried = From >= To; unsigned SourceSize = Source.size(); if (IsLoopCarried) { - Cost *= Iterations / 2; DG.addMemoryDep(From, To + SourceSize, Cost); DG.addMemoryDep(From + SourceSize, To + (SourceSize * 2), Cost); return; @@ -458,7 +487,6 @@ void BottleneckAnalysis::addResourceDep(unsigned From, unsigned To, bool IsLoopCarried = From >= To; unsigned SourceSize = Source.size(); if (IsLoopCarried) { - Cost *= Iterations / 2; DG.addResourceDep(From, To + SourceSize, Mask, Cost); DG.addResourceDep(From + SourceSize, To + (SourceSize * 2), Mask, Cost); return; @@ -514,7 +542,7 @@ void BottleneckAnalysis::onEvent(const HWInstructionEvent &Event) { // Check if this is the last simulated instruction. if (IID == ((Iterations * Source.size()) - 1)) - DG.finalizeGraph(); + DG.finalizeGraph(Iterations); } void BottleneckAnalysis::onEvent(const HWPressureEvent &Event) { diff --git a/tools/llvm-mca/Views/BottleneckAnalysis.h b/tools/llvm-mca/Views/BottleneckAnalysis.h index 7564b1a48206..9e3bd5978f09 100644 --- a/tools/llvm-mca/Views/BottleneckAnalysis.h +++ b/tools/llvm-mca/Views/BottleneckAnalysis.h @@ -236,8 +236,9 @@ class DependencyGraph { void addDependency(unsigned From, unsigned To, DependencyEdge::Dependency &&DE); + void pruneEdges(unsigned Iterations); void initializeRootSet(SmallVectorImpl<unsigned> &RootSet) const; - void propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet); + void propagateThroughEdges(SmallVectorImpl<unsigned> &RootSet, unsigned Iterations); #ifndef NDEBUG void dumpDependencyEdge(raw_ostream &OS, const DependencyEdge &DE, @@ -263,10 +264,11 @@ public: // Called by the bottleneck analysis at the end of simulation to propagate // costs through the edges of the graph, and compute a critical path. - void finalizeGraph() { + void finalizeGraph(unsigned Iterations) { SmallVector<unsigned, 16> RootSet; + pruneEdges(Iterations); initializeRootSet(RootSet); - propagateThroughEdges(RootSet); + propagateThroughEdges(RootSet, Iterations); } // Returns a sequence of edges representing the critical sequence based on the diff --git a/tools/llvm-mca/Views/InstructionInfoView.cpp b/tools/llvm-mca/Views/InstructionInfoView.cpp index 1fbffa3e5b69..a6f9153b4945 100644 --- a/tools/llvm-mca/Views/InstructionInfoView.cpp +++ b/tools/llvm-mca/Views/InstructionInfoView.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "Views/InstructionInfoView.h" +#include "llvm/Support/FormattedStream.h" namespace llvm { namespace mca { @@ -26,10 +27,17 @@ void InstructionInfoView::printView(raw_ostream &OS) const { TempStream << "\n\nInstruction Info:\n"; TempStream << "[1]: #uOps\n[2]: Latency\n[3]: RThroughput\n" - << "[4]: MayLoad\n[5]: MayStore\n[6]: HasSideEffects (U)\n\n"; + << "[4]: MayLoad\n[5]: MayStore\n[6]: HasSideEffects (U)\n"; + if (PrintEncodings) { + TempStream << "[7]: Encoding Size\n"; + TempStream << "\n[1] [2] [3] [4] [5] [6] [7] " + << "Encodings: Instructions:\n"; + } else { + TempStream << "\n[1] [2] [3] [4] [5] [6] Instructions:\n"; + } - TempStream << "[1] [2] [3] [4] [5] [6] Instructions:\n"; - for (const MCInst &Inst : Source) { + for (unsigned I = 0, E = Source.size(); I < E; ++I) { + const MCInst &Inst = Source[I]; const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode()); // Obtain the scheduling class information from the instruction. @@ -72,7 +80,20 @@ void InstructionInfoView::printView(raw_ostream &OS) const { } TempStream << (MCDesc.mayLoad() ? " * " : " "); TempStream << (MCDesc.mayStore() ? " * " : " "); - TempStream << (MCDesc.hasUnmodeledSideEffects() ? " U " : " "); + TempStream << (MCDesc.hasUnmodeledSideEffects() ? " U " : " "); + + if (PrintEncodings) { + StringRef Encoding(CE.getEncoding(I)); + unsigned EncodingSize = Encoding.size(); + TempStream << " " << EncodingSize + << (EncodingSize < 10 ? " " : " "); + TempStream.flush(); + formatted_raw_ostream FOS(TempStream); + for (unsigned i = 0, e = Encoding.size(); i != e; ++i) + FOS << format("%02x ", (uint8_t)Encoding[i]); + FOS.PadToColumn(30); + FOS.flush(); + } MCIP.printInst(&Inst, InstrStream, "", STI); InstrStream.flush(); @@ -80,7 +101,7 @@ void InstructionInfoView::printView(raw_ostream &OS) const { // Consume any tabs or spaces at the beginning of the string. StringRef Str(Instruction); Str = Str.ltrim(); - TempStream << " " << Str << '\n'; + TempStream << Str << '\n'; Instruction = ""; } diff --git a/tools/llvm-mca/Views/InstructionInfoView.h b/tools/llvm-mca/Views/InstructionInfoView.h index 640d87383436..0e948304119f 100644 --- a/tools/llvm-mca/Views/InstructionInfoView.h +++ b/tools/llvm-mca/Views/InstructionInfoView.h @@ -40,6 +40,7 @@ #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MCA/CodeEmitter.h" #include "llvm/Support/raw_ostream.h" #define DEBUG_TYPE "llvm-mca" @@ -51,14 +52,18 @@ namespace mca { class InstructionInfoView : public View { const llvm::MCSubtargetInfo &STI; const llvm::MCInstrInfo &MCII; + CodeEmitter &CE; + bool PrintEncodings; llvm::ArrayRef<llvm::MCInst> Source; llvm::MCInstPrinter &MCIP; public: - InstructionInfoView(const llvm::MCSubtargetInfo &sti, - const llvm::MCInstrInfo &mcii, - llvm::ArrayRef<llvm::MCInst> S, llvm::MCInstPrinter &IP) - : STI(sti), MCII(mcii), Source(S), MCIP(IP) {} + InstructionInfoView(const llvm::MCSubtargetInfo &ST, + const llvm::MCInstrInfo &II, CodeEmitter &C, + bool ShouldPrintEncodings, llvm::ArrayRef<llvm::MCInst> S, + llvm::MCInstPrinter &IP) + : STI(ST), MCII(II), CE(C), PrintEncodings(ShouldPrintEncodings), + Source(S), MCIP(IP) {} void printView(llvm::raw_ostream &OS) const override; }; diff --git a/tools/llvm-mca/Views/TimelineView.cpp b/tools/llvm-mca/Views/TimelineView.cpp index fe3f16ba344c..1e7caa297ac6 100644 --- a/tools/llvm-mca/Views/TimelineView.cpp +++ b/tools/llvm-mca/Views/TimelineView.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "Views/TimelineView.h" +#include <numeric> namespace llvm { namespace mca { @@ -132,25 +133,38 @@ void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS, const WaitTimeEntry &Entry, unsigned SourceIndex, unsigned Executions) const { - OS << SourceIndex << '.'; + bool PrintingTotals = SourceIndex == Source.size(); + unsigned CumulativeExecutions = PrintingTotals ? Timeline.size() : Executions; + + if (!PrintingTotals) + OS << SourceIndex << '.'; + OS.PadToColumn(7); double AverageTime1, AverageTime2, AverageTime3; - AverageTime1 = (double)Entry.CyclesSpentInSchedulerQueue / Executions; - AverageTime2 = (double)Entry.CyclesSpentInSQWhileReady / Executions; - AverageTime3 = (double)Entry.CyclesSpentAfterWBAndBeforeRetire / Executions; + AverageTime1 = + (double)Entry.CyclesSpentInSchedulerQueue / CumulativeExecutions; + AverageTime2 = (double)Entry.CyclesSpentInSQWhileReady / CumulativeExecutions; + AverageTime3 = + (double)Entry.CyclesSpentAfterWBAndBeforeRetire / CumulativeExecutions; OS << Executions; OS.PadToColumn(13); - int BufferSize = UsedBuffer[SourceIndex].second; - tryChangeColor(OS, Entry.CyclesSpentInSchedulerQueue, Executions, BufferSize); + + int BufferSize = PrintingTotals ? 0 : UsedBuffer[SourceIndex].second; + if (!PrintingTotals) + tryChangeColor(OS, Entry.CyclesSpentInSchedulerQueue, CumulativeExecutions, + BufferSize); OS << format("%.1f", floor((AverageTime1 * 10) + 0.5) / 10); OS.PadToColumn(20); - tryChangeColor(OS, Entry.CyclesSpentInSQWhileReady, Executions, BufferSize); + if (!PrintingTotals) + tryChangeColor(OS, Entry.CyclesSpentInSQWhileReady, CumulativeExecutions, + BufferSize); OS << format("%.1f", floor((AverageTime2 * 10) + 0.5) / 10); OS.PadToColumn(27); - tryChangeColor(OS, Entry.CyclesSpentAfterWBAndBeforeRetire, Executions, - STI.getSchedModel().MicroOpBufferSize); + if (!PrintingTotals) + tryChangeColor(OS, Entry.CyclesSpentAfterWBAndBeforeRetire, + CumulativeExecutions, STI.getSchedModel().MicroOpBufferSize); OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10); if (OS.has_colors()) @@ -190,6 +204,24 @@ void TimelineView::printAverageWaitTimes(raw_ostream &OS) const { ++IID; } + + // If the timeline contains more than one instruction, + // let's also print global averages. + if (Source.size() != 1) { + WaitTimeEntry TotalWaitTime = std::accumulate( + WaitTime.begin(), WaitTime.end(), WaitTimeEntry{0, 0, 0}, + [](const WaitTimeEntry &A, const WaitTimeEntry &B) { + return WaitTimeEntry{ + A.CyclesSpentInSchedulerQueue + B.CyclesSpentInSchedulerQueue, + A.CyclesSpentInSQWhileReady + B.CyclesSpentInSQWhileReady, + A.CyclesSpentAfterWBAndBeforeRetire + + B.CyclesSpentAfterWBAndBeforeRetire}; + }); + printWaitTimeEntry(FOS, TotalWaitTime, IID, Executions); + FOS << " " + << "<total>" << '\n'; + InstrStream.flush(); + } } void TimelineView::printTimelineViewEntry(formatted_raw_ostream &OS, diff --git a/tools/llvm-mca/Views/TimelineView.h b/tools/llvm-mca/Views/TimelineView.h index b63b234293cd..9bec3b87db45 100644 --- a/tools/llvm-mca/Views/TimelineView.h +++ b/tools/llvm-mca/Views/TimelineView.h @@ -84,6 +84,7 @@ /// 3. 2 1.5 0.5 1.0 vaddss %xmm1, %xmm0, %xmm3 /// 4. 2 3.5 0.0 0.0 vaddss %xmm3, %xmm2, %xmm4 /// 5. 2 6.5 0.0 0.0 vaddss %xmm4, %xmm5, %xmm6 +/// 2 2.4 0.6 1.6 <total> /// /// By comparing column [2] with column [1], we get an idea about how many /// cycles were spent in the scheduler's queue due to data dependencies. diff --git a/tools/llvm-mca/llvm-mca.cpp b/tools/llvm-mca/llvm-mca.cpp index b3590b5910ec..99c45eebdd88 100644 --- a/tools/llvm-mca/llvm-mca.cpp +++ b/tools/llvm-mca/llvm-mca.cpp @@ -32,11 +32,17 @@ #include "Views/SchedulerStatistics.h" #include "Views/SummaryView.h" #include "Views/TimelineView.h" +#include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.inc" +#include "llvm/MCA/CodeEmitter.h" #include "llvm/MCA/Context.h" +#include "llvm/MCA/InstrBuilder.h" #include "llvm/MCA/Pipeline.h" #include "llvm/MCA/Stages/EntryStage.h" #include "llvm/MCA/Stages/InstructionTables.h" @@ -83,11 +89,20 @@ static cl::opt<std::string> cl::desc("Target a specific cpu type (-mcpu=help for details)"), cl::value_desc("cpu-name"), cl::cat(ToolOptions), cl::init("native")); +static cl::opt<std::string> + MATTR("mattr", + cl::desc("Additional target features."), + cl::cat(ToolOptions)); + static cl::opt<int> OutputAsmVariant("output-asm-variant", cl::desc("Syntax variant to use for output printing"), cl::cat(ToolOptions), cl::init(-1)); +static cl::opt<bool> + PrintImmHex("print-imm-hex", cl::cat(ToolOptions), cl::init(false), + cl::desc("Prefer hex format when printing immediate values")); + static cl::opt<unsigned> Iterations("iterations", cl::desc("Number of iterations to run"), cl::cat(ToolOptions), cl::init(0)); @@ -193,6 +208,11 @@ static cl::opt<bool> EnableBottleneckAnalysis( cl::desc("Enable bottleneck analysis (disabled by default)"), cl::cat(ViewOptions), cl::init(false)); +static cl::opt<bool> ShowEncoding( + "show-encoding", + cl::desc("Print encoding information in the instruction info view"), + cl::cat(ViewOptions), cl::init(false)); + namespace { const Target *getTarget(const char *ProgName) { @@ -218,7 +238,7 @@ ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() { OutputFilename = "-"; std::error_code EC; auto Out = - llvm::make_unique<ToolOutputFile>(OutputFilename, EC, sys::fs::F_None); + std::make_unique<ToolOutputFile>(OutputFilename, EC, sys::fs::OF_Text); if (!EC) return std::move(Out); return EC; @@ -303,33 +323,11 @@ int main(int argc, char **argv) { // Apply overrides to llvm-mca specific options. processViewOptions(); - SourceMgr SrcMgr; - - // Tell SrcMgr about this buffer, which is what the parser will pick up. - SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); - - std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); - assert(MRI && "Unable to create target register info!"); - - std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName)); - assert(MAI && "Unable to create target asm info!"); - - MCObjectFileInfo MOFI; - MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); - MOFI.InitMCObjectFileInfo(TheTriple, /* PIC= */ false, Ctx); - - std::unique_ptr<buffer_ostream> BOS; - - std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); - - std::unique_ptr<MCInstrAnalysis> MCIA( - TheTarget->createMCInstrAnalysis(MCII.get())); - if (!MCPU.compare("native")) MCPU = llvm::sys::getHostCPUName(); std::unique_ptr<MCSubtargetInfo> STI( - TheTarget->createMCSubtargetInfo(TripleName, MCPU, /* FeaturesStr */ "")); + TheTarget->createMCSubtargetInfo(TripleName, MCPU, MATTR)); if (!STI->isCPUStringValid(MCPU)) return 1; @@ -352,6 +350,29 @@ int main(int argc, char **argv) { return 1; } + std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); + assert(MRI && "Unable to create target register info!"); + + std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName)); + assert(MAI && "Unable to create target asm info!"); + + MCObjectFileInfo MOFI; + SourceMgr SrcMgr; + + // Tell SrcMgr about this buffer, which is what the parser will pick up. + SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); + + MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr); + + MOFI.InitMCObjectFileInfo(TheTriple, /* PIC= */ false, Ctx); + + std::unique_ptr<buffer_ostream> BOS; + + std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); + + std::unique_ptr<MCInstrAnalysis> MCIA( + TheTarget->createMCInstrAnalysis(MCII.get())); + // Parse the input and create CodeRegions that llvm-mca can analyze. mca::AsmCodeRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, *MCII); Expected<const mca::CodeRegions &> RegionsOrErr = CRG.parseCodeRegions(); @@ -396,6 +417,9 @@ int main(int argc, char **argv) { return 1; } + // Set the display preference for hex vs. decimal immediates. + IP->setPrintImmHex(PrintImmHex); + std::unique_ptr<ToolOutputFile> TOF = std::move(*OF); const MCSchedModel &SM = STI->getSchedModel(); @@ -413,6 +437,12 @@ int main(int argc, char **argv) { // Number each region in the sequence. unsigned RegionIdx = 0; + std::unique_ptr<MCCodeEmitter> MCE( + TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); + + std::unique_ptr<MCAsmBackend> MAB(TheTarget->createMCAsmBackend( + *STI, *MRI, InitMCTargetOptionsFromFlags())); + for (const std::unique_ptr<mca::CodeRegion> &Region : Regions) { // Skip empty code regions. if (Region->empty()) @@ -430,6 +460,7 @@ int main(int argc, char **argv) { // Lower the MCInst sequence into an mca::Instruction sequence. ArrayRef<MCInst> Insts = Region->getInstructions(); + mca::CodeEmitter CE(*STI, *MAB, *MCE, Insts); std::vector<std::unique_ptr<mca::Instruction>> LoweredSequence; for (const MCInst &MCI : Insts) { Expected<std::unique_ptr<mca::Instruction>> Inst = @@ -459,18 +490,18 @@ int main(int argc, char **argv) { if (PrintInstructionTables) { // Create a pipeline, stages, and a printer. - auto P = llvm::make_unique<mca::Pipeline>(); - P->appendStage(llvm::make_unique<mca::EntryStage>(S)); - P->appendStage(llvm::make_unique<mca::InstructionTables>(SM)); + auto P = std::make_unique<mca::Pipeline>(); + P->appendStage(std::make_unique<mca::EntryStage>(S)); + P->appendStage(std::make_unique<mca::InstructionTables>(SM)); mca::PipelinePrinter Printer(*P); // Create the views for this pipeline, execute, and emit a report. if (PrintInstructionInfoView) { - Printer.addView(llvm::make_unique<mca::InstructionInfoView>( - *STI, *MCII, Insts, *IP)); + Printer.addView(std::make_unique<mca::InstructionInfoView>( + *STI, *MCII, CE, ShowEncoding, Insts, *IP)); } Printer.addView( - llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts)); + std::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts)); if (!runPipeline(*P)) return 1; @@ -480,42 +511,42 @@ int main(int argc, char **argv) { } // Create a basic pipeline simulating an out-of-order backend. - auto P = MCA.createDefaultPipeline(PO, IB, S); + auto P = MCA.createDefaultPipeline(PO, S); mca::PipelinePrinter Printer(*P); if (PrintSummaryView) Printer.addView( - llvm::make_unique<mca::SummaryView>(SM, Insts, DispatchWidth)); + std::make_unique<mca::SummaryView>(SM, Insts, DispatchWidth)); if (EnableBottleneckAnalysis) { - Printer.addView(llvm::make_unique<mca::BottleneckAnalysis>( + Printer.addView(std::make_unique<mca::BottleneckAnalysis>( *STI, *IP, Insts, S.getNumIterations())); } if (PrintInstructionInfoView) - Printer.addView( - llvm::make_unique<mca::InstructionInfoView>(*STI, *MCII, Insts, *IP)); + Printer.addView(std::make_unique<mca::InstructionInfoView>( + *STI, *MCII, CE, ShowEncoding, Insts, *IP)); if (PrintDispatchStats) - Printer.addView(llvm::make_unique<mca::DispatchStatistics>()); + Printer.addView(std::make_unique<mca::DispatchStatistics>()); if (PrintSchedulerStats) - Printer.addView(llvm::make_unique<mca::SchedulerStatistics>(*STI)); + Printer.addView(std::make_unique<mca::SchedulerStatistics>(*STI)); if (PrintRetireStats) - Printer.addView(llvm::make_unique<mca::RetireControlUnitStatistics>(SM)); + Printer.addView(std::make_unique<mca::RetireControlUnitStatistics>(SM)); if (PrintRegisterFileStats) - Printer.addView(llvm::make_unique<mca::RegisterFileStatistics>(*STI)); + Printer.addView(std::make_unique<mca::RegisterFileStatistics>(*STI)); if (PrintResourcePressureView) Printer.addView( - llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts)); + std::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts)); if (PrintTimelineView) { unsigned TimelineIterations = TimelineMaxIterations ? TimelineMaxIterations : 10; - Printer.addView(llvm::make_unique<mca::TimelineView>( + Printer.addView(std::make_unique<mca::TimelineView>( *STI, *IP, Insts, std::min(TimelineIterations, S.getNumIterations()), TimelineMaxCycles)); } diff --git a/tools/llvm-modextract/llvm-modextract.cpp b/tools/llvm-modextract/llvm-modextract.cpp index 3adefc5f0d3e..7c4099625842 100644 --- a/tools/llvm-modextract/llvm-modextract.cpp +++ b/tools/llvm-modextract/llvm-modextract.cpp @@ -54,7 +54,7 @@ int main(int argc, char **argv) { std::error_code EC; std::unique_ptr<ToolOutputFile> Out( - new ToolOutputFile(OutputFilename, EC, sys::fs::F_None)); + new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None)); ExitOnErr(errorCodeToError(EC)); if (BinaryExtract) { diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index aa62e6f0209b..ee55722dc139 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/tools/llvm-nm/llvm-nm.cpp @@ -711,17 +711,21 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, const std::string &ArchiveName, const std::string &ArchitectureName) { if (!NoSort) { - std::function<bool(const NMSymbol &, const NMSymbol &)> Cmp; + using Comparator = bool (*)(const NMSymbol &, const NMSymbol &); + Comparator Cmp; if (NumericSort) - Cmp = compareSymbolAddress; + Cmp = &compareSymbolAddress; else if (SizeSort) - Cmp = compareSymbolSize; + Cmp = &compareSymbolSize; else - Cmp = compareSymbolName; + Cmp = &compareSymbolName; if (ReverseSort) - Cmp = [=](const NMSymbol &A, const NMSymbol &B) { return Cmp(B, A); }; - llvm::sort(SymbolList, Cmp); + llvm::sort(SymbolList, [=](const NMSymbol &A, const NMSymbol &B) -> bool { + return Cmp(B, A); + }); + else + llvm::sort(SymbolList, Cmp); } if (!PrintFileName) { @@ -913,10 +917,12 @@ static char getSymbolNMTypeChar(ELFObjectFileBase &Obj, if (Flags & ELF::SHF_ALLOC) return Flags & ELF::SHF_WRITE ? 'd' : 'r'; - StringRef SecName; - if (SecI->getName(SecName)) + auto NameOrErr = SecI->getName(); + if (!NameOrErr) { + consumeError(NameOrErr.takeError()); return '?'; - if (SecName.startswith(".debug")) + } + if ((*NameOrErr).startswith(".debug")) return 'N'; if (!(Flags & ELF::SHF_WRITE)) return 'n'; @@ -1076,7 +1082,7 @@ static StringRef getNMTypeName(SymbolicFile &Obj, basic_symbol_iterator I) { static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I, StringRef &SecName) { uint32_t Symflags = I->getFlags(); - if (isa<ELFObjectFileBase>(&Obj)) { + if (ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(&Obj)) { if (Symflags & object::SymbolRef::SF_Absolute) SecName = "*ABS*"; else if (Symflags & object::SymbolRef::SF_Common) @@ -1090,8 +1096,16 @@ static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I, consumeError(SecIOrErr.takeError()); return '?'; } - elf_section_iterator secT = *SecIOrErr; - secT->getName(SecName); + + if (*SecIOrErr == ELFObj->section_end()) + return '?'; + + Expected<StringRef> NameOrErr = (*SecIOrErr)->getName(); + if (!NameOrErr) { + consumeError(NameOrErr.takeError()); + return '?'; + } + SecName = *NameOrErr; } } @@ -1347,7 +1361,12 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, StringRef SectionName = StringRef(); for (const SectionRef &Section : MachO->sections()) { S.NSect++; - Section.getName(SectionName); + + if (Expected<StringRef> NameOrErr = Section.getName()) + SectionName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + SegmentName = MachO->getSectionFinalSegmentName( Section.getRawDataRefImpl()); if (S.Address >= Section.getAddress() && @@ -1667,7 +1686,11 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, StringRef SegmentName = StringRef(); StringRef SectionName = StringRef(); for (const SectionRef &Section : MachO->sections()) { - Section.getName(SectionName); + if (Expected<StringRef> NameOrErr = Section.getName()) + SectionName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + SegmentName = MachO->getSectionFinalSegmentName( Section.getRawDataRefImpl()); F.NSect++; diff --git a/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/tools/llvm-objcopy/COFF/COFFObjcopy.cpp index 4ae46851a66f..2a8d816e6f3c 100644 --- a/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -16,8 +16,8 @@ #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" +#include "llvm/Support/CRC.h" #include "llvm/Support/Errc.h" -#include "llvm/Support/JamCRC.h" #include "llvm/Support/Path.h" #include <cassert> @@ -40,22 +40,13 @@ static uint64_t getNextRVA(const Object &Obj) { Obj.IsPE ? Obj.PeHeader.SectionAlignment : 1); } -static uint32_t getCRC32(StringRef Data) { - JamCRC CRC; - CRC.update(ArrayRef<char>(Data.data(), Data.size())); - // The CRC32 value needs to be complemented because the JamCRC dosn't - // finalize the CRC32 value. It also dosn't negate the initial CRC32 value - // but it starts by default at 0xFFFFFFFF which is the complement of zero. - return ~CRC.getCRC(); -} - static std::vector<uint8_t> createGnuDebugLinkSectionContents(StringRef File) { ErrorOr<std::unique_ptr<MemoryBuffer>> LinkTargetOrErr = MemoryBuffer::getFile(File); if (!LinkTargetOrErr) error("'" + File + "': " + LinkTargetOrErr.getError().message()); auto LinkTarget = std::move(*LinkTargetOrErr); - uint32_t CRC32 = getCRC32(LinkTarget->getBuffer()); + uint32_t CRC32 = llvm::crc32(arrayRefFromStringRef(LinkTarget->getBuffer())); StringRef FileName = sys::path::filename(File); size_t CRCPos = alignTo(FileName.size() + 1, 4); @@ -65,26 +56,37 @@ static std::vector<uint8_t> createGnuDebugLinkSectionContents(StringRef File) { return Data; } -static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) { - uint32_t StartRVA = getNextRVA(Obj); +// Adds named section with given contents to the object. +static void addSection(Object &Obj, StringRef Name, ArrayRef<uint8_t> Contents, + uint32_t Characteristics) { + bool NeedVA = Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE); - std::vector<Section> Sections; Section Sec; - Sec.setOwnedContents(createGnuDebugLinkSectionContents(DebugLinkFile)); - Sec.Name = ".gnu_debuglink"; - Sec.Header.VirtualSize = Sec.getContents().size(); - Sec.Header.VirtualAddress = StartRVA; - Sec.Header.SizeOfRawData = alignTo(Sec.Header.VirtualSize, - Obj.IsPE ? Obj.PeHeader.FileAlignment : 1); + Sec.setOwnedContents(Contents); + Sec.Name = Name; + Sec.Header.VirtualSize = NeedVA ? Sec.getContents().size() : 0u; + Sec.Header.VirtualAddress = NeedVA ? getNextRVA(Obj) : 0u; + Sec.Header.SizeOfRawData = + NeedVA ? alignTo(Sec.Header.VirtualSize, + Obj.IsPE ? Obj.PeHeader.FileAlignment : 1) + : Sec.getContents().size(); // Sec.Header.PointerToRawData is filled in by the writer. Sec.Header.PointerToRelocations = 0; Sec.Header.PointerToLinenumbers = 0; // Sec.Header.NumberOfRelocations is filled in by the writer. Sec.Header.NumberOfLinenumbers = 0; - Sec.Header.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | - IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE; - Sections.push_back(Sec); - Obj.addSections(Sections); + Sec.Header.Characteristics = Characteristics; + + Obj.addSections(Sec); +} + +static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) { + std::vector<uint8_t> Contents = + createGnuDebugLinkSectionContents(DebugLinkFile); + addSection(Obj, ".gnu_debuglink", Contents, + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_DISCARDABLE); } static Error handleArgs(const CopyConfig &Config, Object &Obj) { @@ -92,8 +94,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { Obj.removeSections([&Config](const Section &Sec) { // Contrary to --only-keep-debug, --only-section fully removes sections that // aren't mentioned. - if (!Config.OnlySection.empty() && - !is_contained(Config.OnlySection, Sec.Name)) + if (!Config.OnlySection.empty() && !Config.OnlySection.matches(Sec.Name)) return true; if (Config.StripDebug || Config.StripAll || Config.StripAllGNU || @@ -103,7 +104,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { return true; } - if (is_contained(Config.ToRemove, Sec.Name)) + if (Config.ToRemove.matches(Sec.Name)) return true; return false; @@ -137,7 +138,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { if (Config.StripAll || Config.StripAllGNU) return true; - if (is_contained(Config.SymbolsToRemove, Sym.Name)) { + if (Config.SymbolsToRemove.matches(Sym.Name)) { // Explicitly removing a referenced symbol is an error. if (Sym.Referenced) reportError(Config.OutputFilename, @@ -156,7 +157,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC || Sym.Sym.SectionNumber == 0) if (Config.StripUnneeded || - is_contained(Config.UnneededSymbolsToRemove, Sym.Name)) + Config.UnneededSymbolsToRemove.matches(Sym.Name)) return true; // GNU objcopy keeps referenced local symbols and external symbols @@ -171,21 +172,38 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { return false; }); + for (const auto &Flag : Config.AddSection) { + StringRef SecName, FileName; + std::tie(SecName, FileName) = Flag.split("="); + + auto BufOrErr = MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return createFileError(FileName, errorCodeToError(BufOrErr.getError())); + auto Buf = std::move(*BufOrErr); + + addSection( + Obj, SecName, + makeArrayRef(reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), + Buf->getBufferSize()), + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES); + } + if (!Config.AddGnuDebugLink.empty()) addGnuDebugLink(Obj, Config.AddGnuDebugLink); if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || Config.BuildIdLinkInput || Config.BuildIdLinkOutput || !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || - !Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() || - !Config.DumpSection.empty() || !Config.KeepSection.empty() || + !Config.AllocSectionsPrefix.empty() || !Config.DumpSection.empty() || + !Config.KeepSection.empty() || Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || - !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty() || - Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || - Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc || - Config.StripSections || Config.Weaken || Config.DecompressDebugSections || + !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || + !Config.SymbolsToRename.empty() || Config.ExtractDWO || + Config.KeepFileSymbols || Config.LocalizeHidden || Config.PreserveDates || + Config.StripDWO || Config.StripNonAlloc || Config.StripSections || + Config.Weaken || Config.DecompressDebugSections || Config.DiscardMode == DiscardType::Locals || !Config.SymbolsToAdd.empty() || Config.EntryExpr) { return createStringError(llvm::errc::invalid_argument, diff --git a/tools/llvm-objcopy/COFF/Reader.cpp b/tools/llvm-objcopy/COFF/Reader.cpp index 1f0ec9fa9691..2fcec0057c03 100644 --- a/tools/llvm-objcopy/COFF/Reader.cpp +++ b/tools/llvm-objcopy/COFF/Reader.cpp @@ -36,14 +36,9 @@ Error COFFReader::readExecutableHeaders(Object &Obj) const { DH->AddressOfNewExeHeader - sizeof(*DH)); if (COFFObj.is64()) { - const pe32plus_header *PE32Plus = nullptr; - if (auto EC = COFFObj.getPE32PlusHeader(PE32Plus)) - return errorCodeToError(EC); - Obj.PeHeader = *PE32Plus; + Obj.PeHeader = *COFFObj.getPE32PlusHeader(); } else { - const pe32_header *PE32 = nullptr; - if (auto EC = COFFObj.getPE32Header(PE32)) - return errorCodeToError(EC); + const pe32_header *PE32 = COFFObj.getPE32Header(); copyPeHeader(Obj.PeHeader, *PE32); // The pe32plus_header (stored in Object) lacks the BaseOfData field. Obj.BaseOfData = PE32->BaseOfData; @@ -196,16 +191,13 @@ Error COFFReader::setSymbolTargets(Object &Obj) const { } Expected<std::unique_ptr<Object>> COFFReader::create() const { - auto Obj = llvm::make_unique<Object>(); + auto Obj = std::make_unique<Object>(); - const coff_file_header *CFH = nullptr; - const coff_bigobj_file_header *CBFH = nullptr; - COFFObj.getCOFFHeader(CFH); - COFFObj.getCOFFBigObjHeader(CBFH); bool IsBigObj = false; - if (CFH) { + if (const coff_file_header *CFH = COFFObj.getCOFFHeader()) { Obj->CoffFileHeader = *CFH; } else { + const coff_bigobj_file_header *CBFH = COFFObj.getCOFFBigObjHeader(); if (!CBFH) return createStringError(object_error::parse_failed, "no COFF file header returned"); diff --git a/tools/llvm-objcopy/COFF/Writer.cpp b/tools/llvm-objcopy/COFF/Writer.cpp index f3bb1ce331f2..6db37435fd96 100644 --- a/tools/llvm-objcopy/COFF/Writer.cpp +++ b/tools/llvm-objcopy/COFF/Writer.cpp @@ -120,12 +120,12 @@ size_t COFFWriter::finalizeStringTable() { StrTabBuilder.finalize(); for (auto &S : Obj.getMutableSections()) { + memset(S.Header.Name, 0, sizeof(S.Header.Name)); if (S.Name.size() > COFF::NameSize) { - memset(S.Header.Name, 0, sizeof(S.Header.Name)); snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d", (int)StrTabBuilder.getOffset(S.Name)); } else { - strncpy(S.Header.Name, S.Name.data(), COFF::NameSize); + memcpy(S.Header.Name, S.Name.data(), S.Name.size()); } } for (auto &S : Obj.getMutableSymbols()) { diff --git a/tools/llvm-objcopy/CommonOpts.td b/tools/llvm-objcopy/CommonOpts.td new file mode 100644 index 000000000000..e8c092b44431 --- /dev/null +++ b/tools/llvm-objcopy/CommonOpts.td @@ -0,0 +1,123 @@ +include "llvm/Option/OptParser.td" + +multiclass Eq<string name, string help> { + def NAME : Separate<["--"], name>; + def NAME #_eq : Joined<["--"], name #"=">, + Alias<!cast<Separate>(NAME)>, + HelpText<help>; +} + +def help : Flag<["--"], "help">; +def h : Flag<["-"], "h">, Alias<help>; + +def allow_broken_links + : Flag<["--"], "allow-broken-links">, + HelpText<"Allow the tool to remove sections even if it would leave " + "invalid section references. The appropriate sh_link fields " + "will be set to zero.">; + +def enable_deterministic_archives + : Flag<["--"], "enable-deterministic-archives">, + HelpText<"Enable deterministic mode when operating on archives (use " + "zero for UIDs, GIDs, and timestamps).">; +def D : Flag<["-"], "D">, + Alias<enable_deterministic_archives>, + HelpText<"Alias for --enable-deterministic-archives">; + +def disable_deterministic_archives + : Flag<["--"], "disable-deterministic-archives">, + HelpText<"Disable deterministic mode when operating on archives (use " + "real values for UIDs, GIDs, and timestamps).">; +def U : Flag<["-"], "U">, + Alias<disable_deterministic_archives>, + HelpText<"Alias for --disable-deterministic-archives">; + +def preserve_dates : Flag<["--"], "preserve-dates">, + HelpText<"Preserve access and modification timestamps">; +def p : Flag<["-"], "p">, + Alias<preserve_dates>, + HelpText<"Alias for --preserve-dates">; + +def strip_all : Flag<["--"], "strip-all">, + HelpText<"Remove non-allocated sections outside segments. " + ".gnu.warning* sections are not removed">; + +def strip_all_gnu + : Flag<["--"], "strip-all-gnu">, + HelpText<"Compatible with GNU's --strip-all">; + +def strip_debug : Flag<["--"], "strip-debug">, + HelpText<"Remove all debug sections">; +def g : Flag<["-"], "g">, + Alias<strip_debug>, + HelpText<"Alias for --strip-debug">; + +def strip_unneeded : Flag<["--"], "strip-unneeded">, + HelpText<"Remove all symbols not needed by relocations">; + +defm remove_section : Eq<"remove-section", "Remove <section>">, + MetaVarName<"section">; +def R : JoinedOrSeparate<["-"], "R">, + Alias<remove_section>, + HelpText<"Alias for --remove-section">; + +def strip_sections + : Flag<["--"], "strip-sections">, + HelpText<"Remove all section headers and all sections not in segments">; + +defm strip_symbol : Eq<"strip-symbol", "Strip <symbol>">, + MetaVarName<"symbol">; +def N : JoinedOrSeparate<["-"], "N">, + Alias<strip_symbol>, + HelpText<"Alias for --strip-symbol">; + +defm keep_section : Eq<"keep-section", "Keep <section>">, + MetaVarName<"section">; + +defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, + MetaVarName<"symbol">; +def K : JoinedOrSeparate<["-"], "K">, + Alias<keep_symbol>, + HelpText<"Alias for --keep-symbol">; + +def keep_file_symbols : Flag<["--"], "keep-file-symbols">, + HelpText<"Do not remove file symbols">; + +def only_keep_debug + : Flag<["--"], "only-keep-debug">, + HelpText<"Clear sections that would not be stripped by --strip-debug. " + "Currently only implemented for COFF.">; + +def discard_locals : Flag<["--"], "discard-locals">, + HelpText<"Remove compiler-generated local symbols, (e.g. " + "symbols starting with .L)">; +def X : Flag<["-"], "X">, + Alias<discard_locals>, + HelpText<"Alias for --discard-locals">; + +def discard_all + : Flag<["--"], "discard-all">, + HelpText<"Remove all local symbols except file and section symbols">; +def x : Flag<["-"], "x">, + Alias<discard_all>, + HelpText<"Alias for --discard-all">; + +def regex + : Flag<["--"], "regex">, + HelpText<"Permit regular expressions in name comparison">; + +def version : Flag<["--"], "version">, + HelpText<"Print the version and exit.">; +def V : Flag<["-"], "V">, + Alias<version>, + HelpText<"Alias for --version">; + +def wildcard + : Flag<["--"], "wildcard">, + HelpText<"Allow wildcard syntax for symbol-related flags. Incompatible " + "with --regex. Allows using '*' to match any number of " + "characters, '?' to match any single character, '\' to escape " + "special characters, and '[]' to define character classes. " + "Wildcards beginning with '!' will prevent a match, for example " + "\"-N '*' -N '!x'\" will strip all symbols except for \"x\".">; +def w : Flag<["-"], "w">, Alias<wildcard>, HelpText<"Alias for --wildcard">; diff --git a/tools/llvm-objcopy/CopyConfig.cpp b/tools/llvm-objcopy/CopyConfig.cpp index 8d6431b3044f..d707bec20c49 100644 --- a/tools/llvm-objcopy/CopyConfig.cpp +++ b/tools/llvm-objcopy/CopyConfig.cpp @@ -14,10 +14,10 @@ #include "llvm/ADT/StringSet.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" +#include "llvm/Support/CRC.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Errc.h" -#include "llvm/Support/JamCRC.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/StringSaver.h" #include <memory> @@ -155,6 +155,25 @@ static Expected<SectionRename> parseRenameSectionValue(StringRef FlagValue) { return SR; } +static Expected<std::pair<StringRef, uint64_t>> +parseSetSectionAlignment(StringRef FlagValue) { + if (!FlagValue.contains('=')) + return createStringError( + errc::invalid_argument, + "bad format for --set-section-alignment: missing '='"); + auto Split = StringRef(FlagValue).split('='); + if (Split.first.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --set-section-alignment: missing section name"); + uint64_t NewAlign; + if (Split.second.getAsInteger(0, NewAlign)) + return createStringError(errc::invalid_argument, + "invalid alignment for --set-section-alignment: '%s'", + Split.second.str().c_str()); + return std::make_pair(Split.first, NewAlign); +} + static Expected<SectionFlagsUpdate> parseSetSectionFlagValue(StringRef FlagValue) { if (!StringRef(FlagValue).contains('=')) @@ -177,106 +196,6 @@ parseSetSectionFlagValue(StringRef FlagValue) { return SFU; } -static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue) { - // Parse value given with --add-symbol option and create the - // new symbol if possible. The value format for --add-symbol is: - // - // <name>=[<section>:]<value>[,<flags>] - // - // where: - // <name> - symbol name, can be empty string - // <section> - optional section name. If not given ABS symbol is created - // <value> - symbol value, can be decimal or hexadecimal number prefixed - // with 0x. - // <flags> - optional flags affecting symbol type, binding or visibility: - // The following are currently supported: - // - // global, local, weak, default, hidden, file, section, object, - // indirect-function. - // - // The following flags are ignored and provided for GNU - // compatibility only: - // - // warning, debug, constructor, indirect, synthetic, - // unique-object, before=<symbol>. - NewSymbolInfo SI; - StringRef Value; - std::tie(SI.SymbolName, Value) = FlagValue.split('='); - if (Value.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --add-symbol, missing '=' after '%s'", - SI.SymbolName.str().c_str()); - - if (Value.contains(':')) { - std::tie(SI.SectionName, Value) = Value.split(':'); - if (SI.SectionName.empty() || Value.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --add-symbol, missing section name or symbol value"); - } - - SmallVector<StringRef, 6> Flags; - Value.split(Flags, ','); - if (Flags[0].getAsInteger(0, SI.Value)) - return createStringError(errc::invalid_argument, "bad symbol value: '%s'", - Flags[0].str().c_str()); - - using Functor = std::function<void(void)>; - SmallVector<StringRef, 6> UnsupportedFlags; - for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) - static_cast<Functor>( - StringSwitch<Functor>(Flags[I]) - .CaseLower("global", [&SI] { SI.Bind = ELF::STB_GLOBAL; }) - .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; }) - .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; }) - .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; }) - .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; }) - .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; }) - .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; }) - .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; }) - .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; }) - .CaseLower("indirect-function", - [&SI] { SI.Type = ELF::STT_GNU_IFUNC; }) - .CaseLower("debug", [] {}) - .CaseLower("constructor", [] {}) - .CaseLower("warning", [] {}) - .CaseLower("indirect", [] {}) - .CaseLower("synthetic", [] {}) - .CaseLower("unique-object", [] {}) - .StartsWithLower("before", [] {}) - .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))(); - if (!UnsupportedFlags.empty()) - return createStringError(errc::invalid_argument, - "unsupported flag%s for --add-symbol: '%s'", - UnsupportedFlags.size() > 1 ? "s" : "", - join(UnsupportedFlags, "', '").c_str()); - return SI; -} - -static const StringMap<MachineInfo> ArchMap{ - // Name, {EMachine, 64bit, LittleEndian} - {"aarch64", {ELF::EM_AARCH64, true, true}}, - {"arm", {ELF::EM_ARM, false, true}}, - {"i386", {ELF::EM_386, false, true}}, - {"i386:x86-64", {ELF::EM_X86_64, true, true}}, - {"mips", {ELF::EM_MIPS, false, false}}, - {"powerpc:common64", {ELF::EM_PPC64, true, true}}, - {"riscv:rv32", {ELF::EM_RISCV, false, true}}, - {"riscv:rv64", {ELF::EM_RISCV, true, true}}, - {"sparc", {ELF::EM_SPARC, false, false}}, - {"sparcel", {ELF::EM_SPARC, false, true}}, - {"x86-64", {ELF::EM_X86_64, true, true}}, -}; - -static Expected<const MachineInfo &> getMachineInfo(StringRef Arch) { - auto Iter = ArchMap.find(Arch); - if (Iter == std::end(ArchMap)) - return createStringError(errc::invalid_argument, - "invalid architecture: '%s'", Arch.str().c_str()); - return Iter->getValue(); -} - struct TargetInfo { FileFormat Format; MachineInfo Machine; @@ -341,9 +260,10 @@ getOutputTargetInfoByTargetName(StringRef TargetName) { return {TargetInfo{Format, MI}}; } -static Error addSymbolsFromFile(std::vector<NameOrRegex> &Symbols, - BumpPtrAllocator &Alloc, StringRef Filename, - bool UseRegex) { +static Error +addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, + StringRef Filename, MatchStyle MS, + llvm::function_ref<Error(Error)> ErrorCallback) { StringSaver Saver(Alloc); SmallVector<StringRef, 16> Lines; auto BufOrErr = MemoryBuffer::getFile(Filename); @@ -356,21 +276,47 @@ static Error addSymbolsFromFile(std::vector<NameOrRegex> &Symbols, // it's not empty. auto TrimmedLine = Line.split('#').first.trim(); if (!TrimmedLine.empty()) - Symbols.emplace_back(Saver.save(TrimmedLine), UseRegex); + if (Error E = Symbols.addMatcher(NameOrPattern::create( + Saver.save(TrimmedLine), MS, ErrorCallback))) + return E; } return Error::success(); } -NameOrRegex::NameOrRegex(StringRef Pattern, bool IsRegex) { - if (!IsRegex) { - Name = Pattern; - return; - } +Expected<NameOrPattern> +NameOrPattern::create(StringRef Pattern, MatchStyle MS, + llvm::function_ref<Error(Error)> ErrorCallback) { + switch (MS) { + case MatchStyle::Literal: + return NameOrPattern(Pattern); + case MatchStyle::Wildcard: { + SmallVector<char, 32> Data; + bool IsPositiveMatch = true; + if (Pattern[0] == '!') { + IsPositiveMatch = false; + Pattern = Pattern.drop_front(); + } + Expected<GlobPattern> GlobOrErr = GlobPattern::create(Pattern); + + // If we couldn't create it as a glob, report the error, but try again with + // a literal if the error reporting is non-fatal. + if (!GlobOrErr) { + if (Error E = ErrorCallback(GlobOrErr.takeError())) + return std::move(E); + return create(Pattern, MatchStyle::Literal, ErrorCallback); + } - SmallVector<char, 32> Data; - R = std::make_shared<Regex>( - ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data)); + return NameOrPattern(std::make_shared<GlobPattern>(*GlobOrErr), + IsPositiveMatch); + } + case MatchStyle::Regex: { + SmallVector<char, 32> Data; + return NameOrPattern(std::make_shared<Regex>( + ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data))); + } + } + llvm_unreachable("Unhandled llvm.objcopy.MatchStyle enum"); } static Error addSymbolsToRenameFromFile(StringMap<StringRef> &SymbolsToRename, @@ -407,10 +353,22 @@ template <class T> static ErrorOr<T> getAsInteger(StringRef Val) { return Result; } +static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS, + StringRef ToolName) { + OptTable.PrintHelp(OS, (ToolName + " input [output]").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, + // but libOption does not have that yet. + OS << "\nPass @FILE as argument to read options from FILE.\n"; +} + // ParseObjcopyOptions returns the config and sets the input arguments. If a // help flag is set then ParseObjcopyOptions will print the help messege and // exit. -Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { +Expected<DriverConfig> +parseObjcopyOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback) { DriverConfig DC; ObjcopyOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; @@ -418,12 +376,12 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); if (InputArgs.size() == 0) { - T.PrintHelp(errs(), "llvm-objcopy input [output]", "objcopy tool"); + printHelp(T, errs(), "llvm-objcopy"); exit(1); } if (InputArgs.hasArg(OBJCOPY_help)) { - T.PrintHelp(outs(), "llvm-objcopy input [output]", "objcopy tool"); + printHelp(T, outs(), "llvm-objcopy"); exit(0); } @@ -459,7 +417,18 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { errc::invalid_argument, "--target cannot be used with --input-target or --output-target"); - bool UseRegex = InputArgs.hasArg(OBJCOPY_regex); + if (InputArgs.hasArg(OBJCOPY_regex) && InputArgs.hasArg(OBJCOPY_wildcard)) + return createStringError(errc::invalid_argument, + "--regex and --wildcard are incompatible"); + + MatchStyle SectionMatchStyle = InputArgs.hasArg(OBJCOPY_regex) + ? MatchStyle::Regex + : MatchStyle::Wildcard; + MatchStyle SymbolMatchStyle = InputArgs.hasArg(OBJCOPY_regex) + ? MatchStyle::Regex + : InputArgs.hasArg(OBJCOPY_wildcard) + ? MatchStyle::Wildcard + : MatchStyle::Literal; StringRef InputFormat, OutputFormat; if (InputArgs.hasArg(OBJCOPY_target)) { InputFormat = InputArgs.getLastArgValue(OBJCOPY_target); @@ -476,28 +445,26 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { .Case("binary", FileFormat::Binary) .Case("ihex", FileFormat::IHex) .Default(FileFormat::Unspecified); - if (Config.InputFormat == FileFormat::Binary) { - auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture); - if (BinaryArch.empty()) - return createStringError( - errc::invalid_argument, - "specified binary input without specifiying an architecture"); - Expected<const MachineInfo &> MI = getMachineInfo(BinaryArch); - if (!MI) - return MI.takeError(); - Config.BinaryArch = *MI; - } + + if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) + Config.NewSymbolVisibility = + InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility); Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat) .Case("binary", FileFormat::Binary) .Case("ihex", FileFormat::IHex) .Default(FileFormat::Unspecified); - if (Config.OutputFormat == FileFormat::Unspecified && !OutputFormat.empty()) { - Expected<TargetInfo> Target = getOutputTargetInfoByTargetName(OutputFormat); - if (!Target) - return Target.takeError(); - Config.OutputFormat = Target->Format; - Config.OutputArch = Target->Machine; + if (Config.OutputFormat == FileFormat::Unspecified) { + if (OutputFormat.empty()) { + Config.OutputFormat = Config.InputFormat; + } else { + Expected<TargetInfo> Target = + getOutputTargetInfoByTargetName(OutputFormat); + if (!Target) + return Target.takeError(); + Config.OutputFormat = Target->Format; + Config.OutputArch = Target->Machine; + } } if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections, @@ -535,12 +502,8 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { if (!DebugOrErr) return createFileError(Config.AddGnuDebugLink, DebugOrErr.getError()); auto Debug = std::move(*DebugOrErr); - JamCRC CRC; - CRC.update( - ArrayRef<char>(Debug->getBuffer().data(), Debug->getBuffer().size())); - // The CRC32 value needs to be complemented because the JamCRC doesn't - // finalize the CRC32 value. - Config.GnuDebugLinkCRC32 = ~CRC.getCRC(); + Config.GnuDebugLinkCRC32 = + llvm::crc32(arrayRefFromStringRef(Debug->getBuffer())); } Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); if (InputArgs.hasArg(OBJCOPY_build_id_link_input)) @@ -582,6 +545,13 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { "multiple renames of section '%s'", SR->OriginalName.str().c_str()); } + for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_alignment)) { + Expected<std::pair<StringRef, uint64_t>> NameAndAlign = + parseSetSectionAlignment(Arg->getValue()); + if (!NameAndAlign) + return NameAndAlign.takeError(); + Config.SetSectionAlignment[NameAndAlign->first] = NameAndAlign->second; + } for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_flags)) { Expected<SectionFlagsUpdate> SFU = parseSetSectionFlagValue(Arg->getValue()); @@ -612,13 +582,28 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { } for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) - Config.ToRemove.emplace_back(Arg->getValue(), UseRegex); + if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section)) - Config.KeepSection.emplace_back(Arg->getValue(), UseRegex); + if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_only_section)) - Config.OnlySection.emplace_back(Arg->getValue(), UseRegex); - for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) - Config.AddSection.push_back(Arg->getValue()); + if (Error E = Config.OnlySection.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); + for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) { + StringRef ArgValue(Arg->getValue()); + if (!ArgValue.contains('=')) + return createStringError(errc::invalid_argument, + "bad format for --add-section: missing '='"); + if (ArgValue.split("=").second.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --add-section: missing file name"); + Config.AddSection.push_back(ArgValue); + } for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section)) Config.DumpSection.push_back(Arg->getValue()); Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); @@ -645,53 +630,71 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { if (Config.DiscardMode == DiscardType::All) Config.StripDebug = true; for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) - Config.SymbolsToLocalize.emplace_back(Arg->getValue(), UseRegex); + if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) - Config.SymbolsToKeepGlobal.emplace_back(Arg->getValue(), UseRegex); + if (Error E = Config.SymbolsToKeepGlobal.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol)) - Config.SymbolsToGlobalize.emplace_back(Arg->getValue(), UseRegex); + if (Error E = Config.SymbolsToGlobalize.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol)) - Config.SymbolsToWeaken.emplace_back(Arg->getValue(), UseRegex); + if (Error E = Config.SymbolsToWeaken.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol)) - Config.SymbolsToRemove.emplace_back(Arg->getValue(), UseRegex); + if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol)) - Config.UnneededSymbolsToRemove.emplace_back(Arg->getValue(), UseRegex); + if (Error E = + Config.UnneededSymbolsToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols)) if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) - Config.SymbolsToKeep.emplace_back(Arg->getValue(), UseRegex); + if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbols)) - if (Error E = addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, - Arg->getValue(), UseRegex)) + if (Error E = + addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(), + SymbolMatchStyle, ErrorCallback)) return std::move(E); - for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) { - Expected<NewSymbolInfo> NSI = parseNewSymbolInfo(Arg->getValue()); - if (!NSI) - return NSI.takeError(); - Config.SymbolsToAdd.push_back(*NSI); - } + for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) + Config.SymbolsToAdd.push_back(Arg->getValue()); Config.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links); @@ -754,19 +757,19 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { // exit. Expected<DriverConfig> parseStripOptions(ArrayRef<const char *> ArgsArr, - std::function<Error(Error)> ErrorCallback) { + llvm::function_ref<Error(Error)> ErrorCallback) { StripOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); if (InputArgs.size() == 0) { - T.PrintHelp(errs(), "llvm-strip [options] file...", "strip tool"); + printHelp(T, errs(), "llvm-strip"); exit(1); } if (InputArgs.hasArg(STRIP_help)) { - T.PrintHelp(outs(), "llvm-strip [options] file...", "strip tool"); + printHelp(T, outs(), "llvm-strip"); exit(0); } @@ -792,7 +795,17 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, "multiple input files cannot be used in combination with -o"); CopyConfig Config; - bool UseRegexp = InputArgs.hasArg(STRIP_regex); + + if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard)) + return createStringError(errc::invalid_argument, + "--regex and --wildcard are incompatible"); + MatchStyle SectionMatchStyle = + InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex : MatchStyle::Wildcard; + MatchStyle SymbolMatchStyle = InputArgs.hasArg(STRIP_regex) + ? MatchStyle::Regex + : InputArgs.hasArg(STRIP_wildcard) + ? MatchStyle::Wildcard + : MatchStyle::Literal; Config.AllowBrokenLinks = InputArgs.hasArg(STRIP_allow_broken_links); Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); @@ -801,6 +814,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, InputArgs.hasFlag(STRIP_discard_all, STRIP_discard_locals) ? DiscardType::All : DiscardType::Locals; + Config.StripSections = InputArgs.hasArg(STRIP_strip_sections); Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded); if (auto Arg = InputArgs.getLastArg(STRIP_strip_all, STRIP_no_strip_all)) Config.StripAll = Arg->getOption().getID() == STRIP_strip_all; @@ -809,16 +823,24 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); for (auto Arg : InputArgs.filtered(STRIP_keep_section)) - Config.KeepSection.emplace_back(Arg->getValue(), UseRegexp); + if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(STRIP_remove_section)) - Config.ToRemove.emplace_back(Arg->getValue(), UseRegexp); + if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(STRIP_strip_symbol)) - Config.SymbolsToRemove.emplace_back(Arg->getValue(), UseRegexp); + if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(STRIP_keep_symbol)) - Config.SymbolsToKeep.emplace_back(Arg->getValue(), UseRegexp); + if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); if (!InputArgs.hasArg(STRIP_no_strip_all) && !Config.StripDebug && !Config.StripUnneeded && Config.DiscardMode == DiscardType::None && diff --git a/tools/llvm-objcopy/CopyConfig.h b/tools/llvm-objcopy/CopyConfig.h index aff3631a487c..55a55d3a2bc2 100644 --- a/tools/llvm-objcopy/CopyConfig.h +++ b/tools/llvm-objcopy/CopyConfig.h @@ -9,6 +9,7 @@ #ifndef LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H #define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H +#include "ELF/ELFConfig.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/Optional.h" @@ -18,6 +19,7 @@ #include "llvm/Object/ELFTypes.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Error.h" +#include "llvm/Support/GlobPattern.h" #include "llvm/Support/Regex.h" // Necessary for llvm::DebugCompressionType::None #include "llvm/Target/TargetOptions.h" @@ -87,36 +89,71 @@ enum class DiscardType { Locals, // --discard-locals (-X) }; -class NameOrRegex { +enum class MatchStyle { + Literal, // Default for symbols. + Wildcard, // Default for sections, or enabled with --wildcard (-w). + Regex, // Enabled with --regex. +}; + +class NameOrPattern { StringRef Name; // Regex is shared between multiple CopyConfig instances. std::shared_ptr<Regex> R; + std::shared_ptr<GlobPattern> G; + bool IsPositiveMatch = true; + + NameOrPattern(StringRef N) : Name(N) {} + NameOrPattern(std::shared_ptr<Regex> R) : R(R) {} + NameOrPattern(std::shared_ptr<GlobPattern> G, bool IsPositiveMatch) + : G(G), IsPositiveMatch(IsPositiveMatch) {} public: - NameOrRegex(StringRef Pattern, bool IsRegex); - bool operator==(StringRef S) const { return R ? R->match(S) : Name == S; } + // ErrorCallback is used to handle recoverable errors. An Error returned + // by the callback aborts the parsing and is then returned by this function. + static Expected<NameOrPattern> + create(StringRef Pattern, MatchStyle MS, + llvm::function_ref<Error(Error)> ErrorCallback); + + bool isPositiveMatch() const { return IsPositiveMatch; } + bool operator==(StringRef S) const { + return R ? R->match(S) : G ? G->match(S) : Name == S; + } bool operator!=(StringRef S) const { return !operator==(S); } }; -struct NewSymbolInfo { - StringRef SymbolName; - StringRef SectionName; - uint64_t Value = 0; - uint8_t Type = ELF::STT_NOTYPE; - uint8_t Bind = ELF::STB_GLOBAL; - uint8_t Visibility = ELF::STV_DEFAULT; +// Matcher that checks symbol or section names against the command line flags +// provided for that option. +class NameMatcher { + std::vector<NameOrPattern> PosMatchers; + std::vector<NameOrPattern> NegMatchers; + +public: + Error addMatcher(Expected<NameOrPattern> Matcher) { + if (!Matcher) + return Matcher.takeError(); + if (Matcher->isPositiveMatch()) + PosMatchers.push_back(std::move(*Matcher)); + else + NegMatchers.push_back(std::move(*Matcher)); + return Error::success(); + } + bool matches(StringRef S) const { + return is_contained(PosMatchers, S) && !is_contained(NegMatchers, S); + } + bool empty() const { return PosMatchers.empty() && NegMatchers.empty(); } }; // Configuration for copying/stripping a single file. struct CopyConfig { + // Format-specific options to be initialized lazily when needed. + Optional<elf::ELFCopyConfig> ELF; + // Main input/output options StringRef InputFilename; FileFormat InputFormat; StringRef OutputFilename; FileFormat OutputFormat; - // Only applicable for --input-format=binary - MachineInfo BinaryArch; // Only applicable when --output-format!=binary (e.g. elf64-x86-64). Optional<MachineInfo> OutputArch; @@ -132,24 +169,30 @@ struct CopyConfig { StringRef SymbolsPrefix; StringRef AllocSectionsPrefix; DiscardType DiscardMode = DiscardType::None; + Optional<StringRef> NewSymbolVisibility; // Repeated options std::vector<StringRef> AddSection; std::vector<StringRef> DumpSection; - std::vector<NewSymbolInfo> SymbolsToAdd; - std::vector<NameOrRegex> KeepSection; - std::vector<NameOrRegex> OnlySection; - std::vector<NameOrRegex> SymbolsToGlobalize; - std::vector<NameOrRegex> SymbolsToKeep; - std::vector<NameOrRegex> SymbolsToLocalize; - std::vector<NameOrRegex> SymbolsToRemove; - std::vector<NameOrRegex> UnneededSymbolsToRemove; - std::vector<NameOrRegex> SymbolsToWeaken; - std::vector<NameOrRegex> ToRemove; - std::vector<NameOrRegex> SymbolsToKeepGlobal; + std::vector<StringRef> SymbolsToAdd; + + // Section matchers + NameMatcher KeepSection; + NameMatcher OnlySection; + NameMatcher ToRemove; + + // Symbol matchers + NameMatcher SymbolsToGlobalize; + NameMatcher SymbolsToKeep; + NameMatcher SymbolsToLocalize; + NameMatcher SymbolsToRemove; + NameMatcher UnneededSymbolsToRemove; + NameMatcher SymbolsToWeaken; + NameMatcher SymbolsToKeepGlobal; // Map options StringMap<SectionRename> SectionsToRename; + StringMap<uint64_t> SetSectionAlignment; StringMap<SectionFlagsUpdate> SetSectionFlags; StringMap<StringRef> SymbolsToRename; @@ -178,6 +221,18 @@ struct CopyConfig { bool Weaken = false; bool DecompressDebugSections = false; DebugCompressionType CompressionType = DebugCompressionType::None; + + // parseELFConfig performs ELF-specific command-line parsing. Fills `ELF` on + // success or returns an Error otherwise. + Error parseELFConfig() { + if (!ELF) { + Expected<elf::ELFCopyConfig> ELFConfig = elf::parseConfig(*this); + if (!ELFConfig) + return ELFConfig.takeError(); + ELF = *ELFConfig; + } + return Error::success(); + } }; // Configuration for the overall invocation of this tool. When invoked as @@ -190,8 +245,11 @@ struct DriverConfig { // ParseObjcopyOptions returns the config and sets the input arguments. If a // help flag is set then ParseObjcopyOptions will print the help messege and -// exit. -Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr); +// exit. ErrorCallback is used to handle recoverable errors. An Error returned +// by the callback aborts the parsing and is then returned by this function. +Expected<DriverConfig> +parseObjcopyOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); // ParseStripOptions returns the config and sets the input arguments. If a // help flag is set then ParseStripOptions will print the help messege and @@ -199,7 +257,7 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr); // by the callback aborts the parsing and is then returned by this function. Expected<DriverConfig> parseStripOptions(ArrayRef<const char *> ArgsArr, - std::function<Error(Error)> ErrorCallback); + llvm::function_ref<Error(Error)> ErrorCallback); } // namespace objcopy } // namespace llvm diff --git a/tools/llvm-objcopy/ELF/ELFConfig.cpp b/tools/llvm-objcopy/ELF/ELFConfig.cpp new file mode 100644 index 000000000000..40993760add7 --- /dev/null +++ b/tools/llvm-objcopy/ELF/ELFConfig.cpp @@ -0,0 +1,133 @@ +//===- ELFConfig.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CopyConfig.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { +namespace elf { + +static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue, + uint8_t DefaultVisibility) { + // Parse value given with --add-symbol option and create the + // new symbol if possible. The value format for --add-symbol is: + // + // <name>=[<section>:]<value>[,<flags>] + // + // where: + // <name> - symbol name, can be empty string + // <section> - optional section name. If not given ABS symbol is created + // <value> - symbol value, can be decimal or hexadecimal number prefixed + // with 0x. + // <flags> - optional flags affecting symbol type, binding or visibility: + // The following are currently supported: + // + // global, local, weak, default, hidden, file, section, object, + // indirect-function. + // + // The following flags are ignored and provided for GNU + // compatibility only: + // + // warning, debug, constructor, indirect, synthetic, + // unique-object, before=<symbol>. + NewSymbolInfo SI; + StringRef Value; + std::tie(SI.SymbolName, Value) = FlagValue.split('='); + if (Value.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --add-symbol, missing '=' after '%s'", + SI.SymbolName.str().c_str()); + + if (Value.contains(':')) { + std::tie(SI.SectionName, Value) = Value.split(':'); + if (SI.SectionName.empty() || Value.empty()) + return createStringError( + errc::invalid_argument, + "bad format for --add-symbol, missing section name or symbol value"); + } + + SmallVector<StringRef, 6> Flags; + Value.split(Flags, ','); + if (Flags[0].getAsInteger(0, SI.Value)) + return createStringError(errc::invalid_argument, "bad symbol value: '%s'", + Flags[0].str().c_str()); + + SI.Visibility = DefaultVisibility; + + using Functor = std::function<void(void)>; + SmallVector<StringRef, 6> UnsupportedFlags; + for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) + static_cast<Functor>( + StringSwitch<Functor>(Flags[I]) + .CaseLower("global", [&SI] { SI.Bind = ELF::STB_GLOBAL; }) + .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; }) + .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; }) + .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; }) + .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; }) + .CaseLower("protected", + [&SI] { SI.Visibility = ELF::STV_PROTECTED; }) + .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; }) + .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; }) + .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; }) + .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; }) + .CaseLower("indirect-function", + [&SI] { SI.Type = ELF::STT_GNU_IFUNC; }) + .CaseLower("debug", [] {}) + .CaseLower("constructor", [] {}) + .CaseLower("warning", [] {}) + .CaseLower("indirect", [] {}) + .CaseLower("synthetic", [] {}) + .CaseLower("unique-object", [] {}) + .StartsWithLower("before", [] {}) + .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))(); + if (!UnsupportedFlags.empty()) + return createStringError(errc::invalid_argument, + "unsupported flag%s for --add-symbol: '%s'", + UnsupportedFlags.size() > 1 ? "s" : "", + join(UnsupportedFlags, "', '").c_str()); + return SI; +} + +Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config) { + ELFCopyConfig ELFConfig; + if (Config.NewSymbolVisibility) { + const uint8_t Invalid = 0xff; + ELFConfig.NewSymbolVisibility = + StringSwitch<uint8_t>(*Config.NewSymbolVisibility) + .Case("default", ELF::STV_DEFAULT) + .Case("hidden", ELF::STV_HIDDEN) + .Case("internal", ELF::STV_INTERNAL) + .Case("protected", ELF::STV_PROTECTED) + .Default(Invalid); + + if (ELFConfig.NewSymbolVisibility == Invalid) + return createStringError(errc::invalid_argument, + "'%s' is not a valid symbol visibility", + Config.NewSymbolVisibility->str().c_str()); + } + + for (StringRef Arg : Config.SymbolsToAdd) { + Expected<elf::NewSymbolInfo> NSI = parseNewSymbolInfo( + Arg, + ELFConfig.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT)); + if (!NSI) + return NSI.takeError(); + ELFConfig.SymbolsToAdd.push_back(*NSI); + } + + return ELFConfig; +} + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm diff --git a/tools/llvm-objcopy/ELF/ELFConfig.h b/tools/llvm-objcopy/ELF/ELFConfig.h new file mode 100644 index 000000000000..977efbc4166f --- /dev/null +++ b/tools/llvm-objcopy/ELF/ELFConfig.h @@ -0,0 +1,44 @@ +//===- ELFConfig.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_ELFCONFIG_H +#define LLVM_TOOLS_OBJCOPY_ELFCONFIG_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/Error.h" +#include <vector> + +namespace llvm { +namespace objcopy { +struct CopyConfig; + +namespace elf { + +struct NewSymbolInfo { + StringRef SymbolName; + StringRef SectionName; + uint64_t Value = 0; + uint8_t Type = ELF::STT_NOTYPE; + uint8_t Bind = ELF::STB_GLOBAL; + uint8_t Visibility = ELF::STV_DEFAULT; +}; + +struct ELFCopyConfig { + Optional<uint8_t> NewSymbolVisibility; + std::vector<NewSymbolInfo> SymbolsToAdd; +}; + +Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config); + +} // namespace elf +} // namespace objcopy +} // namespace llvm + +#endif diff --git a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp index b366c6e55987..8bf7e0f88010 100644 --- a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -136,16 +136,16 @@ static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config, // Depending on the initial ELFT and OutputFormat we need a different Writer. switch (OutputElfType) { case ELFT_ELF32LE: - return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, + return std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, !Config.StripSections); case ELFT_ELF64LE: - return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, + return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, !Config.StripSections); case ELFT_ELF32BE: - return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, + return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, !Config.StripSections); case ELFT_ELF64BE: - return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, + return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, !Config.StripSections); } llvm_unreachable("Invalid output format"); @@ -156,9 +156,9 @@ static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, ElfType OutputElfType) { switch (Config.OutputFormat) { case FileFormat::Binary: - return llvm::make_unique<BinaryWriter>(Obj, Buf); + return std::make_unique<BinaryWriter>(Obj, Buf); case FileFormat::IHex: - return llvm::make_unique<IHexWriter>(Obj, Buf); + return std::make_unique<IHexWriter>(Obj, Buf); default: return createELFWriter(Config, Obj, Buf, OutputElfType); } @@ -263,7 +263,7 @@ static Error linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink, static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader, StringRef File, ElfType OutputElfType) { - auto DWOFile = Reader.create(); + auto DWOFile = Reader.create(false); auto OnlyKeepDWOPred = [&DWOFile](const SectionBase &Sec) { return onlyKeepDWOPred(*DWOFile, Sec); }; @@ -305,9 +305,9 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename, SecName.str().c_str()); } -static bool isCompressable(const SectionBase &Section) { - return !(Section.Flags & ELF::SHF_COMPRESSED) && - StringRef(Section.Name).startswith(".debug"); +static bool isCompressable(const SectionBase &Sec) { + return !(Sec.Flags & ELF::SHF_COMPRESSED) && + StringRef(Sec.Name).startswith(".debug"); } static void replaceDebugSections( @@ -356,7 +356,7 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { if (!Sym.isCommon() && Sym.getShndx() != SHN_UNDEF && ((Config.LocalizeHidden && (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) || - is_contained(Config.SymbolsToLocalize, Sym.Name))) + Config.SymbolsToLocalize.matches(Sym.Name))) Sym.Binding = STB_LOCAL; // Note: these two globalize flags have very similar names but different @@ -370,16 +370,15 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { // --keep-global-symbol. Because of that, make sure to check // --globalize-symbol second. if (!Config.SymbolsToKeepGlobal.empty() && - !is_contained(Config.SymbolsToKeepGlobal, Sym.Name) && + !Config.SymbolsToKeepGlobal.matches(Sym.Name) && Sym.getShndx() != SHN_UNDEF) Sym.Binding = STB_LOCAL; - if (is_contained(Config.SymbolsToGlobalize, Sym.Name) && + if (Config.SymbolsToGlobalize.matches(Sym.Name) && Sym.getShndx() != SHN_UNDEF) Sym.Binding = STB_GLOBAL; - if (is_contained(Config.SymbolsToWeaken, Sym.Name) && - Sym.Binding == STB_GLOBAL) + if (Config.SymbolsToWeaken.matches(Sym.Name) && Sym.Binding == STB_GLOBAL) Sym.Binding = STB_WEAK; if (Config.Weaken && Sym.Binding == STB_GLOBAL && @@ -399,12 +398,12 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { // symbols are still 'needed' and which are not. if (Config.StripUnneeded || !Config.UnneededSymbolsToRemove.empty() || !Config.OnlySection.empty()) { - for (auto &Section : Obj.sections()) - Section.markSymbols(); + for (SectionBase &Sec : Obj.sections()) + Sec.markSymbols(); } auto RemoveSymbolsPred = [&](const Symbol &Sym) { - if (is_contained(Config.SymbolsToKeep, Sym.Name) || + if (Config.SymbolsToKeep.matches(Sym.Name) || (Config.KeepFileSymbols && Sym.Type == STT_FILE)) return false; @@ -418,12 +417,12 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { if (Config.StripAll || Config.StripAllGNU) return true; - if (is_contained(Config.SymbolsToRemove, Sym.Name)) + if (Config.SymbolsToRemove.matches(Sym.Name)) return true; if ((Config.StripUnneeded || - is_contained(Config.UnneededSymbolsToRemove, Sym.Name)) && - isUnneededSymbol(Sym)) + Config.UnneededSymbolsToRemove.matches(Sym.Name)) && + (!Obj.isRelocatable() || isUnneededSymbol(Sym))) return true; // We want to remove undefined symbols if all references have been stripped. @@ -443,7 +442,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { // Removes: if (!Config.ToRemove.empty()) { RemovePred = [&Config](const SectionBase &Sec) { - return is_contained(Config.ToRemove, Sec.Name); + return Config.ToRemove.matches(Sec.Name); }; } @@ -481,7 +480,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { }; } - if (Config.StripDebug) { + if (Config.StripDebug || Config.StripUnneeded) { RemovePred = [RemovePred](const SectionBase &Sec) { return RemovePred(Sec) || isDebugSection(Sec); }; @@ -523,7 +522,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { if (!Config.OnlySection.empty()) { RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) { // Explicitly keep these sections regardless of previous removes. - if (is_contained(Config.OnlySection, Sec.Name)) + if (Config.OnlySection.matches(Sec.Name)) return false; // Allow all implicit removes. @@ -545,7 +544,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { if (!Config.KeepSection.empty()) { RemovePred = [&Config, RemovePred](const SectionBase &Sec) { // Explicitly keep these sections regardless of previous removes. - if (is_contained(Config.KeepSection, Sec.Name)) + if (Config.KeepSection.matches(Sec.Name)) return false; // Otherwise defer to RemovePred. return RemovePred(Sec); @@ -614,9 +613,8 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj, if (Error E = updateAndRemoveSymbols(Config, Obj)) return E; - if (!Config.SectionsToRename.empty() || !Config.AllocSectionsPrefix.empty()) { - DenseSet<SectionBase *> PrefixedSections; - for (auto &Sec : Obj.sections()) { + if (!Config.SectionsToRename.empty()) { + for (SectionBase &Sec : Obj.sections()) { const auto Iter = Config.SectionsToRename.find(Sec.Name); if (Iter != Config.SectionsToRename.end()) { const SectionRename &SR = Iter->second; @@ -624,63 +622,62 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj, if (SR.NewFlags.hasValue()) setSectionFlagsAndType(Sec, SR.NewFlags.getValue()); } + } + } - // Add a prefix to allocated sections and their relocation sections. This - // should be done after renaming the section by Config.SectionToRename to - // imitate the GNU objcopy behavior. - if (!Config.AllocSectionsPrefix.empty()) { - if (Sec.Flags & SHF_ALLOC) { - Sec.Name = (Config.AllocSectionsPrefix + Sec.Name).str(); - PrefixedSections.insert(&Sec); - - // Rename relocation sections associated to the allocated sections. - // For example, if we rename .text to .prefix.text, we also rename - // .rel.text to .rel.prefix.text. - // - // Dynamic relocation sections (SHT_REL[A] with SHF_ALLOC) are handled - // above, e.g., .rela.plt is renamed to .prefix.rela.plt, not - // .rela.prefix.plt since GNU objcopy does so. - } else if (auto *RelocSec = dyn_cast<RelocationSectionBase>(&Sec)) { - auto *TargetSec = RelocSec->getSection(); - if (TargetSec && (TargetSec->Flags & SHF_ALLOC)) { - StringRef prefix; - switch (Sec.Type) { - case SHT_REL: - prefix = ".rel"; - break; - case SHT_RELA: - prefix = ".rela"; - break; - default: - continue; - } - - // If the relocation section comes *after* the target section, we - // don't add Config.AllocSectionsPrefix because we've already added - // the prefix to TargetSec->Name. Otherwise, if the relocation - // section comes *before* the target section, we add the prefix. - if (PrefixedSections.count(TargetSec)) { - Sec.Name = (prefix + TargetSec->Name).str(); - } else { - const auto Iter = Config.SectionsToRename.find(TargetSec->Name); - if (Iter != Config.SectionsToRename.end()) { - // Both `--rename-section` and `--prefix-alloc-sections` are - // given but the target section is not yet renamed. - Sec.Name = - (prefix + Config.AllocSectionsPrefix + Iter->second.NewName) - .str(); - } else { - Sec.Name = - (prefix + Config.AllocSectionsPrefix + TargetSec->Name) - .str(); - } - } + // Add a prefix to allocated sections and their relocation sections. This + // should be done after renaming the section by Config.SectionToRename to + // imitate the GNU objcopy behavior. + if (!Config.AllocSectionsPrefix.empty()) { + DenseSet<SectionBase *> PrefixedSections; + for (SectionBase &Sec : Obj.sections()) { + if (Sec.Flags & SHF_ALLOC) { + Sec.Name = (Config.AllocSectionsPrefix + Sec.Name).str(); + PrefixedSections.insert(&Sec); + } else if (auto *RelocSec = dyn_cast<RelocationSectionBase>(&Sec)) { + // Rename relocation sections associated to the allocated sections. + // For example, if we rename .text to .prefix.text, we also rename + // .rel.text to .rel.prefix.text. + // + // Dynamic relocation sections (SHT_REL[A] with SHF_ALLOC) are handled + // above, e.g., .rela.plt is renamed to .prefix.rela.plt, not + // .rela.prefix.plt since GNU objcopy does so. + const SectionBase *TargetSec = RelocSec->getSection(); + if (TargetSec && (TargetSec->Flags & SHF_ALLOC)) { + StringRef prefix; + switch (Sec.Type) { + case SHT_REL: + prefix = ".rel"; + break; + case SHT_RELA: + prefix = ".rela"; + break; + default: + llvm_unreachable("not a relocation section"); } + + // If the relocation section comes *after* the target section, we + // don't add Config.AllocSectionsPrefix because we've already added + // the prefix to TargetSec->Name. Otherwise, if the relocation + // section comes *before* the target section, we add the prefix. + if (PrefixedSections.count(TargetSec)) + Sec.Name = (prefix + TargetSec->Name).str(); + else + Sec.Name = + (prefix + Config.AllocSectionsPrefix + TargetSec->Name).str(); } } } } + if (!Config.SetSectionAlignment.empty()) { + for (SectionBase &Sec : Obj.sections()) { + auto I = Config.SetSectionAlignment.find(Sec.Name); + if (I != Config.SetSectionAlignment.end()) + Sec.Align = I->second; + } + } + if (!Config.SetSectionFlags.empty()) { for (auto &Sec : Obj.sections()) { const auto Iter = Config.SetSectionFlags.find(Sec.Name); @@ -721,7 +718,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj, Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink, Config.GnuDebugLinkCRC32); - for (const NewSymbolInfo &SI : Config.SymbolsToAdd) { + for (const NewSymbolInfo &SI : Config.ELF->SymbolsToAdd) { SectionBase *Sec = Obj.findSection(SI.SectionName); uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value; Obj.SymbolTable->addSymbol( @@ -746,9 +743,9 @@ static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out, Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, Buffer &Out) { IHexReader Reader(&In); - std::unique_ptr<Object> Obj = Reader.create(); + std::unique_ptr<Object> Obj = Reader.create(true); const ElfType OutputElfType = - getOutputElfType(Config.OutputArch.getValueOr(Config.BinaryArch)); + getOutputElfType(Config.OutputArch.getValueOr(MachineInfo())); if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType)) return E; return writeOutput(Config, *Obj, Out, OutputElfType); @@ -756,13 +753,15 @@ Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, Buffer &Out) { - BinaryReader Reader(Config.BinaryArch, &In); - std::unique_ptr<Object> Obj = Reader.create(); + uint8_t NewSymbolVisibility = + Config.ELF->NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT); + BinaryReader Reader(&In, NewSymbolVisibility); + std::unique_ptr<Object> Obj = Reader.create(true); // Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch // (-B<arch>). const ElfType OutputElfType = - getOutputElfType(Config.OutputArch.getValueOr(Config.BinaryArch)); + getOutputElfType(Config.OutputArch.getValueOr(MachineInfo())); if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType)) return E; return writeOutput(Config, *Obj, Out, OutputElfType); @@ -771,7 +770,7 @@ Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, Error executeObjcopyOnBinary(const CopyConfig &Config, object::ELFObjectFileBase &In, Buffer &Out) { ELFReader Reader(&In, Config.ExtractPartition); - std::unique_ptr<Object> Obj = Reader.create(); + std::unique_ptr<Object> Obj = Reader.create(!Config.SymbolsToAdd.empty()); // Prefer OutputArch (-O<format>) if set, otherwise infer it from the input. const ElfType OutputElfType = Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue()) diff --git a/tools/llvm-objcopy/ELF/Object.cpp b/tools/llvm-objcopy/ELF/Object.cpp index fa696380e17c..74145dad6e6b 100644 --- a/tools/llvm-objcopy/ELF/Object.cpp +++ b/tools/llvm-objcopy/ELF/Object.cpp @@ -397,7 +397,7 @@ void SectionWriter::visit(const OwnedDataSection &Sec) { llvm::copy(Sec.Data, Out.getBufferStart() + Sec.Offset); } -static const std::vector<uint8_t> ZlibGnuMagic = {'Z', 'L', 'I', 'B'}; +static constexpr std::array<uint8_t, 4> ZlibGnuMagic = {{'Z', 'L', 'I', 'B'}}; static bool isDataGnuCompressed(ArrayRef<uint8_t> Data) { return Data.size() > ZlibGnuMagic.size() && @@ -665,7 +665,7 @@ void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type, Sym.Visibility = Visibility; Sym.Size = SymbolSize; Sym.Index = Symbols.size(); - Symbols.emplace_back(llvm::make_unique<Symbol>(Sym)); + Symbols.emplace_back(std::make_unique<Symbol>(Sym)); Size += this->EntrySize; } @@ -1055,29 +1055,28 @@ void GroupSection::accept(MutableSectionVisitor &Visitor) { } // Returns true IFF a section is wholly inside the range of a segment -static bool sectionWithinSegment(const SectionBase &Section, - const Segment &Segment) { +static bool sectionWithinSegment(const SectionBase &Sec, const Segment &Seg) { // If a section is empty it should be treated like it has a size of 1. This is // to clarify the case when an empty section lies on a boundary between two // segments and ensures that the section "belongs" to the second segment and // not the first. - uint64_t SecSize = Section.Size ? Section.Size : 1; + uint64_t SecSize = Sec.Size ? Sec.Size : 1; - if (Section.Type == SHT_NOBITS) { - if (!(Section.Flags & SHF_ALLOC)) + if (Sec.Type == SHT_NOBITS) { + if (!(Sec.Flags & SHF_ALLOC)) return false; - bool SectionIsTLS = Section.Flags & SHF_TLS; - bool SegmentIsTLS = Segment.Type == PT_TLS; + bool SectionIsTLS = Sec.Flags & SHF_TLS; + bool SegmentIsTLS = Seg.Type == PT_TLS; if (SectionIsTLS != SegmentIsTLS) return false; - return Segment.VAddr <= Section.Addr && - Segment.VAddr + Segment.MemSize >= Section.Addr + SecSize; + return Seg.VAddr <= Sec.Addr && + Seg.VAddr + Seg.MemSize >= Sec.Addr + SecSize; } - return Segment.Offset <= Section.OriginalOffset && - Segment.Offset + Segment.FileSize >= Section.OriginalOffset + SecSize; + return Seg.Offset <= Sec.OriginalOffset && + Seg.Offset + Seg.FileSize >= Sec.OriginalOffset + SecSize; } // Returns true IFF a segment's original offset is inside of another segment's @@ -1113,7 +1112,7 @@ void BasicELFBuilder::initFileHeader() { Obj->OSABI = ELFOSABI_NONE; Obj->ABIVersion = 0; Obj->Entry = 0x0; - Obj->Machine = EMachine; + Obj->Machine = EM_NONE; Obj->Version = 1; } @@ -1141,8 +1140,8 @@ SymbolTableSection *BasicELFBuilder::addSymTab(StringTableSection *StrTab) { } void BasicELFBuilder::initSections() { - for (auto &Section : Obj->sections()) - Section.initialize(Obj->sections()); + for (SectionBase &Sec : Obj->sections()) + Sec.initialize(Obj->sections()); } void BinaryELFBuilder::addData(SymbolTableSection *SymTab) { @@ -1161,11 +1160,12 @@ void BinaryELFBuilder::addData(SymbolTableSection *SymTab) { Twine Prefix = Twine("_binary_") + SanitizedFilename; SymTab->addSymbol(Prefix + "_start", STB_GLOBAL, STT_NOTYPE, &DataSection, - /*Value=*/0, STV_DEFAULT, 0, 0); + /*Value=*/0, NewSymbolVisibility, 0, 0); SymTab->addSymbol(Prefix + "_end", STB_GLOBAL, STT_NOTYPE, &DataSection, - /*Value=*/DataSection.Size, STV_DEFAULT, 0, 0); + /*Value=*/DataSection.Size, NewSymbolVisibility, 0, 0); SymTab->addSymbol(Prefix + "_size", STB_GLOBAL, STT_NOTYPE, nullptr, - /*Value=*/DataSection.Size, STV_DEFAULT, SHN_ABS, 0); + /*Value=*/DataSection.Size, NewSymbolVisibility, SHN_ABS, + 0); } std::unique_ptr<Object> BinaryELFBuilder::build() { @@ -1255,10 +1255,9 @@ template <class ELFT> void ELFBuilder<ELFT>::findEhdrOffset() { if (!ExtractPartition) return; - for (const SectionBase &Section : Obj.sections()) { - if (Section.Type == SHT_LLVM_PART_EHDR && - Section.Name == *ExtractPartition) { - EhdrOffset = Section.Offset; + for (const SectionBase &Sec : Obj.sections()) { + if (Sec.Type == SHT_LLVM_PART_EHDR && Sec.Name == *ExtractPartition) { + EhdrOffset = Sec.Offset; return; } } @@ -1287,15 +1286,12 @@ void ELFBuilder<ELFT>::readProgramHeaders(const ELFFile<ELFT> &HeadersFile) { Seg.MemSize = Phdr.p_memsz; Seg.Align = Phdr.p_align; Seg.Index = Index++; - for (SectionBase &Section : Obj.sections()) { - if (sectionWithinSegment(Section, Seg)) { - Seg.addSection(&Section); - if (!Section.ParentSegment || - Section.ParentSegment->Offset > Seg.Offset) { - Section.ParentSegment = &Seg; - } + for (SectionBase &Sec : Obj.sections()) + if (sectionWithinSegment(Sec, Seg)) { + Seg.addSection(&Sec); + if (!Sec.ParentSegment || Sec.ParentSegment->Offset > Seg.Offset) + Sec.ParentSegment = &Seg; } - } } auto &ElfHdr = Obj.ElfHdrSegment; @@ -1531,7 +1527,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { } } -template <class ELFT> void ELFBuilder<ELFT>::readSections() { +template <class ELFT> void ELFBuilder<ELFT>::readSections(bool EnsureSymtab) { // If a section index table exists we'll need to initialize it before we // initialize the symbol table because the symbol table might need to // reference it. @@ -1544,16 +1540,37 @@ template <class ELFT> void ELFBuilder<ELFT>::readSections() { if (Obj.SymbolTable) { Obj.SymbolTable->initialize(Obj.sections()); initSymbolTable(Obj.SymbolTable); + } else if (EnsureSymtab) { + // Reuse the existing SHT_STRTAB section if 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 .strtab to .shstrtab. + 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; } // Now that all sections and symbols have been added we can add // relocations that reference symbols and set the link and info fields for // relocation sections. - for (auto &Section : Obj.sections()) { - if (&Section == Obj.SymbolTable) + for (auto &Sec : Obj.sections()) { + if (&Sec == Obj.SymbolTable) continue; - Section.initialize(Obj.sections()); - if (auto RelSec = dyn_cast<RelocationSection>(&Section)) { + Sec.initialize(Obj.sections()); + if (auto RelSec = dyn_cast<RelocationSection>(&Sec)) { auto Shdr = unwrapOrError(ElfFile.sections()).begin() + RelSec->Index; if (RelSec->Type == SHT_REL) initRelocations(RelSec, Obj.SymbolTable, @@ -1561,7 +1578,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readSections() { else initRelocations(RelSec, Obj.SymbolTable, unwrapOrError(ElfFile.relas(Shdr))); - } else if (auto GroupSec = dyn_cast<GroupSection>(&Section)) { + } else if (auto GroupSec = dyn_cast<GroupSection>(&Sec)) { initGroupSection(GroupSec); } } @@ -1582,7 +1599,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readSections() { " is not a string table"); } -template <class ELFT> void ELFBuilder<ELFT>::build() { +template <class ELFT> void ELFBuilder<ELFT>::build(bool EnsureSymtab) { readSectionHeaders(); findEhdrOffset(); @@ -1601,7 +1618,7 @@ template <class ELFT> void ELFBuilder<ELFT>::build() { Obj.Entry = Ehdr.e_entry; Obj.Flags = Ehdr.e_flags; - readSections(); + readSections(EnsureSymtab); readProgramHeaders(HeadersFile); } @@ -1609,8 +1626,8 @@ Writer::~Writer() {} Reader::~Reader() {} -std::unique_ptr<Object> BinaryReader::create() const { - return BinaryELFBuilder(MInfo.EMachine, MemBuf).build(); +std::unique_ptr<Object> BinaryReader::create(bool /*EnsureSymtab*/) const { + return BinaryELFBuilder(MemBuf, NewSymbolVisibility).build(); } Expected<std::vector<IHexRecord>> IHexReader::parse() const { @@ -1639,28 +1656,28 @@ Expected<std::vector<IHexRecord>> IHexReader::parse() const { return std::move(Records); } -std::unique_ptr<Object> IHexReader::create() const { +std::unique_ptr<Object> IHexReader::create(bool /*EnsureSymtab*/) const { std::vector<IHexRecord> Records = unwrapOrError(parse()); return IHexELFBuilder(Records).build(); } -std::unique_ptr<Object> ELFReader::create() const { - auto Obj = llvm::make_unique<Object>(); +std::unique_ptr<Object> ELFReader::create(bool EnsureSymtab) const { + auto Obj = std::make_unique<Object>(); if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { ELFBuilder<ELF32LE> Builder(*O, *Obj, ExtractPartition); - Builder.build(); + Builder.build(EnsureSymtab); return Obj; } else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { ELFBuilder<ELF64LE> Builder(*O, *Obj, ExtractPartition); - Builder.build(); + Builder.build(EnsureSymtab); return Obj; } else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { ELFBuilder<ELF32BE> Builder(*O, *Obj, ExtractPartition); - Builder.build(); + Builder.build(EnsureSymtab); return Obj; } else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { ELFBuilder<ELF64BE> Builder(*O, *Obj, ExtractPartition); - Builder.build(); + Builder.build(EnsureSymtab); return Obj; } error("invalid file type"); @@ -1693,7 +1710,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { Ehdr.e_ehsize = sizeof(Elf_Ehdr); if (WriteSectionHeaders && Obj.sections().size() != 0) { Ehdr.e_shentsize = sizeof(Elf_Shdr); - Ehdr.e_shoff = Obj.SHOffset; + Ehdr.e_shoff = Obj.SHOff; // """ // If the number of sections is greater than or equal to // SHN_LORESERVE (0xff00), this member has the value zero and the actual @@ -1732,7 +1749,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeShdrs() { // This reference serves to write the dummy section header at the begining // of the file. It is not used for anything else Elf_Shdr &Shdr = - *reinterpret_cast<Elf_Shdr *>(Buf.getBufferStart() + Obj.SHOffset); + *reinterpret_cast<Elf_Shdr *>(Buf.getBufferStart() + Obj.SHOff); Shdr.sh_name = 0; Shdr.sh_type = SHT_NULL; Shdr.sh_flags = 0; @@ -1862,26 +1879,13 @@ void Object::sortSections() { }); } -static uint64_t alignToAddr(uint64_t Offset, uint64_t Addr, uint64_t Align) { - // Calculate Diff such that (Offset + Diff) & -Align == Addr & -Align. - if (Align == 0) - Align = 1; - auto Diff = - static_cast<int64_t>(Addr % Align) - static_cast<int64_t>(Offset % Align); - // We only want to add to Offset, however, so if Diff < 0 we can add Align and - // (Offset + Diff) & -Align == Addr & -Align will still hold. - if (Diff < 0) - Diff += Align; - return Offset + Diff; -} - // Orders segments such that if x = y->ParentSegment then y comes before x. static void orderSegments(std::vector<Segment *> &Segments) { llvm::stable_sort(Segments, compareSegmentsByOffset); } // This function finds a consistent layout for a list of segments starting from -// an Offset. It assumes that Segments have been sorted by OrderSegments and +// an Offset. It assumes that Segments have been sorted by orderSegments and // returns an Offset one past the end of the last segment. static uint64_t layoutSegments(std::vector<Segment *> &Segments, uint64_t Offset) { @@ -1902,8 +1906,8 @@ static uint64_t layoutSegments(std::vector<Segment *> &Segments, Seg->Offset = Parent->Offset + Seg->OriginalOffset - Parent->OriginalOffset; } else { - Offset = alignToAddr(Offset, Seg->VAddr, Seg->Align); - Seg->Offset = Offset; + Seg->Offset = + alignTo(Offset, std::max<uint64_t>(Seg->Align, 1), Seg->VAddr); } Offset = std::max(Offset, Seg->Offset + Seg->FileSize); } @@ -1925,17 +1929,17 @@ static uint64_t layoutSections(Range Sections, uint64_t Offset) { // of the segment we can assign a new offset to the section. For sections not // covered by segments we can just bump Offset to the next valid location. uint32_t Index = 1; - for (auto &Section : Sections) { - Section.Index = Index++; - if (Section.ParentSegment != nullptr) { - auto Segment = *Section.ParentSegment; - Section.Offset = - Segment.Offset + (Section.OriginalOffset - Segment.OriginalOffset); + for (auto &Sec : Sections) { + Sec.Index = Index++; + if (Sec.ParentSegment != nullptr) { + auto Segment = *Sec.ParentSegment; + Sec.Offset = + Segment.Offset + (Sec.OriginalOffset - Segment.OriginalOffset); } else { - Offset = alignTo(Offset, Section.Align == 0 ? 1 : Section.Align); - Section.Offset = Offset; - if (Section.Type != SHT_NOBITS) - Offset += Section.Size; + Offset = alignTo(Offset, Sec.Align == 0 ? 1 : Sec.Align); + Sec.Offset = Offset; + if (Sec.Type != SHT_NOBITS) + Offset += Sec.Size; } } return Offset; @@ -1971,16 +1975,16 @@ template <class ELFT> void ELFWriter<ELFT>::assignOffsets() { // Offset so that SHOffset is valid. if (WriteSectionHeaders) Offset = alignTo(Offset, sizeof(Elf_Addr)); - Obj.SHOffset = Offset; + Obj.SHOff = Offset; } template <class ELFT> size_t ELFWriter<ELFT>::totalSize() const { // We already have the section header offset so we can calculate the total // size by just adding up the size of each section header. if (!WriteSectionHeaders) - return Obj.SHOffset; + return Obj.SHOff; size_t ShdrCount = Obj.sections().size() + 1; // Includes null shdr. - return Obj.SHOffset + ShdrCount * sizeof(Elf_Shdr); + return Obj.SHOff + ShdrCount * sizeof(Elf_Shdr); } template <class ELFT> Error ELFWriter<ELFT>::write() { @@ -1995,6 +1999,25 @@ template <class ELFT> Error ELFWriter<ELFT>::write() { return Buf.commit(); } +static Error removeUnneededSections(Object &Obj) { + // We can remove an empty symbol table from non-relocatable objects. + // Relocatable objects typically have relocation sections whose + // sh_link field points to .symtab, so we can't remove .symtab + // even if it is empty. + if (Obj.isRelocatable() || Obj.SymbolTable == nullptr || + !Obj.SymbolTable->empty()) + return Error::success(); + + // .strtab can be used for section names. In such a case we shouldn't + // remove it. + auto *StrTab = Obj.SymbolTable->getStrTab() == Obj.SectionNames + ? nullptr + : Obj.SymbolTable->getStrTab(); + return Obj.removeSections(false, [&](const SectionBase &Sec) { + return &Sec == Obj.SymbolTable || &Sec == StrTab; + }); +} + template <class ELFT> Error ELFWriter<ELFT>::finalize() { // It could happen that SectionNames has been removed and yet the user wants // a section header table output. We need to throw an error if a user tries @@ -2004,6 +2027,8 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() { "cannot write section header table because " "section header string table was removed"); + if (Error E = removeUnneededSections(Obj)) + return E; Obj.sortSections(); // We need to assign indexes before we perform layout because we need to know @@ -2045,9 +2070,8 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() { // Make sure we add the names of all the sections. Importantly this must be // done after we decide to add or remove SectionIndexes. if (Obj.SectionNames != nullptr) - for (const auto &Section : Obj.sections()) { - Obj.SectionNames->addString(Section.Name); - } + for (const SectionBase &Sec : Obj.sections()) + Obj.SectionNames->addString(Sec.Name); initEhdrSegment(); @@ -2055,8 +2079,8 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() { // Also, the output arch may not be the same as the input arch, so fix up // size-related fields before doing layout calculations. uint64_t Index = 0; - auto SecSizer = llvm::make_unique<ELFSectionSizer<ELFT>>(); - for (auto &Sec : Obj.sections()) { + auto SecSizer = std::make_unique<ELFSectionSizer<ELFT>>(); + for (SectionBase &Sec : Obj.sections()) { Sec.Index = Index++; Sec.accept(*SecSizer); } @@ -2082,40 +2106,36 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() { // Finally now that all offsets and indexes have been set we can finalize any // remaining issues. - uint64_t Offset = Obj.SHOffset + sizeof(Elf_Shdr); - for (SectionBase &Section : Obj.sections()) { - Section.HeaderOffset = Offset; + uint64_t Offset = Obj.SHOff + sizeof(Elf_Shdr); + for (SectionBase &Sec : Obj.sections()) { + Sec.HeaderOffset = Offset; Offset += sizeof(Elf_Shdr); if (WriteSectionHeaders) - Section.NameIndex = Obj.SectionNames->findIndex(Section.Name); - Section.finalize(); + Sec.NameIndex = Obj.SectionNames->findIndex(Sec.Name); + Sec.finalize(); } if (Error E = Buf.allocate(totalSize())) return E; - SecWriter = llvm::make_unique<ELFSectionWriter<ELFT>>(Buf); + SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(Buf); return Error::success(); } Error BinaryWriter::write() { - for (auto &Section : Obj.sections()) - if (Section.Flags & SHF_ALLOC) - Section.accept(*SecWriter); + for (const SectionBase &Sec : Obj.allocSections()) + Sec.accept(*SecWriter); return Buf.commit(); } Error BinaryWriter::finalize() { - // TODO: Create a filter range to construct OrderedSegments from so that this - // code can be deduped with assignOffsets above. This should also solve the - // todo below for LayoutSections. // 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 (SectionBase &Section : Obj.sections()) - if ((Section.Flags & SHF_ALLOC) != 0 && Section.ParentSegment != nullptr) - OrderedSegments.push_back(Section.ParentSegment); + 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 @@ -2130,7 +2150,7 @@ Error BinaryWriter::finalize() { 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 + // segments in OrderedSegments. If there were duplicates then layoutSegments // would do very strange things. auto End = std::unique(std::begin(OrderedSegments), std::end(OrderedSegments)); @@ -2158,28 +2178,20 @@ Error BinaryWriter::finalize() { } } - // TODO: generalize LayoutSections to take a range. Pass a special range - // constructed from an iterator that skips values for which a predicate does - // not hold. Then pass such a range to LayoutSections instead of constructing - // AllocatedSections here. - std::vector<SectionBase *> AllocatedSections; - for (SectionBase &Section : Obj.sections()) - if (Section.Flags & SHF_ALLOC) - AllocatedSections.push_back(&Section); - layoutSections(make_pointee_range(AllocatedSections), Offset); + layoutSections(Obj.allocSections(), Offset); // 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 + // layoutSections, because we want to truncate the last segment to the end of // its last section, to match GNU objcopy's behaviour. TotalSize = 0; - for (SectionBase *Section : AllocatedSections) - if (Section->Type != SHT_NOBITS) - TotalSize = std::max(TotalSize, Section->Offset + Section->Size); + for (const SectionBase &Sec : Obj.allocSections()) + if (Sec.Type != SHT_NOBITS) + TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size); if (Error E = Buf.allocate(TotalSize)) return E; - SecWriter = llvm::make_unique<BinarySectionWriter>(Buf); + SecWriter = std::make_unique<BinarySectionWriter>(Buf); return Error::success(); } @@ -2259,17 +2271,17 @@ Error IHexWriter::finalize() { // If any section we're to write has segment then we // switch to using physical addresses. Otherwise we // use section virtual address. - for (auto &Section : Obj.sections()) - if (ShouldWrite(Section) && IsInPtLoad(Section)) { + for (const SectionBase &Sec : Obj.sections()) + if (ShouldWrite(Sec) && IsInPtLoad(Sec)) { UseSegments = true; break; } - for (auto &Section : Obj.sections()) - if (ShouldWrite(Section) && (!UseSegments || IsInPtLoad(Section))) { - if (Error E = checkSection(Section)) + for (const SectionBase &Sec : Obj.sections()) + if (ShouldWrite(Sec) && (!UseSegments || IsInPtLoad(Sec))) { + if (Error E = checkSection(Sec)) return E; - Sections.insert(&Section); + Sections.insert(&Sec); } IHexSectionWriterBase LengthCalc(Buf); diff --git a/tools/llvm-objcopy/ELF/Object.h b/tools/llvm-objcopy/ELF/Object.h index f3df93b9662f..eeacb014e4dc 100644 --- a/tools/llvm-objcopy/ELF/Object.h +++ b/tools/llvm-objcopy/ELF/Object.h @@ -57,8 +57,8 @@ public: : Sections(Secs) {} SectionTableRef(const SectionTableRef &) = default; - iterator begin() { return iterator(Sections.data()); } - iterator end() { return iterator(Sections.data() + Sections.size()); } + iterator begin() const { return iterator(Sections.data()); } + iterator end() const { return iterator(Sections.data() + Sections.size()); } size_t size() const { return Sections.size(); } SectionBase *getSection(uint32_t Index, Twine ErrMsg); @@ -863,7 +863,7 @@ public: class Reader { public: virtual ~Reader(); - virtual std::unique_ptr<Object> create() const = 0; + virtual std::unique_ptr<Object> create(bool EnsureSymtab) const = 0; }; using object::Binary; @@ -873,7 +873,6 @@ using object::OwningBinary; class BasicELFBuilder { protected: - uint16_t EMachine; std::unique_ptr<Object> Obj; void initFileHeader(); @@ -883,17 +882,18 @@ protected: void initSections(); public: - BasicELFBuilder(uint16_t EM) - : EMachine(EM), Obj(llvm::make_unique<Object>()) {} + BasicELFBuilder() : Obj(std::make_unique<Object>()) {} }; class BinaryELFBuilder : public BasicELFBuilder { MemoryBuffer *MemBuf; + uint8_t NewSymbolVisibility; void addData(SymbolTableSection *SymTab); public: - BinaryELFBuilder(uint16_t EM, MemoryBuffer *MB) - : BasicELFBuilder(EM), MemBuf(MB) {} + BinaryELFBuilder(MemoryBuffer *MB, uint8_t NewSymbolVisibility) + : BasicELFBuilder(), MemBuf(MB), + NewSymbolVisibility(NewSymbolVisibility) {} std::unique_ptr<Object> build(); }; @@ -905,7 +905,7 @@ class IHexELFBuilder : public BasicELFBuilder { public: IHexELFBuilder(const std::vector<IHexRecord> &Records) - : BasicELFBuilder(ELF::EM_386), Records(Records) {} + : BasicELFBuilder(), Records(Records) {} std::unique_ptr<Object> build(); }; @@ -926,7 +926,7 @@ private: void initGroupSection(GroupSection *GroupSec); void initSymbolTable(SymbolTableSection *SymTab); void readSectionHeaders(); - void readSections(); + void readSections(bool EnsureSymtab); void findEhdrOffset(); SectionBase &makeSection(const Elf_Shdr &Shdr); @@ -936,17 +936,17 @@ public: : ElfFile(*ElfObj.getELFFile()), Obj(Obj), ExtractPartition(ExtractPartition) {} - void build(); + void build(bool EnsureSymtab); }; class BinaryReader : public Reader { - const MachineInfo &MInfo; MemoryBuffer *MemBuf; + uint8_t NewSymbolVisibility; public: - BinaryReader(const MachineInfo &MI, MemoryBuffer *MB) - : MInfo(MI), MemBuf(MB) {} - std::unique_ptr<Object> create() const override; + BinaryReader(MemoryBuffer *MB, const uint8_t NewSymbolVisibility) + : MemBuf(MB), NewSymbolVisibility(NewSymbolVisibility) {} + std::unique_ptr<Object> create(bool EnsureSymtab) const override; }; class IHexReader : public Reader { @@ -968,7 +968,7 @@ class IHexReader : public Reader { public: IHexReader(MemoryBuffer *MB) : MemBuf(MB) {} - std::unique_ptr<Object> create() const override; + std::unique_ptr<Object> create(bool EnsureSymtab) const override; }; class ELFReader : public Reader { @@ -976,7 +976,7 @@ class ELFReader : public Reader { Optional<StringRef> ExtractPartition; public: - std::unique_ptr<Object> create() const override; + std::unique_ptr<Object> create(bool EnsureSymtab) const override; explicit ELFReader(Binary *B, Optional<StringRef> ExtractPartition) : Bin(B), ExtractPartition(ExtractPartition) {} }; @@ -990,6 +990,10 @@ private: std::vector<SegPtr> Segments; std::vector<SecPtr> RemovedSections; + static bool sectionIsAlloc(const SectionBase &Sec) { + return Sec.Flags & ELF::SHF_ALLOC; + }; + public: template <class T> using Range = iterator_range< @@ -1011,13 +1015,14 @@ public: uint8_t OSABI; uint8_t ABIVersion; uint64_t Entry; - uint64_t SHOffset; + uint64_t SHOff; uint32_t Type; uint32_t Machine; uint32_t Version; uint32_t Flags; bool HadShdrs = true; + bool MustBeRelocatable = false; StringTableSection *SectionNames = nullptr; SymbolTableSection *SymbolTable = nullptr; SectionIndexSection *SectionIndexTable = nullptr; @@ -1027,6 +1032,13 @@ public: ConstRange<SectionBase> sections() const { return make_pointee_range(Sections); } + iterator_range< + filter_iterator<pointee_iterator<std::vector<SecPtr>::const_iterator>, + decltype(§ionIsAlloc)>> + allocSections() const { + return make_filter_range(make_pointee_range(Sections), sectionIsAlloc); + } + SectionBase *findSection(StringRef Name) { auto SecIt = find_if(Sections, [&](const SecPtr &Sec) { return Sec->Name == Name; }); @@ -1041,16 +1053,20 @@ public: std::function<bool(const SectionBase &)> ToRemove); Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove); template <class T, class... Ts> T &addSection(Ts &&... Args) { - auto Sec = llvm::make_unique<T>(std::forward<Ts>(Args)...); + auto Sec = std::make_unique<T>(std::forward<Ts>(Args)...); auto Ptr = Sec.get(); + MustBeRelocatable |= isa<RelocationSection>(*Ptr); Sections.emplace_back(std::move(Sec)); Ptr->Index = Sections.size(); return *Ptr; } Segment &addSegment(ArrayRef<uint8_t> Data) { - Segments.emplace_back(llvm::make_unique<Segment>(Data)); + Segments.emplace_back(std::make_unique<Segment>(Data)); return *Segments.back(); } + bool isRelocatable() const { + return (Type != ELF::ET_DYN && Type != ELF::ET_EXEC) || MustBeRelocatable; + } }; } // end namespace elf diff --git a/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp new file mode 100644 index 000000000000..f621f3aa09cf --- /dev/null +++ b/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp @@ -0,0 +1,350 @@ +//===- MachOLayoutBuilder.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MachOLayoutBuilder.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" + +namespace llvm { +namespace objcopy { +namespace macho { + +uint32_t MachOLayoutBuilder::computeSizeOfCmds() const { + uint32_t Size = 0; + for (const auto &LC : O.LoadCommands) { + const MachO::macho_load_command &MLC = LC.MachOLoadCommand; + auto cmd = MLC.load_command_data.cmd; + switch (cmd) { + case MachO::LC_SEGMENT: + Size += sizeof(MachO::segment_command) + + sizeof(MachO::section) * LC.Sections.size(); + continue; + case MachO::LC_SEGMENT_64: + Size += sizeof(MachO::segment_command_64) + + sizeof(MachO::section_64) * LC.Sections.size(); + continue; + } + + switch (cmd) { +#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ + case MachO::LCName: \ + Size += sizeof(MachO::LCStruct) + LC.Payload.size(); \ + break; +#include "llvm/BinaryFormat/MachO.def" +#undef HANDLE_LOAD_COMMAND + } + } + + return Size; +} + +void MachOLayoutBuilder::constructStringTable() { + for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols) + StrTableBuilder.add(Sym->Name); + StrTableBuilder.finalize(); +} + +void MachOLayoutBuilder::updateSymbolIndexes() { + uint32_t Index = 0; + for (auto &Symbol : O.SymTable.Symbols) + Symbol->Index = Index++; +} + +// Updates the index and the number of local/external/undefined symbols. +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) { + return (A->isLocalSymbol() && !B->isLocalSymbol()) || + (!A->isUndefinedSymbol() && + B->isUndefinedSymbol()); + }) && + "Symbols are not sorted by their types."); + + uint32_t NumLocalSymbols = 0; + auto Iter = O.SymTable.Symbols.begin(); + auto End = O.SymTable.Symbols.end(); + for (; Iter != End; ++Iter) { + if ((*Iter)->isExternalSymbol()) + break; + + ++NumLocalSymbols; + } + + uint32_t NumExtDefSymbols = 0; + for (; Iter != End; ++Iter) { + if ((*Iter)->isUndefinedSymbol()) + break; + + ++NumExtDefSymbols; + } + + MLC.dysymtab_command_data.ilocalsym = 0; + MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols; + MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols; + MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols; + MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols; + MLC.dysymtab_command_data.nundefsym = + O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols); +} + +// Recomputes and updates offset and size fields in load commands and sections +// since they could be modified. +uint64_t MachOLayoutBuilder::layoutSegments() { + auto HeaderSize = + Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); + const bool IsObjectFile = + O.Header.FileType == MachO::HeaderFileType::MH_OBJECT; + uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0; + for (auto &LC : O.LoadCommands) { + auto &MLC = LC.MachOLoadCommand; + StringRef Segname; + uint64_t SegmentVmAddr; + uint64_t SegmentVmSize; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + SegmentVmAddr = MLC.segment_command_data.vmaddr; + SegmentVmSize = MLC.segment_command_data.vmsize; + Segname = StringRef(MLC.segment_command_data.segname, + strnlen(MLC.segment_command_data.segname, + sizeof(MLC.segment_command_data.segname))); + break; + case MachO::LC_SEGMENT_64: + SegmentVmAddr = MLC.segment_command_64_data.vmaddr; + SegmentVmSize = MLC.segment_command_64_data.vmsize; + Segname = StringRef(MLC.segment_command_64_data.segname, + strnlen(MLC.segment_command_64_data.segname, + sizeof(MLC.segment_command_64_data.segname))); + break; + default: + continue; + } + + if (Segname == "__LINKEDIT") { + // We update the __LINKEDIT segment later (in layoutTail). + assert(LC.Sections.empty() && "__LINKEDIT segment has sections"); + LinkEditLoadCommand = &MLC; + continue; + } + + // Update file offsets and sizes of sections. + uint64_t SegOffset = Offset; + uint64_t SegFileSize = 0; + uint64_t VMSize = 0; + for (auto &Sec : LC.Sections) { + if (IsObjectFile) { + 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; + } + VMSize = std::max(VMSize, Sec.Addr + Sec.Size); + } else { + if (Sec.isVirtualSection()) { + Sec.Offset = 0; + VMSize += Sec.Size; + } 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); + } + } + } + + if (IsObjectFile) { + Offset += SegFileSize; + } else { + Offset = alignTo(Offset + SegFileSize, PageSize); + SegFileSize = alignTo(SegFileSize, PageSize); + // Use the original vmsize if the segment is __PAGEZERO. + VMSize = + Segname == "__PAGEZERO" ? SegmentVmSize : alignTo(VMSize, PageSize); + } + + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + MLC.segment_command_data.cmdsize = + sizeof(MachO::segment_command) + + sizeof(MachO::section) * LC.Sections.size(); + MLC.segment_command_data.nsects = LC.Sections.size(); + MLC.segment_command_data.fileoff = SegOffset; + MLC.segment_command_data.vmsize = VMSize; + MLC.segment_command_data.filesize = SegFileSize; + break; + case MachO::LC_SEGMENT_64: + MLC.segment_command_64_data.cmdsize = + sizeof(MachO::segment_command_64) + + sizeof(MachO::section_64) * LC.Sections.size(); + MLC.segment_command_64_data.nsects = LC.Sections.size(); + MLC.segment_command_64_data.fileoff = SegOffset; + MLC.segment_command_64_data.vmsize = VMSize; + MLC.segment_command_64_data.filesize = SegFileSize; + break; + } + } + + return Offset; +} + +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; + } + + return Offset; +} + +Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { + // 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. + uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); + uint64_t StartOfLinkEdit = Offset; + uint64_t StartOfRebaseInfo = StartOfLinkEdit; + uint64_t StartOfBindingInfo = StartOfRebaseInfo + O.Rebases.Opcodes.size(); + uint64_t StartOfWeakBindingInfo = StartOfBindingInfo + O.Binds.Opcodes.size(); + uint64_t StartOfLazyBindingInfo = + StartOfWeakBindingInfo + O.WeakBinds.Opcodes.size(); + uint64_t StartOfExportTrie = + StartOfLazyBindingInfo + O.LazyBinds.Opcodes.size(); + uint64_t StartOfFunctionStarts = StartOfExportTrie + O.Exports.Trie.size(); + uint64_t StartOfDataInCode = + StartOfFunctionStarts + O.FunctionStarts.Data.size(); + uint64_t StartOfSymbols = StartOfDataInCode + O.DataInCode.Data.size(); + uint64_t StartOfIndirectSymbols = + StartOfSymbols + NListSize * O.SymTable.Symbols.size(); + uint64_t StartOfSymbolStrings = + StartOfIndirectSymbols + + sizeof(uint32_t) * O.IndirectSymTable.Symbols.size(); + uint64_t LinkEditSize = + (StartOfSymbolStrings + StrTableBuilder.getSize()) - StartOfLinkEdit; + + // Now we have determined the layout of the contents of the __LINKEDIT + // segment. Update its load command. + if (LinkEditLoadCommand) { + MachO::macho_load_command *MLC = LinkEditLoadCommand; + switch (LinkEditLoadCommand->load_command_data.cmd) { + case MachO::LC_SEGMENT: + MLC->segment_command_data.cmdsize = sizeof(MachO::segment_command); + MLC->segment_command_data.fileoff = StartOfLinkEdit; + MLC->segment_command_data.vmsize = alignTo(LinkEditSize, PageSize); + MLC->segment_command_data.filesize = LinkEditSize; + break; + case MachO::LC_SEGMENT_64: + MLC->segment_command_64_data.cmdsize = sizeof(MachO::segment_command_64); + MLC->segment_command_64_data.fileoff = StartOfLinkEdit; + MLC->segment_command_64_data.vmsize = alignTo(LinkEditSize, PageSize); + MLC->segment_command_64_data.filesize = LinkEditSize; + break; + } + } + + for (auto &LC : O.LoadCommands) { + auto &MLC = LC.MachOLoadCommand; + auto cmd = MLC.load_command_data.cmd; + switch (cmd) { + case MachO::LC_SYMTAB: + MLC.symtab_command_data.symoff = StartOfSymbols; + MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size(); + MLC.symtab_command_data.stroff = StartOfSymbolStrings; + MLC.symtab_command_data.strsize = StrTableBuilder.getSize(); + break; + case MachO::LC_DYSYMTAB: { + if (MLC.dysymtab_command_data.ntoc != 0 || + MLC.dysymtab_command_data.nmodtab != 0 || + MLC.dysymtab_command_data.nextrefsyms != 0 || + MLC.dysymtab_command_data.nlocrel != 0 || + MLC.dysymtab_command_data.nextrel != 0) + return createStringError(llvm::errc::not_supported, + "shared library is not yet supported"); + + if (!O.IndirectSymTable.Symbols.empty()) { + MLC.dysymtab_command_data.indirectsymoff = StartOfIndirectSymbols; + MLC.dysymtab_command_data.nindirectsyms = + O.IndirectSymTable.Symbols.size(); + } + + updateDySymTab(MLC); + break; + } + case MachO::LC_DATA_IN_CODE: + MLC.linkedit_data_command_data.dataoff = StartOfDataInCode; + MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size(); + break; + case MachO::LC_FUNCTION_STARTS: + MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts; + MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size(); + break; + case MachO::LC_DYLD_INFO: + case MachO::LC_DYLD_INFO_ONLY: + MLC.dyld_info_command_data.rebase_off = + O.Rebases.Opcodes.empty() ? 0 : StartOfRebaseInfo; + MLC.dyld_info_command_data.rebase_size = O.Rebases.Opcodes.size(); + MLC.dyld_info_command_data.bind_off = + O.Binds.Opcodes.empty() ? 0 : StartOfBindingInfo; + MLC.dyld_info_command_data.bind_size = O.Binds.Opcodes.size(); + MLC.dyld_info_command_data.weak_bind_off = + O.WeakBinds.Opcodes.empty() ? 0 : StartOfWeakBindingInfo; + MLC.dyld_info_command_data.weak_bind_size = O.WeakBinds.Opcodes.size(); + MLC.dyld_info_command_data.lazy_bind_off = + O.LazyBinds.Opcodes.empty() ? 0 : StartOfLazyBindingInfo; + MLC.dyld_info_command_data.lazy_bind_size = O.LazyBinds.Opcodes.size(); + MLC.dyld_info_command_data.export_off = + O.Exports.Trie.empty() ? 0 : StartOfExportTrie; + MLC.dyld_info_command_data.export_size = O.Exports.Trie.size(); + break; + case MachO::LC_LOAD_DYLINKER: + case MachO::LC_MAIN: + case MachO::LC_RPATH: + case MachO::LC_SEGMENT: + case MachO::LC_SEGMENT_64: + case MachO::LC_VERSION_MIN_MACOSX: + case MachO::LC_BUILD_VERSION: + case MachO::LC_ID_DYLIB: + case MachO::LC_LOAD_DYLIB: + case MachO::LC_UUID: + case MachO::LC_SOURCE_VERSION: + // Nothing to update. + break; + default: + // Abort if it's unsupported in order to prevent corrupting the object. + return createStringError(llvm::errc::not_supported, + "unsupported load command (cmd=0x%x)", cmd); + } + } + + return Error::success(); +} + +Error MachOLayoutBuilder::layout() { + O.Header.NCmds = O.LoadCommands.size(); + O.Header.SizeOfCmds = computeSizeOfCmds(); + constructStringTable(); + updateSymbolIndexes(); + uint64_t Offset = layoutSegments(); + Offset = layoutRelocations(Offset); + return layoutTail(Offset); +} + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm diff --git a/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h b/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h new file mode 100644 index 000000000000..21cbe56605de --- /dev/null +++ b/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h @@ -0,0 +1,50 @@ +//===- MachOLayoutBuilder.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_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H +#define LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H + +#include "MachOObjcopy.h" +#include "Object.h" + +namespace llvm { +namespace objcopy { +namespace macho { + +class MachOLayoutBuilder { + Object &O; + bool Is64Bit; + uint64_t PageSize; + + // Points to the __LINKEDIT segment if it exists. + MachO::macho_load_command *LinkEditLoadCommand = nullptr; + StringTableBuilder StrTableBuilder{StringTableBuilder::MachO}; + + uint32_t computeSizeOfCmds() const; + void constructStringTable(); + void updateSymbolIndexes(); + void updateDySymTab(MachO::macho_load_command &MLC); + uint64_t layoutSegments(); + uint64_t layoutRelocations(uint64_t Offset); + Error layoutTail(uint64_t Offset); + +public: + MachOLayoutBuilder(Object &O, bool Is64Bit, uint64_t PageSize) + : O(O), Is64Bit(Is64Bit), PageSize(PageSize) {} + + // Recomputes and updates fields in the given object such as file offsets. + Error layout(); + + StringTableBuilder &getStringTableBuilder() { return StrTableBuilder; } +}; + +} // end namespace macho +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H diff --git a/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/tools/llvm-objcopy/MachO/MachOObjcopy.cpp index 19343b65dd1e..6d586e7d73f1 100644 --- a/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -25,18 +25,20 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || !Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() || !Config.DumpSection.empty() || !Config.KeepSection.empty() || - !Config.OnlySection.empty() || !Config.SymbolsToGlobalize.empty() || - !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || - !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || - !Config.SectionsToRename.empty() || !Config.SymbolsToRename.empty() || + Config.NewSymbolVisibility || !Config.OnlySection.empty() || + !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || + !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || + !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || + !Config.SymbolsToRename.empty() || !Config.UnneededSymbolsToRemove.empty() || - !Config.SetSectionFlags.empty() || !Config.ToRemove.empty() || - Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || - Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc || - Config.StripSections || Config.Weaken || Config.DecompressDebugSections || - Config.StripDebug || Config.StripNonAlloc || Config.StripSections || - Config.StripUnneeded || Config.DiscardMode != DiscardType::None || - !Config.SymbolsToAdd.empty() || Config.EntryExpr) { + !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || + !Config.ToRemove.empty() || Config.ExtractDWO || Config.KeepFileSymbols || + Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO || + Config.StripNonAlloc || Config.StripSections || Config.Weaken || + Config.DecompressDebugSections || Config.StripDebug || + Config.StripNonAlloc || Config.StripSections || Config.StripUnneeded || + Config.DiscardMode != DiscardType::None || !Config.SymbolsToAdd.empty() || + Config.EntryExpr) { return createStringError(llvm::errc::invalid_argument, "option not supported by llvm-objcopy for MachO"); } @@ -57,7 +59,11 @@ Error executeObjcopyOnBinary(const CopyConfig &Config, if (Error E = handleArgs(Config, *O)) return createFileError(Config.InputFilename, std::move(E)); - MachOWriter Writer(*O, In.is64Bit(), In.isLittleEndian(), Out); + // TODO: Support 16KB pages which are employed in iOS arm64 binaries: + // https://github.com/llvm/llvm-project/commit/1bebb2832ee312d3b0316dacff457a7a29435edb + const uint64_t PageSize = 4096; + + MachOWriter Writer(*O, In.is64Bit(), In.isLittleEndian(), PageSize, Out); if (auto E = Writer.finalize()) return E; return Writer.write(); diff --git a/tools/llvm-objcopy/MachO/MachOReader.cpp b/tools/llvm-objcopy/MachO/MachOReader.cpp index d31293034608..b48a0d8952d0 100644 --- a/tools/llvm-objcopy/MachO/MachOReader.cpp +++ b/tools/llvm-objcopy/MachO/MachOReader.cpp @@ -129,10 +129,19 @@ void MachOReader::readLoadCommands(Object &O) const { case MachO::LC_SYMTAB: O.SymTabCommandIndex = O.LoadCommands.size(); break; + case MachO::LC_DYSYMTAB: + O.DySymTabCommandIndex = O.LoadCommands.size(); + break; case MachO::LC_DYLD_INFO: case MachO::LC_DYLD_INFO_ONLY: O.DyLdInfoCommandIndex = O.LoadCommands.size(); break; + case MachO::LC_DATA_IN_CODE: + O.DataInCodeCommandIndex = O.LoadCommands.size(); + break; + case MachO::LC_FUNCTION_STARTS: + O.FunctionStartsCommandIndex = O.LoadCommands.size(); + break; } #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ case MachO::LCName: \ @@ -188,7 +197,7 @@ void MachOReader::readSymbolTable(Object &O) const { StrTable, MachOObj.getSymbolTableEntry(Symbol.getRawDataRefImpl()))); - O.SymTable.Symbols.push_back(llvm::make_unique<SymbolEntry>(SE)); + O.SymTable.Symbols.push_back(std::make_unique<SymbolEntry>(SE)); } } @@ -222,8 +231,37 @@ void MachOReader::readExportInfo(Object &O) const { O.Exports.Trie = MachOObj.getDyldInfoExportsTrie(); } +void MachOReader::readDataInCodeData(Object &O) const { + if (!O.DataInCodeCommandIndex) + return; + const MachO::linkedit_data_command &LDC = + O.LoadCommands[*O.DataInCodeCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + O.DataInCode.Data = arrayRefFromStringRef( + MachOObj.getData().substr(LDC.dataoff, LDC.datasize)); +} + +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; + + O.FunctionStarts.Data = arrayRefFromStringRef( + MachOObj.getData().substr(LDC.dataoff, LDC.datasize)); +} + +void MachOReader::readIndirectSymbolTable(Object &O) const { + MachO::dysymtab_command DySymTab = MachOObj.getDysymtabLoadCommand(); + for (uint32_t i = 0; i < DySymTab.nindirectsyms; ++i) + O.IndirectSymTable.Symbols.push_back( + MachOObj.getIndirectSymbolTableEntry(DySymTab, i)); +} + std::unique_ptr<Object> MachOReader::create() const { - auto Obj = llvm::make_unique<Object>(); + auto Obj = std::make_unique<Object>(); readHeader(*Obj); readLoadCommands(*Obj); readSymbolTable(*Obj); @@ -233,6 +271,9 @@ std::unique_ptr<Object> MachOReader::create() const { readWeakBindInfo(*Obj); readLazyBindInfo(*Obj); readExportInfo(*Obj); + readDataInCodeData(*Obj); + readFunctionStartsData(*Obj); + readIndirectSymbolTable(*Obj); return Obj; } diff --git a/tools/llvm-objcopy/MachO/MachOReader.h b/tools/llvm-objcopy/MachO/MachOReader.h index 795e5cc2363d..00c8f0d55f61 100644 --- a/tools/llvm-objcopy/MachO/MachOReader.h +++ b/tools/llvm-objcopy/MachO/MachOReader.h @@ -36,6 +36,9 @@ class MachOReader : public Reader { void readWeakBindInfo(Object &O) const; void readLazyBindInfo(Object &O) const; void readExportInfo(Object &O) const; + void readDataInCodeData(Object &O) const; + void readFunctionStartsData(Object &O) const; + void readIndirectSymbolTable(Object &O) const; public: explicit MachOReader(const object::MachOObjectFile &Obj) : MachOObj(Obj) {} diff --git a/tools/llvm-objcopy/MachO/MachOWriter.cpp b/tools/llvm-objcopy/MachO/MachOWriter.cpp index 74200c5aa62a..4ec91cc9eb7a 100644 --- a/tools/llvm-objcopy/MachO/MachOWriter.cpp +++ b/tools/llvm-objcopy/MachO/MachOWriter.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "MachOWriter.h" +#include "MachOLayoutBuilder.h" #include "Object.h" #include "llvm/ADT/STLExtras.h" #include "llvm/BinaryFormat/MachO.h" @@ -40,16 +41,10 @@ size_t MachOWriter::totalSize() const { const MachO::symtab_command &SymTabCommand = O.LoadCommands[*O.SymTabCommandIndex] .MachOLoadCommand.symtab_command_data; - if (SymTabCommand.symoff) { - assert((SymTabCommand.nsyms == O.SymTable.Symbols.size()) && - "Incorrect number of symbols"); + if (SymTabCommand.symoff) Ends.push_back(SymTabCommand.symoff + symTableSize()); - } - if (SymTabCommand.stroff) { - assert((SymTabCommand.strsize == StrTableBuilder.getSize()) && - "Incorrect string table size"); + if (SymTabCommand.stroff) Ends.push_back(SymTabCommand.stroff + SymTabCommand.strsize); - } } if (O.DyLdInfoCommandIndex) { const MachO::dyld_info_command &DyLdInfoCommand = @@ -84,6 +79,36 @@ size_t MachOWriter::totalSize() const { } } + if (O.DySymTabCommandIndex) { + const MachO::dysymtab_command &DySymTabCommand = + O.LoadCommands[*O.DySymTabCommandIndex] + .MachOLoadCommand.dysymtab_command_data; + + if (DySymTabCommand.indirectsymoff) + Ends.push_back(DySymTabCommand.indirectsymoff + + sizeof(uint32_t) * O.IndirectSymTable.Symbols.size()); + } + + if (O.DataInCodeCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.DataInCodeCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Ends.push_back(LinkEditDataCommand.dataoff + + LinkEditDataCommand.datasize); + } + + if (O.FunctionStartsCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.FunctionStartsCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + if (LinkEditDataCommand.dataoff) + Ends.push_back(LinkEditDataCommand.dataoff + + LinkEditDataCommand.datasize); + } + // Otherwise, use the last section / reloction. for (const auto &LC : O.LoadCommands) for (const auto &S : LC.Sections) { @@ -120,14 +145,6 @@ void MachOWriter::writeHeader() { memcpy(B.getBufferStart(), &Header, HeaderSize); } -void MachOWriter::updateSymbolIndexes() { - uint32_t Index = 0; - for (auto &Symbol : O.SymTable.Symbols) { - Symbol->Index = Index; - Index++; - } -} - void MachOWriter::writeLoadCommands() { uint8_t *Begin = B.getBufferStart() + headerSize(); for (const auto &LC : O.LoadCommands) { @@ -253,7 +270,7 @@ void writeNListEntry(const SymbolEntry &SE, bool IsLittleEndian, char *&Out, Out += sizeof(NListType); } -void MachOWriter::writeSymbolTable() { +void MachOWriter::writeStringTable() { if (!O.SymTabCommandIndex) return; const MachO::symtab_command &SymTabCommand = @@ -261,10 +278,10 @@ void MachOWriter::writeSymbolTable() { .MachOLoadCommand.symtab_command_data; uint8_t *StrTable = (uint8_t *)B.getBufferStart() + SymTabCommand.stroff; - StrTableBuilder.write(StrTable); + LayoutBuilder.getStringTableBuilder().write(StrTable); } -void MachOWriter::writeStringTable() { +void MachOWriter::writeSymbolTable() { if (!O.SymTabCommandIndex) return; const MachO::symtab_command &SymTabCommand = @@ -275,7 +292,7 @@ void MachOWriter::writeStringTable() { for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end(); Iter != End; Iter++) { SymbolEntry *Sym = Iter->get(); - auto Nstrx = StrTableBuilder.getOffset(Sym->Name); + uint32_t Nstrx = LayoutBuilder.getStringTableBuilder().getOffset(Sym->Name); if (Is64Bit) writeNListEntry<MachO::nlist_64>(*Sym, IsLittleEndian, SymTable, Nstrx); @@ -344,6 +361,45 @@ void MachOWriter::writeExportInfo() { memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size()); } +void MachOWriter::writeIndirectSymbolTable() { + if (!O.DySymTabCommandIndex) + return; + + const MachO::dysymtab_command &DySymTabCommand = + O.LoadCommands[*O.DySymTabCommandIndex] + .MachOLoadCommand.dysymtab_command_data; + + char *Out = (char *)B.getBufferStart() + DySymTabCommand.indirectsymoff; + assert((DySymTabCommand.nindirectsyms == O.IndirectSymTable.Symbols.size()) && + "Incorrect indirect symbol table size"); + memcpy(Out, O.IndirectSymTable.Symbols.data(), + sizeof(uint32_t) * O.IndirectSymTable.Symbols.size()); +} + +void MachOWriter::writeDataInCodeData() { + if (!O.DataInCodeCommandIndex) + return; + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.DataInCodeCommandIndex] + .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()); +} + +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()); +} + void MachOWriter::writeTail() { typedef void (MachOWriter::*WriteHandlerType)(void); typedef std::pair<uint64_t, WriteHandlerType> WriteOperation; @@ -379,206 +435,51 @@ void MachOWriter::writeTail() { {DyLdInfoCommand.export_off, &MachOWriter::writeExportInfo}); } - llvm::sort(Queue, [](const WriteOperation &LHS, const WriteOperation &RHS) { - return LHS.first < RHS.first; - }); - - for (auto WriteOp : Queue) - (this->*WriteOp.second)(); -} - -void MachOWriter::updateSizeOfCmds() { - auto Size = 0; - for (const auto &LC : O.LoadCommands) { - auto &MLC = LC.MachOLoadCommand; - auto cmd = MLC.load_command_data.cmd; - - switch (cmd) { - case MachO::LC_SEGMENT: - Size += sizeof(MachO::segment_command) + - sizeof(MachO::section) * LC.Sections.size(); - continue; - case MachO::LC_SEGMENT_64: - Size += sizeof(MachO::segment_command_64) + - sizeof(MachO::section_64) * LC.Sections.size(); - continue; - } - - switch (cmd) { -#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ - case MachO::LCName: \ - Size += sizeof(MachO::LCStruct); \ - break; -#include "llvm/BinaryFormat/MachO.def" -#undef HANDLE_LOAD_COMMAND - } - } - - O.Header.SizeOfCmds = Size; -} - -// Updates the index and the number of local/external/undefined symbols. Here we -// assume that MLC is a LC_DYSYMTAB and the nlist entries in the symbol table -// are already sorted by the those types. -void MachOWriter::updateDySymTab(MachO::macho_load_command &MLC) { - uint32_t NumLocalSymbols = 0; - auto Iter = O.SymTable.Symbols.begin(); - auto End = O.SymTable.Symbols.end(); - for (; Iter != End; Iter++) { - if ((*Iter)->n_type & (MachO::N_EXT | MachO::N_PEXT)) - break; + if (O.DySymTabCommandIndex) { + const MachO::dysymtab_command &DySymTabCommand = + O.LoadCommands[*O.DySymTabCommandIndex] + .MachOLoadCommand.dysymtab_command_data; - NumLocalSymbols++; + if (DySymTabCommand.indirectsymoff) + Queue.emplace_back(DySymTabCommand.indirectsymoff, + &MachOWriter::writeIndirectSymbolTable); } - uint32_t NumExtDefSymbols = 0; - for (; Iter != End; Iter++) { - if (((*Iter)->n_type & MachO::N_TYPE) == MachO::N_UNDF) - break; - - NumExtDefSymbols++; - } - - MLC.dysymtab_command_data.ilocalsym = 0; - MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols; - MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols; - MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols; - MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols; - MLC.dysymtab_command_data.nundefsym = - O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols); -} - -// Recomputes and updates offset and size fields in load commands and sections -// since they could be modified. -Error MachOWriter::layout() { - auto SizeOfCmds = loadCommandsSize(); - auto Offset = headerSize() + SizeOfCmds; - O.Header.NCmds = O.LoadCommands.size(); - O.Header.SizeOfCmds = SizeOfCmds; - - // Lay out sections. - for (auto &LC : O.LoadCommands) { - uint64_t FileOff = Offset; - uint64_t VMSize = 0; - uint64_t FileOffsetInSegment = 0; - for (auto &Sec : LC.Sections) { - if (!Sec.isVirtualSection()) { - auto FilePaddingSize = - OffsetToAlignment(FileOffsetInSegment, 1ull << Sec.Align); - Sec.Offset = Offset + FileOffsetInSegment + FilePaddingSize; - Sec.Size = Sec.Content.size(); - FileOffsetInSegment += FilePaddingSize + Sec.Size; - } - - VMSize = std::max(VMSize, Sec.Addr + Sec.Size); - } - - // TODO: Handle the __PAGEZERO segment. - auto &MLC = LC.MachOLoadCommand; - switch (MLC.load_command_data.cmd) { - case MachO::LC_SEGMENT: - MLC.segment_command_data.cmdsize = - sizeof(MachO::segment_command) + - sizeof(MachO::section) * LC.Sections.size(); - MLC.segment_command_data.nsects = LC.Sections.size(); - MLC.segment_command_data.fileoff = FileOff; - MLC.segment_command_data.vmsize = VMSize; - MLC.segment_command_data.filesize = FileOffsetInSegment; - break; - case MachO::LC_SEGMENT_64: - MLC.segment_command_64_data.cmdsize = - sizeof(MachO::segment_command_64) + - sizeof(MachO::section_64) * LC.Sections.size(); - MLC.segment_command_64_data.nsects = LC.Sections.size(); - MLC.segment_command_64_data.fileoff = FileOff; - MLC.segment_command_64_data.vmsize = VMSize; - MLC.segment_command_64_data.filesize = FileOffsetInSegment; - break; - } + if (O.DataInCodeCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.DataInCodeCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; - Offset += FileOffsetInSegment; + if (LinkEditDataCommand.dataoff) + Queue.emplace_back(LinkEditDataCommand.dataoff, + &MachOWriter::writeDataInCodeData); } - // Lay out relocations. - for (auto &LC : O.LoadCommands) - for (auto &Sec : LC.Sections) { - Sec.RelOff = Sec.Relocations.empty() ? 0 : Offset; - Sec.NReloc = Sec.Relocations.size(); - Offset += sizeof(MachO::any_relocation_info) * Sec.NReloc; - } + if (O.FunctionStartsCommandIndex) { + const MachO::linkedit_data_command &LinkEditDataCommand = + O.LoadCommands[*O.FunctionStartsCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; - // Lay out tail stuff. - auto NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); - for (auto &LC : O.LoadCommands) { - auto &MLC = LC.MachOLoadCommand; - auto cmd = MLC.load_command_data.cmd; - switch (cmd) { - case MachO::LC_SYMTAB: - MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size(); - MLC.symtab_command_data.strsize = StrTableBuilder.getSize(); - MLC.symtab_command_data.symoff = Offset; - Offset += NListSize * MLC.symtab_command_data.nsyms; - MLC.symtab_command_data.stroff = Offset; - Offset += MLC.symtab_command_data.strsize; - break; - case MachO::LC_DYSYMTAB: { - if (MLC.dysymtab_command_data.ntoc != 0 || - MLC.dysymtab_command_data.nmodtab != 0 || - MLC.dysymtab_command_data.nextrefsyms != 0 || - MLC.dysymtab_command_data.nlocrel != 0 || - MLC.dysymtab_command_data.nextrel != 0) - return createStringError(llvm::errc::not_supported, - "shared library is not yet supported"); - - if (MLC.dysymtab_command_data.nindirectsyms != 0) - return createStringError(llvm::errc::not_supported, - "indirect symbol table is not yet supported"); - - updateDySymTab(MLC); - break; - } - case MachO::LC_SEGMENT: - case MachO::LC_SEGMENT_64: - case MachO::LC_VERSION_MIN_MACOSX: - case MachO::LC_BUILD_VERSION: - case MachO::LC_ID_DYLIB: - case MachO::LC_LOAD_DYLIB: - case MachO::LC_UUID: - case MachO::LC_SOURCE_VERSION: - // Nothing to update. - break; - default: - // Abort if it's unsupported in order to prevent corrupting the object. - return createStringError(llvm::errc::not_supported, - "unsupported load command (cmd=0x%x)", cmd); - } + if (LinkEditDataCommand.dataoff) + Queue.emplace_back(LinkEditDataCommand.dataoff, + &MachOWriter::writeFunctionStartsData); } - return Error::success(); -} + llvm::sort(Queue, [](const WriteOperation &LHS, const WriteOperation &RHS) { + return LHS.first < RHS.first; + }); -void MachOWriter::constructStringTable() { - for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols) - StrTableBuilder.add(Sym->Name); - StrTableBuilder.finalize(); + for (auto WriteOp : Queue) + (this->*WriteOp.second)(); } -Error MachOWriter::finalize() { - updateSizeOfCmds(); - constructStringTable(); - - if (auto E = layout()) - return E; - - return Error::success(); -} +Error MachOWriter::finalize() { return LayoutBuilder.layout(); } Error MachOWriter::write() { if (Error E = B.allocate(totalSize())) return E; memset(B.getBufferStart(), 0, totalSize()); writeHeader(); - updateSymbolIndexes(); writeLoadCommands(); writeSections(); writeTail(); diff --git a/tools/llvm-objcopy/MachO/MachOWriter.h b/tools/llvm-objcopy/MachO/MachOWriter.h index ecf12d62de2c..22abbad56f41 100644 --- a/tools/llvm-objcopy/MachO/MachOWriter.h +++ b/tools/llvm-objcopy/MachO/MachOWriter.h @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "../Buffer.h" +#include "MachOLayoutBuilder.h" #include "MachOObjcopy.h" #include "Object.h" #include "llvm/BinaryFormat/MachO.h" @@ -22,20 +23,15 @@ class MachOWriter { Object &O; bool Is64Bit; bool IsLittleEndian; + uint64_t PageSize; Buffer &B; - StringTableBuilder StrTableBuilder{StringTableBuilder::MachO}; + MachOLayoutBuilder LayoutBuilder; size_t headerSize() const; size_t loadCommandsSize() const; size_t symTableSize() const; size_t strTableSize() const; - void updateDySymTab(MachO::macho_load_command &MLC); - void updateSizeOfCmds(); - void updateSymbolIndexes(); - void constructStringTable(); - Error layout(); - void writeHeader(); void writeLoadCommands(); template <typename StructType> @@ -48,11 +44,16 @@ class MachOWriter { void writeWeakBindInfo(); void writeLazyBindInfo(); void writeExportInfo(); + void writeIndirectSymbolTable(); + void writeDataInCodeData(); + void writeFunctionStartsData(); void writeTail(); public: - MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, Buffer &B) - : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), B(B) {} + MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, uint64_t PageSize, + Buffer &B) + : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), + PageSize(PageSize), B(B), LayoutBuilder(O, Is64Bit, PageSize) {} size_t totalSize() const; Error finalize(); diff --git a/tools/llvm-objcopy/MachO/Object.h b/tools/llvm-objcopy/MachO/Object.h index ed85fcbc47f7..1cebf8253d19 100644 --- a/tools/llvm-objcopy/MachO/Object.h +++ b/tools/llvm-objcopy/MachO/Object.h @@ -90,6 +90,16 @@ struct SymbolEntry { uint8_t n_sect; uint16_t n_desc; uint64_t n_value; + + bool isExternalSymbol() const { + return n_type & ((MachO::N_EXT | MachO::N_PEXT)); + } + + bool isLocalSymbol() const { return !isExternalSymbol(); } + + bool isUndefinedSymbol() const { + return (n_type & MachO::N_TYPE) == MachO::N_UNDF; + } }; /// The location of the symbol table inside the binary is described by LC_SYMTAB @@ -100,6 +110,10 @@ struct SymbolTable { const SymbolEntry *getSymbolByIndex(uint32_t Index) const; }; +struct IndirectSymbolTable { + std::vector<uint32_t> Symbols; +}; + /// The location of the string table inside the binary is described by LC_SYMTAB /// load command. struct StringTable { @@ -206,6 +220,10 @@ struct ExportInfo { ArrayRef<uint8_t> Trie; }; +struct LinkData { + ArrayRef<uint8_t> Data; +}; + struct Object { MachHeader Header; std::vector<LoadCommand> LoadCommands; @@ -218,11 +236,20 @@ struct Object { WeakBindInfo WeakBinds; LazyBindInfo LazyBinds; ExportInfo Exports; + IndirectSymbolTable IndirectSymTable; + LinkData DataInCode; + LinkData FunctionStarts; /// The index of LC_SYMTAB load command if present. Optional<size_t> SymTabCommandIndex; /// The index of LC_DYLD_INFO or LC_DYLD_INFO_ONLY load command if present. Optional<size_t> DyLdInfoCommandIndex; + /// The index LC_DYSYMTAB load comamnd if present. + Optional<size_t> DySymTabCommandIndex; + /// The index LC_DATA_IN_CODE load comamnd if present. + Optional<size_t> DataInCodeCommandIndex; + /// The index LC_FUNCTION_STARTS load comamnd if present. + Optional<size_t> FunctionStartsCommandIndex; }; } // end namespace macho diff --git a/tools/llvm-objcopy/ObjcopyOpts.td b/tools/llvm-objcopy/ObjcopyOpts.td index 5fce4fbde539..9e6b6f0005cd 100644 --- a/tools/llvm-objcopy/ObjcopyOpts.td +++ b/tools/llvm-objcopy/ObjcopyOpts.td @@ -1,37 +1,33 @@ -include "llvm/Option/OptParser.td" - -multiclass Eq<string name, string help> { - def NAME : Separate<["--"], name>; - def NAME #_eq : Joined<["--"], name #"=">, - Alias<!cast<Separate>(NAME)>, - HelpText<help>; -} - -def help : Flag<["--"], "help">; -def h : Flag<["-"], "h">, Alias<help>; - -def allow_broken_links - : Flag<["--"], "allow-broken-links">, - HelpText<"Allow llvm-objcopy to remove sections even if it would leave " - "invalid section references. The appropriate sh_link fields " - "will be set to zero.">; +include "CommonOpts.td" defm binary_architecture - : Eq<"binary-architecture", "Used when transforming an architecture-less " - "format (such as binary) to another format">; -def B : JoinedOrSeparate<["-"], "B">, Alias<binary_architecture>; + : Eq<"binary-architecture", "Ignored for compatibility">; +def B : JoinedOrSeparate<["-"], "B">, + Alias<binary_architecture>, + HelpText<"Alias for --binary-architecture">; defm target : Eq<"target", "Format of the input and output file">, Values<"binary">; -def F : JoinedOrSeparate<["-"], "F">, Alias<target>; +def F : JoinedOrSeparate<["-"], "F">, + Alias<target>, + HelpText<"Alias for --target">; defm input_target : Eq<"input-target", "Format of the input file">, Values<"binary">; -def I : JoinedOrSeparate<["-"], "I">, Alias<input_target>; +def I : JoinedOrSeparate<["-"], "I">, + Alias<input_target>, + HelpText<"Alias for --input-target">; defm output_target : Eq<"output-target", "Format of the output file">, Values<"binary">; -def O : JoinedOrSeparate<["-"], "O">, Alias<output_target>; +def O : JoinedOrSeparate<["-"], "O">, + Alias<output_target>, + HelpText<"Alias for --output-target">; + +defm new_symbol_visibility : Eq<"new-symbol-visibility", "Visibility of " + "symbols generated for binary input or added" + " with --add-symbol unless otherwise" + " specified. The default value is 'default'.">; def compress_debug_sections : Flag<["--"], "compress-debug-sections">; def compress_debug_sections_eq @@ -46,34 +42,10 @@ defm split_dwo "<dwo-file>, then strip-dwo on the input file">, MetaVarName<"dwo-file">; -def enable_deterministic_archives - : Flag<["--"], "enable-deterministic-archives">, - HelpText<"Enable deterministic mode when copying archives (use zero for " - "UIDs, GIDs, and timestamps).">; -def D : Flag<["-"], "D">, - Alias<enable_deterministic_archives>, - HelpText<"Alias for --enable-deterministic-archives">; - -def disable_deterministic_archives - : Flag<["--"], "disable-deterministic-archives">, - HelpText<"Disable deterministic mode when copying archives (use real " - "values for UIDs, GIDs, and timestamps).">; -def U : Flag<["-"], "U">, - Alias<disable_deterministic_archives>, - HelpText<"Alias for --disable-deterministic-archives">; - -def preserve_dates : Flag<["--"], "preserve-dates">, - HelpText<"Preserve access and modification timestamps">; -def p : Flag<["-"], "p">, Alias<preserve_dates>; - defm add_gnu_debuglink : Eq<"add-gnu-debuglink", "Add a .gnu_debuglink for <debug-file>">, MetaVarName<"debug-file">; -defm remove_section : Eq<"remove-section", "Remove <section>">, - MetaVarName<"section">; -def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>; - defm rename_section : Eq<"rename-section", "Renames a section from old to new, optionally with specified flags. " @@ -93,16 +65,20 @@ defm redefine_symbols "symbols from many files.">, MetaVarName<"filename">; -defm keep_section : Eq<"keep-section", "Keep <section>">, - MetaVarName<"section">; defm only_section : Eq<"only-section", "Remove all but <section>">, MetaVarName<"section">; -def j : JoinedOrSeparate<["-"], "j">, Alias<only_section>; +def j : JoinedOrSeparate<["-"], "j">, + Alias<only_section>, + HelpText<"Alias for --only-section">; defm add_section : Eq<"add-section", "Make a section named <section> with the contents of <file>.">, MetaVarName<"section=file">; +defm set_section_alignment + : Eq<"set-section-alignment", "Set alignment for a given section.">, + MetaVarName<"section=align">; + defm set_section_flags : Eq<"set-section-flags", "Set section flags for a given section. Flags supported for GNU " @@ -110,26 +86,14 @@ defm set_section_flags "rom, share, contents, merge, strings.">, MetaVarName<"section=flag1[,flag2,...]">; -def strip_all : Flag<["--"], "strip-all">, - HelpText<"Remove non-allocated sections outside segments. " - ".gnu.warning* sections are not removed">; -def S : Flag<["-"], "S">, Alias<strip_all>; -def strip_all_gnu : Flag<["--"], "strip-all-gnu">, - HelpText<"Compatible with GNU objcopy's --strip-all">; -def strip_debug : Flag<["--"], "strip-debug">, - HelpText<"Remove all debug information">; -def g : Flag<["-"], "g">, Alias<strip_debug>, - HelpText<"Alias for --strip-debug">; +def S : Flag<["-"], "S">, + Alias<strip_all>, + HelpText<"Alias for --strip-all">; def strip_dwo : Flag<["--"], "strip-dwo">, HelpText<"Remove all DWARF .dwo sections from file">; -def strip_sections - : Flag<["--"], "strip-sections">, - HelpText<"Remove all section headers and all sections not in segments">; def strip_non_alloc : Flag<["--"], "strip-non-alloc">, HelpText<"Remove all non-allocated sections outside segments">; -def strip_unneeded : Flag<["--"], "strip-unneeded">, - HelpText<"Remove all symbols not needed by relocations">; defm strip_unneeded_symbol : Eq<"strip-unneeded-symbol", "Remove symbol <symbol> if it is not needed by relocations">, @@ -163,7 +127,9 @@ defm localize_symbols "Reads a list of symbols from <filename> and marks them local.">, MetaVarName<"filename">; -def L : JoinedOrSeparate<["-"], "L">, Alias<localize_symbol>; +def L : JoinedOrSeparate<["-"], "L">, + Alias<localize_symbol>, + HelpText<"Alias for --localize-symbol">; defm globalize_symbol : Eq<"globalize-symbol", "Mark <symbol> as global">, MetaVarName<"symbol">; @@ -178,7 +144,9 @@ defm keep_global_symbol "Convert all symbols except <symbol> to local. May be repeated to " "convert all except a set of symbols to local.">, MetaVarName<"symbol">; -def G : JoinedOrSeparate<["-"], "G">, Alias<keep_global_symbol>; +def G : JoinedOrSeparate<["-"], "G">, + Alias<keep_global_symbol>, + HelpText<"Alias for --keep-global-symbol">; defm keep_global_symbols : Eq<"keep-global-symbols", @@ -196,31 +164,17 @@ defm weaken_symbols "Reads a list of symbols from <filename> and marks them weak.">, MetaVarName<"filename">; -def W : JoinedOrSeparate<["-"], "W">, Alias<weaken_symbol>; +def W : JoinedOrSeparate<["-"], "W">, + Alias<weaken_symbol>, + HelpText<"Alias for --weaken-symbol">; def weaken : Flag<["--"], "weaken">, HelpText<"Mark all global symbols as weak">; -def discard_locals : Flag<["--"], "discard-locals">, - HelpText<"Remove compiler-generated local symbols, (e.g. " - "symbols starting with .L)">; -def X : Flag<["-"], "X">, Alias<discard_locals>; - -def discard_all - : Flag<["--"], "discard-all">, - HelpText<"Remove all local symbols except file and section symbols">; -def x : Flag<["-"], "x">, Alias<discard_all>; -defm strip_symbol : Eq<"strip-symbol", "Remove symbol <symbol>">, - MetaVarName<"symbol">; defm strip_symbols : Eq<"strip-symbols", "Reads a list of symbols from <filename> and removes them.">, MetaVarName<"filename">; -def N : JoinedOrSeparate<["-"], "N">, Alias<strip_symbol>; -defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, - MetaVarName<"symbol">; -def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>; - defm keep_symbols : Eq<"keep-symbols", "Reads a list of symbols from <filename> and runs as if " @@ -230,13 +184,6 @@ defm keep_symbols "be repeated to read symbols from many files.">, MetaVarName<"filename">; -def only_keep_debug - : Flag<["--"], "only-keep-debug">, - HelpText<"Clear sections that would not be stripped by --strip-debug. " - "Currently only implemented for COFF.">; - -def keep_file_symbols : Flag<["--"], "keep-file-symbols">, - HelpText<"Do not remove file symbols">; defm dump_section : Eq<"dump-section", "Dump contents of section named <section> into file <file>">, @@ -249,9 +196,6 @@ defm prefix_alloc_sections : Eq<"prefix-alloc-sections", "Add <prefix> to the start of every allocated section name">, MetaVarName<"prefix">; -def version : Flag<["--"], "version">, - HelpText<"Print the version and exit.">; -def V : Flag<["-"], "V">, Alias<version>; defm build_id_link_dir : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and " "--build-id-link-output to <dir>">, @@ -265,10 +209,6 @@ defm build_id_link_output "name derived from hex build ID">, MetaVarName<"suffix">; -def regex - : Flag<["--"], "regex">, - HelpText<"Permit regular expressions in name comparison">; - defm set_start : Eq<"set-start", "Set the start address to <addr>. Overrides " "any previous --change-start or --adjust-start values.">, MetaVarName<"addr">; @@ -277,11 +217,12 @@ defm change_start : Eq<"change-start", "Add <incr> to the start address. Can be "cumulatively.">, MetaVarName<"incr">; def adjust_start : JoinedOrSeparate<["--"], "adjust-start">, - Alias<change_start>; + Alias<change_start>, + HelpText<"Alias for --change-start">; defm add_symbol : Eq<"add-symbol", "Add new symbol <name> to .symtab. Accepted flags: " - "global, local, weak, default, hidden, file, section, object, " + "global, local, weak, default, hidden, protected, file, section, object, " "function, indirect-function. Accepted but ignored for " "compatibility: debug, constructor, warning, indirect, synthetic, " "unique-object, before.">, diff --git a/tools/llvm-objcopy/StripOpts.td b/tools/llvm-objcopy/StripOpts.td index 1d06bb3dfb38..cd02cffae673 100644 --- a/tools/llvm-objcopy/StripOpts.td +++ b/tools/llvm-objcopy/StripOpts.td @@ -1,96 +1,17 @@ -include "llvm/Option/OptParser.td" +include "CommonOpts.td" -multiclass Eq<string name, string help> { - def NAME : Separate<["--"], name>; - def NAME #_eq : Joined<["--"], name #"=">, - Alias<!cast<Separate>(NAME)>, - HelpText<help>; -} +def output : JoinedOrSeparate<["-"], "o">, HelpText<"Write output to <file>">, + MetaVarName<"<file>">; -def help : Flag<["--"], "help">; -def h : Flag<["-"], "h">, Alias<help>; - -def allow_broken_links - : Flag<["--"], "allow-broken-links">, - HelpText<"Allow llvm-strip to remove sections even if it would leave " - "invalid section references. The appropriate sh_link fields " - "will be set to zero.">; - -def enable_deterministic_archives - : Flag<["--"], "enable-deterministic-archives">, - HelpText<"Enable deterministic mode when stripping archives (use zero " - "for UIDs, GIDs, and timestamps).">; -def D : Flag<["-"], "D">, - Alias<enable_deterministic_archives>, - HelpText<"Alias for --enable-deterministic-archives">; - -def disable_deterministic_archives - : Flag<["--"], "disable-deterministic-archives">, - HelpText<"Disable deterministic mode when stripping archives (use real " - "values for UIDs, GIDs, and timestamps).">; -def U : Flag<["-"], "U">, - Alias<disable_deterministic_archives>, - HelpText<"Alias for --disable-deterministic-archives">; - -def output : JoinedOrSeparate<["-"], "o">, HelpText<"Write output to <file>">; - -def preserve_dates : Flag<["--"], "preserve-dates">, - HelpText<"Preserve access and modification timestamps">; -def p : Flag<["-"], "p">, Alias<preserve_dates>; - -def strip_all : Flag<["--"], "strip-all">, - HelpText<"Remove non-allocated sections outside segments. " - ".gnu.warning* sections are not removed">; -def s : Flag<["-"], "s">, Alias<strip_all>; +def s : Flag<["-"], "s">, + Alias<strip_all>, + HelpText<"Alias for --strip-all">; def no_strip_all : Flag<["--"], "no-strip-all">, HelpText<"Disable --strip-all">; -def strip_all_gnu : Flag<["--"], "strip-all-gnu">, - HelpText<"Compatible with GNU strip's --strip-all">; -def strip_debug : Flag<["--"], "strip-debug">, - HelpText<"Remove debugging symbols only">; -def d : Flag<["-"], "d">, Alias<strip_debug>; -def g : Flag<["-"], "g">, Alias<strip_debug>; -def S : Flag<["-"], "S">, Alias<strip_debug>; -def strip_unneeded : Flag<["--"], "strip-unneeded">, - HelpText<"Remove all symbols not needed by relocations">; - -defm remove_section : Eq<"remove-section", "Remove <section>">, - MetaVarName<"section">; -def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>; - -defm strip_symbol : Eq<"strip-symbol", "Strip <symbol>">, - MetaVarName<"symbol">; -def N : JoinedOrSeparate<["-"], "N">, Alias<strip_symbol>; - -defm keep_section : Eq<"keep-section", "Keep <section>">, - MetaVarName<"section">; -defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, - MetaVarName<"symbol">; -def keep_file_symbols : Flag<["--"], "keep-file-symbols">, - HelpText<"Do not remove file symbols">; - -def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>; - -def only_keep_debug - : Flag<["--"], "only-keep-debug">, - HelpText<"Clear sections that would not be stripped by --strip-debug. " - "Currently only implemented for COFF.">; - -def discard_locals : Flag<["--"], "discard-locals">, - HelpText<"Remove compiler-generated local symbols, (e.g. " - "symbols starting with .L)">; -def X : Flag<["-"], "X">, Alias<discard_locals>; - -def discard_all - : Flag<["--"], "discard-all">, - HelpText<"Remove all local symbols except file and section symbols">; -def x : Flag<["-"], "x">, Alias<discard_all>; - -def regex - : Flag<["--"], "regex">, - HelpText<"Permit regular expressions in name comparison">; - -def version : Flag<["--"], "version">, - HelpText<"Print the version and exit.">; -def V : Flag<["-"], "V">, Alias<version>; +def d : Flag<["-"], "d">, + Alias<strip_debug>, + HelpText<"Alias for --strip-debug">; +def S : Flag<["-"], "S">, + Alias<strip_debug>, + HelpText<"Alias for --strip-debug">; diff --git a/tools/llvm-objcopy/llvm-objcopy.cpp b/tools/llvm-objcopy/llvm-objcopy.cpp index e9372176e43b..a68210f3fdd3 100644 --- a/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/tools/llvm-objcopy/llvm-objcopy.cpp @@ -29,6 +29,7 @@ #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" @@ -36,6 +37,7 @@ #include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/StringSaver.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -84,7 +86,7 @@ LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { ErrorSuccess reportWarning(Error E) { assert(E); - WithColor::warning(errs(), ToolName) << toString(std::move(E)); + WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n'; return Error::success(); } @@ -130,16 +132,18 @@ static Error deepWriteArchive(StringRef ArcName, /// The function executeObjcopyOnIHex does the dispatch based on the format /// of the output specified by the command line options. -static Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, +static Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In, Buffer &Out) { // TODO: support output formats other than ELF. + if (Error E = Config.parseELFConfig()) + return E; return elf::executeObjcopyOnIHex(Config, In, Out); } /// The function executeObjcopyOnRawBinary does the dispatch based on the format /// of the output specified by the command line options. -static Error executeObjcopyOnRawBinary(const CopyConfig &Config, - MemoryBuffer &In, Buffer &Out) { +static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { switch (Config.OutputFormat) { case FileFormat::ELF: // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the @@ -148,6 +152,8 @@ static Error executeObjcopyOnRawBinary(const CopyConfig &Config, case FileFormat::Binary: case FileFormat::IHex: case FileFormat::Unspecified: + if (Error E = Config.parseELFConfig()) + return E; return elf::executeObjcopyOnRawBinary(Config, In, Out); } @@ -156,11 +162,13 @@ static Error executeObjcopyOnRawBinary(const CopyConfig &Config, /// The function executeObjcopyOnBinary does the dispatch based on the format /// of the input binary (ELF, MachO or COFF). -static Error executeObjcopyOnBinary(const CopyConfig &Config, - object::Binary &In, Buffer &Out) { - if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) +static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, + Buffer &Out) { + if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) { + if (Error E = Config.parseELFConfig()) + return E; return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); - else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) + } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); @@ -169,8 +177,7 @@ static Error executeObjcopyOnBinary(const CopyConfig &Config, "unsupported object file format"); } -static Error executeObjcopyOnArchive(const CopyConfig &Config, - const Archive &Ar) { +static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) { std::vector<NewArchiveMember> NewArchiveMembers; Error Err = Error::success(); for (const Archive::Child &Child : Ar.children(Err)) { @@ -246,7 +253,7 @@ static Error restoreStatOnFile(StringRef Filename, /// The function executeObjcopy does the higher level dispatch based on the type /// of input (raw binary, archive or single object file) and takes care of the /// format-agnostic modifications, i.e. preserving dates. -static Error executeObjcopy(const CopyConfig &Config) { +static Error executeObjcopy(CopyConfig &Config) { sys::fs::file_status Stat; if (Config.InputFilename != "-") { if (auto EC = sys::fs::status(Config.InputFilename, Stat)) @@ -255,7 +262,7 @@ static Error executeObjcopy(const CopyConfig &Config) { Stat.permissions(static_cast<sys::fs::perms>(0777)); } - typedef Error (*ProcessRawFn)(const CopyConfig &, MemoryBuffer &, Buffer &); + using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &); ProcessRawFn ProcessRaw; switch (Config.InputFormat) { case FileFormat::Binary: @@ -310,15 +317,31 @@ int main(int argc, char **argv) { InitLLVM X(argc, argv); ToolName = argv[0]; bool IsStrip = sys::path::stem(ToolName).contains("strip"); + + // 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 + // here. This is duplicated code. + SmallVector<const char *, 20> NewArgv(argv, argv + argc); + BumpPtrAllocator A; + StringSaver Saver(A); + cl::ExpandResponseFiles(Saver, + Triple(sys::getProcessTriple()).isOSWindows() + ? cl::TokenizeWindowsCommandLine + : cl::TokenizeGNUCommandLine, + NewArgv); + + auto Args = makeArrayRef(NewArgv).drop_front(); + Expected<DriverConfig> DriverConfig = - IsStrip ? parseStripOptions(makeArrayRef(argv + 1, argc), reportWarning) - : parseObjcopyOptions(makeArrayRef(argv + 1, argc)); + IsStrip ? parseStripOptions(Args, reportWarning) + : parseObjcopyOptions(Args, reportWarning); if (!DriverConfig) { logAllUnhandledErrors(DriverConfig.takeError(), WithColor::error(errs(), ToolName)); return 1; } - for (const CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { + for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { if (Error E = executeObjcopy(CopyConfig)) { logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); return 1; diff --git a/tools/llvm-objdump/COFFDump.cpp b/tools/llvm-objdump/COFFDump.cpp index 1ba0a68902c9..60b0f5a3cbd1 100644 --- a/tools/llvm-objdump/COFFDump.cpp +++ b/tools/llvm-objdump/COFFDump.cpp @@ -234,15 +234,14 @@ printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) { if (Count == 0) return; - const pe32_header *PE32Header; - error(Obj->getPE32Header(PE32Header)); - uint32_t ImageBase = PE32Header->ImageBase; uintptr_t IntPtr = 0; - error(Obj->getVaPtr(TableVA, IntPtr)); + if (std::error_code EC = Obj->getVaPtr(TableVA, IntPtr)) + reportError(errorCodeToError(EC), Obj->getFileName()); + const support::ulittle32_t *P = (const support::ulittle32_t *)IntPtr; outs() << "SEH Table:"; for (int I = 0; I < Count; ++I) - outs() << format(" 0x%x", P[I] + ImageBase); + outs() << format(" 0x%x", P[I] + Obj->getPE32Header()->ImageBase); outs() << "\n\n"; } @@ -268,22 +267,24 @@ static void printTLSDirectoryT(const coff_tls_directory<T> *TLSDir) { } static void printTLSDirectory(const COFFObjectFile *Obj) { - const pe32_header *PE32Header; - error(Obj->getPE32Header(PE32Header)); - - const pe32plus_header *PE32PlusHeader; - error(Obj->getPE32PlusHeader(PE32PlusHeader)); + const pe32_header *PE32Header = Obj->getPE32Header(); + const pe32plus_header *PE32PlusHeader = Obj->getPE32PlusHeader(); // Skip if it's not executable. if (!PE32Header && !PE32PlusHeader) return; const data_directory *DataDir; - error(Obj->getDataDirectory(COFF::TLS_TABLE, DataDir)); - uintptr_t IntPtr = 0; + if (std::error_code EC = Obj->getDataDirectory(COFF::TLS_TABLE, DataDir)) + reportError(errorCodeToError(EC), Obj->getFileName()); + if (DataDir->RelativeVirtualAddress == 0) return; - error(Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr)); + + uintptr_t IntPtr = 0; + if (std::error_code EC = + Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr)) + reportError(errorCodeToError(EC), Obj->getFileName()); if (PE32Header) { auto *TLSDir = reinterpret_cast<const coff_tls_directory32 *>(IntPtr); @@ -298,9 +299,7 @@ static void printTLSDirectory(const COFFObjectFile *Obj) { static void printLoadConfiguration(const COFFObjectFile *Obj) { // Skip if it's not executable. - const pe32_header *PE32Header; - error(Obj->getPE32Header(PE32Header)); - if (!PE32Header) + if (!Obj->getPE32Header()) return; // Currently only x86 is supported @@ -308,11 +307,18 @@ static void printLoadConfiguration(const COFFObjectFile *Obj) { return; const data_directory *DataDir; - error(Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE, DataDir)); + + if (std::error_code EC = + Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE, DataDir)) + reportError(errorCodeToError(EC), Obj->getFileName()); + uintptr_t IntPtr = 0; if (DataDir->RelativeVirtualAddress == 0) return; - error(Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr)); + + if (std::error_code EC = + Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr)) + reportError(errorCodeToError(EC), Obj->getFileName()); auto *LoadConf = reinterpret_cast<const coff_load_configuration32 *>(IntPtr); outs() << "Load configuration:" @@ -442,8 +448,7 @@ static bool getPDataSection(const COFFObjectFile *Obj, std::vector<RelocationRef> &Rels, const RuntimeFunction *&RFStart, int &NumRFs) { for (const SectionRef &Section : Obj->sections()) { - StringRef Name; - error(Section.getName(Name)); + StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName()); if (Name != ".pdata") continue; @@ -455,7 +460,9 @@ static bool getPDataSection(const COFFObjectFile *Obj, llvm::sort(Rels, isRelocAddressLess); ArrayRef<uint8_t> Contents; - error(Obj->getSectionContents(Pdata, Contents)); + if (Error E = Obj->getSectionContents(Pdata, Contents)) + reportError(std::move(E), Obj->getFileName()); + if (Contents.empty()) continue; @@ -571,10 +578,12 @@ static void printRuntimeFunctionRels(const COFFObjectFile *Obj, ArrayRef<uint8_t> XContents; uint64_t UnwindInfoOffset = 0; - error(getSectionContents( - Obj, Rels, SectionOffset + - /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8, - XContents, UnwindInfoOffset)); + if (Error E = getSectionContents( + Obj, Rels, + SectionOffset + + /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8, + XContents, UnwindInfoOffset)) + reportError(std::move(E), Obj->getFileName()); if (XContents.empty()) return; @@ -650,9 +659,12 @@ void printCOFFSymbolTable(const object::COFFImportFile *i) { void 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; - error(Symbol.takeError()); - error(coff->getSymbolName(*Symbol, Name)); + if (std::error_code EC = coff->getSymbolName(*Symbol, Name)) + reportError(errorCodeToError(EC), coff->getFileName()); outs() << "[" << format("%2d", SI) << "]" << "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")" @@ -682,7 +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; - error(coff->getAuxSymbol<coff_aux_section_definition>(SI + 1, asd)); + if (std::error_code EC = + coff->getAuxSymbol<coff_aux_section_definition>(SI + 1, asd)) + reportError(errorCodeToError(EC), coff->getFileName()); int32_t AuxNumber = asd->getNumber(Symbol->isBigObj()); @@ -697,7 +711,8 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) { , unsigned(asd->Selection)); } else if (Symbol->isFileRecord()) { const char *FileName; - error(coff->getAuxSymbol<char>(SI + 1, FileName)); + if (std::error_code EC = coff->getAuxSymbol<char>(SI + 1, FileName)) + reportError(errorCodeToError(EC), coff->getFileName()); StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() * coff->getSymbolTableEntrySize()); @@ -707,7 +722,9 @@ void printCOFFSymbolTable(const COFFObjectFile *coff) { break; } else if (Symbol->isWeakExternal()) { const coff_aux_weak_external *awe; - error(coff->getAuxSymbol<coff_aux_weak_external>(SI + 1, awe)); + if (std::error_code EC = + coff->getAuxSymbol<coff_aux_weak_external>(SI + 1, awe)) + reportError(errorCodeToError(EC), coff->getFileName()); outs() << "AUX " << format("indx %d srch %d\n", static_cast<uint32_t>(awe->TagIndex), diff --git a/tools/llvm-objdump/ELFDump.cpp b/tools/llvm-objdump/ELFDump.cpp index 9c4d67d0f1bd..93d070eee16c 100644 --- a/tools/llvm-objdump/ELFDump.cpp +++ b/tools/llvm-objdump/ELFDump.cpp @@ -178,7 +178,7 @@ void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) { outs() << (Data + Dyn.d_un.d_val) << "\n"; continue; } - warn(toString(StrTabOrErr.takeError())); + reportWarning(toString(StrTabOrErr.takeError()), Filename); consumeError(StrTabOrErr.takeError()); } outs() << format(Fmt, (uint64_t)Dyn.d_un.d_val); diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 58ff7be4543c..e4684d0f1601 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -236,11 +236,11 @@ struct SymbolSorter { bool operator()(const SymbolRef &A, const SymbolRef &B) { Expected<SymbolRef::Type> ATypeOrErr = A.getType(); if (!ATypeOrErr) - report_error(ATypeOrErr.takeError(), A.getObject()->getFileName()); + reportError(ATypeOrErr.takeError(), A.getObject()->getFileName()); SymbolRef::Type AType = *ATypeOrErr; Expected<SymbolRef::Type> BTypeOrErr = B.getType(); if (!BTypeOrErr) - report_error(BTypeOrErr.takeError(), B.getObject()->getFileName()); + 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(); @@ -371,11 +371,8 @@ static void getSectionsAndSymbols(MachOObjectFile *MachOObj, Symbols.push_back(Symbol); } - for (const SectionRef &Section : MachOObj->sections()) { - StringRef SectName; - Section.getName(SectName); + for (const SectionRef &Section : MachOObj->sections()) Sections.push_back(Section); - } bool BaseSegmentAddressSet = false; for (const auto &Command : MachOObj->load_commands()) { @@ -393,10 +390,40 @@ static void getSectionsAndSymbols(MachOObjectFile *MachOObj, BaseSegmentAddressSet = true; BaseSegmentAddress = SLC.vmaddr; } + } else if (Command.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 SLC = MachOObj->getSegment64LoadCommand(Command); + StringRef SegName = SLC.segname; + if (!BaseSegmentAddressSet && SegName != "__PAGEZERO") { + BaseSegmentAddressSet = true; + BaseSegmentAddress = SLC.vmaddr; + } } } } +static bool DumpAndSkipDataInCode(uint64_t PC, const uint8_t *bytes, + DiceTable &Dices, uint64_t &InstSize) { + // Check the data in code table here to see if this is data not an + // instruction to be disassembled. + DiceTable Dice; + Dice.push_back(std::make_pair(PC, DiceRef())); + dice_table_iterator DTI = + std::search(Dices.begin(), Dices.end(), Dice.begin(), Dice.end(), + compareDiceTableEntries); + if (DTI != Dices.end()) { + uint16_t Length; + DTI->second.getLength(Length); + uint16_t Kind; + DTI->second.getKind(Kind); + InstSize = DumpDataInCode(bytes, Length, Kind); + if ((Kind == MachO::DICE_KIND_JUMP_TABLE8) && + (PC == (DTI->first + Length - 1)) && (Length & 1)) + InstSize++; + return true; + } + return false; +} + static void printRelocationTargetName(const MachOObjectFile *O, const MachO::any_relocation_info &RE, raw_string_ostream &Fmt) { @@ -419,13 +446,11 @@ static void printRelocationTargetName(const MachOObjectFile *O, // If we couldn't find a symbol that this relocation refers to, try // to find a section beginning instead. for (const SectionRef &Section : ToolSectionFilter(*O)) { - StringRef Name; uint64_t Addr = Section.getAddress(); if (Addr != Val) continue; - if (std::error_code EC = Section.getName(Name)) - report_error(errorCodeToError(EC), O->getFileName()); - Fmt << Name; + StringRef NameOrErr = unwrapOrError(Section.getName(), O->getFileName()); + Fmt << NameOrErr; return; } @@ -458,10 +483,14 @@ static void printRelocationTargetName(const MachOObjectFile *O, --I; advance(SI, 1); } - if (SI == O->section_end()) + if (SI == O->section_end()) { Fmt << Val << " (?,?)"; - else - SI->getName(S); + } else { + if (Expected<StringRef> NameOrErr = SI->getName()) + S = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + } } Fmt << S; @@ -504,8 +533,8 @@ Error getMachORelocationValueString(const MachOObjectFile *Obj, // NOTE: Scattered relocations don't exist on x86_64. unsigned RType = Obj->getAnyRelocationType(RENext); if (RType != MachO::X86_64_RELOC_UNSIGNED) - report_error(Obj->getFileName(), "Expected X86_64_RELOC_UNSIGNED after " - "X86_64_RELOC_SUBTRACTOR."); + reportError(Obj->getFileName(), "Expected X86_64_RELOC_UNSIGNED after " + "X86_64_RELOC_SUBTRACTOR."); // The X86_64_RELOC_UNSIGNED contains the minuend symbol; // X86_64_RELOC_SUBTRACTOR contains the subtrahend. @@ -553,8 +582,8 @@ Error getMachORelocationValueString(const MachOObjectFile *Obj, unsigned RType = Obj->getAnyRelocationType(RENext); if (RType != MachO::GENERIC_RELOC_PAIR) - report_error(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " - "GENERIC_RELOC_SECTDIFF."); + reportError(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " + "GENERIC_RELOC_SECTDIFF."); printRelocationTargetName(Obj, RE, Fmt); Fmt << "-"; @@ -574,8 +603,8 @@ Error getMachORelocationValueString(const MachOObjectFile *Obj, // GENERIC_RELOC_PAIR. unsigned RType = Obj->getAnyRelocationType(RENext); if (RType != MachO::GENERIC_RELOC_PAIR) - report_error(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " - "GENERIC_RELOC_LOCAL_SECTDIFF."); + reportError(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " + "GENERIC_RELOC_LOCAL_SECTDIFF."); printRelocationTargetName(Obj, RE, Fmt); Fmt << "-"; @@ -614,8 +643,8 @@ Error getMachORelocationValueString(const MachOObjectFile *Obj, // ARM_RELOC_PAIR. unsigned RType = Obj->getAnyRelocationType(RENext); if (RType != MachO::ARM_RELOC_PAIR) - report_error(Obj->getFileName(), "Expected ARM_RELOC_PAIR after " - "ARM_RELOC_HALF"); + reportError(Obj->getFileName(), "Expected ARM_RELOC_PAIR after " + "ARM_RELOC_HALF"); // NOTE: The half of the target virtual address is stashed in the // address field of the secondary relocation, but we can't reverse @@ -1501,7 +1530,12 @@ static void DumpLiteralPointerSection(MachOObjectFile *O, uint64_t SectSize = Sect->getSize(); StringRef SectName; - Sect->getName(SectName); + Expected<StringRef> SectNameOrErr = Sect->getName(); + if (SectNameOrErr) + SectName = *SectNameOrErr; + else + consumeError(SectNameOrErr.takeError()); + DataRefImpl Ref = Sect->getRawDataRefImpl(); StringRef SegmentName = O->getSectionFinalSegmentName(Ref); outs() << SegmentName << ":" << SectName << ":"; @@ -1713,7 +1747,12 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, } for (const SectionRef &Section : O->sections()) { StringRef SectName; - Section.getName(SectName); + Expected<StringRef> SecNameOrErr = Section.getName(); + if (SecNameOrErr) + SectName = *SecNameOrErr; + else + consumeError(SecNameOrErr.takeError()); + DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); if ((DumpSegName.empty() || SegName == DumpSegName) && @@ -1809,7 +1848,12 @@ static void DumpInfoPlistSectionContents(StringRef Filename, MachOObjectFile *O) { for (const SectionRef &Section : O->sections()) { StringRef SectName; - Section.getName(SectName); + Expected<StringRef> SecNameOrErr = Section.getName(); + if (SecNameOrErr) + SectName = *SecNameOrErr; + else + consumeError(SecNameOrErr.takeError()); + DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); if (SegName == "__TEXT" && SectName == "__info_plist") { @@ -1901,12 +1945,16 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, // the error message. if (Disassemble || IndirectSymbols || !FilterSections.empty() || UnwindInfo) if (Error Err = MachOOF->checkSymbolTable()) - report_error(std::move(Err), ArchiveName, FileName, ArchitectureName); + reportError(std::move(Err), FileName, ArchiveName, ArchitectureName); if (DisassembleAll) { for (const SectionRef &Section : MachOOF->sections()) { StringRef SectName; - Section.getName(SectName); + if (Expected<StringRef> NameOrErr = Section.getName()) + SectName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + if (SectName.equals("__text")) { DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = MachOOF->getSectionFinalSegmentName(Ref); @@ -2151,7 +2199,7 @@ static void printMachOUniversalHeaders(const object::MachOUniversalBinary *UB, outs() << " offset " << OFA.getOffset(); if (OFA.getOffset() > size) outs() << " (past end of file)"; - if (OFA.getOffset() % (1 << OFA.getAlign()) != 0) + if (OFA.getOffset() % (1ull << OFA.getAlign()) != 0) outs() << " (not aligned on it's alignment (2^" << OFA.getAlign() << ")"; outs() << "\n"; outs() << " size " << OFA.getSize(); @@ -2165,12 +2213,14 @@ static void printMachOUniversalHeaders(const object::MachOUniversalBinary *UB, } static void printArchiveChild(StringRef Filename, const Archive::Child &C, - bool verbose, bool print_offset, + size_t ChildIndex, bool verbose, + bool print_offset, StringRef ArchitectureName = StringRef()) { if (print_offset) outs() << C.getChildOffset() << "\t"; sys::fs::perms Mode = - unwrapOrError(C.getAccessMode(), Filename, C, ArchitectureName); + unwrapOrError(C.getAccessMode(), getFileNameForError(C, ChildIndex), + Filename, ArchitectureName); if (verbose) { // FIXME: this first dash, "-", is for (Mode & S_IFMT) == S_IFREG. // But there is nothing in sys::fs::perms for S_IFMT or S_IFREG. @@ -2188,11 +2238,14 @@ static void printArchiveChild(StringRef Filename, const Archive::Child &C, outs() << format("0%o ", Mode); } - outs() << format( - "%3d/%-3d %5" PRId64 " ", - unwrapOrError(C.getUID(), Filename, C, ArchitectureName), - unwrapOrError(C.getGID(), Filename, C, ArchitectureName), - unwrapOrError(C.getRawSize(), Filename, C, ArchitectureName)); + outs() << format("%3d/%-3d %5" PRId64 " ", + unwrapOrError(C.getUID(), getFileNameForError(C, ChildIndex), + Filename, ArchitectureName), + unwrapOrError(C.getGID(), getFileNameForError(C, ChildIndex), + Filename, ArchitectureName), + unwrapOrError(C.getRawSize(), + getFileNameForError(C, ChildIndex), Filename, + ArchitectureName)); StringRef RawLastModified = C.getRawLastModified(); if (verbose) { @@ -2215,14 +2268,17 @@ static void printArchiveChild(StringRef Filename, const Archive::Child &C, Expected<StringRef> NameOrErr = C.getName(); if (!NameOrErr) { consumeError(NameOrErr.takeError()); - outs() << unwrapOrError(C.getRawName(), Filename, C, ArchitectureName) + outs() << unwrapOrError(C.getRawName(), + getFileNameForError(C, ChildIndex), Filename, + ArchitectureName) << "\n"; } else { StringRef Name = NameOrErr.get(); outs() << Name << "\n"; } } else { - outs() << unwrapOrError(C.getRawName(), Filename, C, ArchitectureName) + outs() << unwrapOrError(C.getRawName(), getFileNameForError(C, ChildIndex), + Filename, ArchitectureName) << "\n"; } } @@ -2231,11 +2287,13 @@ static void printArchiveHeaders(StringRef Filename, Archive *A, bool verbose, bool print_offset, StringRef ArchitectureName = StringRef()) { Error Err = Error::success(); + size_t I = 0; for (const auto &C : A->children(Err, false)) - printArchiveChild(Filename, C, verbose, print_offset, ArchitectureName); + printArchiveChild(Filename, C, I++, verbose, print_offset, + ArchitectureName); if (Err) - report_error(std::move(Err), StringRef(), Filename, ArchitectureName); + reportError(std::move(Err), Filename, "", ArchitectureName); } static bool ValidateArchFlags() { @@ -2267,7 +2325,7 @@ void parseInputMachO(StringRef Filename) { Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Filename); if (!BinaryOrErr) { if (Error E = isNotObjectErrorInvalidFileType(BinaryOrErr.takeError())) - report_error(std::move(E), Filename); + reportError(std::move(E), Filename); else outs() << Filename << ": is not an object file\n"; return; @@ -2280,11 +2338,13 @@ void parseInputMachO(StringRef Filename) { printArchiveHeaders(Filename, A, !NonVerbose, ArchiveMemberOffsets); Error Err = Error::success(); + unsigned I = -1; for (auto &C : A->children(Err)) { + ++I; Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(std::move(E), Filename, C); + reportError(std::move(E), getFileNameForError(C, I), Filename); continue; } if (MachOObjectFile *O = dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) { @@ -2294,7 +2354,7 @@ void parseInputMachO(StringRef Filename) { } } if (Err) - report_error(std::move(Err), Filename); + reportError(std::move(Err), Filename); return; } if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) { @@ -2346,7 +2406,7 @@ void parseInputMachO(MachOUniversalBinary *UB) { ProcessMachO(Filename, MachOOF, "", ArchitectureName); } else if (Error E = isNotObjectErrorInvalidFileType( ObjOrErr.takeError())) { - report_error(std::move(E), Filename, StringRef(), ArchitectureName); + reportError(std::move(E), "", Filename, ArchitectureName); continue; } else if (Expected<std::unique_ptr<Archive>> AOrErr = I->getAsArchive()) { @@ -2359,11 +2419,15 @@ void parseInputMachO(MachOUniversalBinary *UB) { printArchiveHeaders(Filename, A.get(), !NonVerbose, ArchiveMemberOffsets, ArchitectureName); Error Err = Error::success(); + unsigned I = -1; for (auto &C : A->children(Err)) { + ++I; Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { - if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(std::move(E), Filename, C, ArchitectureName); + if (Error E = + isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + reportError(std::move(E), getFileNameForError(C, I), Filename, + ArchitectureName); continue; } if (MachOObjectFile *O = @@ -2371,12 +2435,13 @@ void parseInputMachO(MachOUniversalBinary *UB) { ProcessMachO(Filename, O, O->getFileName(), ArchitectureName); } if (Err) - report_error(std::move(Err), Filename); + reportError(std::move(Err), Filename); } else { consumeError(AOrErr.takeError()); - error("Mach-O universal file: " + Filename + " for " + - "architecture " + StringRef(I->getArchFlagName()) + - " is not a Mach-O file or an archive file"); + reportError(Filename, + "Mach-O universal file for architecture " + + StringRef(I->getArchFlagName()) + + " is not a Mach-O file or an archive file"); } } } @@ -2406,7 +2471,7 @@ void parseInputMachO(MachOUniversalBinary *UB) { ProcessMachO(Filename, MachOOF); } else if (Error E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { - report_error(std::move(E), Filename); + reportError(std::move(E), Filename); } else if (Expected<std::unique_ptr<Archive>> AOrErr = I->getAsArchive()) { std::unique_ptr<Archive> &A = *AOrErr; @@ -2415,12 +2480,14 @@ void parseInputMachO(MachOUniversalBinary *UB) { printArchiveHeaders(Filename, A.get(), !NonVerbose, ArchiveMemberOffsets); Error Err = Error::success(); + unsigned I = -1; for (auto &C : A->children(Err)) { + ++I; Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(std::move(E), Filename, C); + reportError(std::move(E), getFileNameForError(C, I), Filename); continue; } if (MachOObjectFile *O = @@ -2428,12 +2495,12 @@ void parseInputMachO(MachOUniversalBinary *UB) { ProcessMachO(Filename, O, O->getFileName()); } if (Err) - report_error(std::move(Err), Filename); + reportError(std::move(Err), Filename); } else { consumeError(AOrErr.takeError()); - error("Mach-O universal file: " + Filename + " for architecture " + - StringRef(I->getArchFlagName()) + - " is not a Mach-O file or an archive file"); + reportError(Filename, "Mach-O universal file for architecture " + + StringRef(I->getArchFlagName()) + + " is not a Mach-O file or an archive file"); } return; } @@ -2455,7 +2522,7 @@ void parseInputMachO(MachOUniversalBinary *UB) { ProcessMachO(Filename, MachOOF, "", ArchitectureName); } else if (Error E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { - report_error(std::move(E), StringRef(), Filename, ArchitectureName); + reportError(std::move(E), Filename, "", ArchitectureName); } else if (Expected<std::unique_ptr<Archive>> AOrErr = I->getAsArchive()) { std::unique_ptr<Archive> &A = *AOrErr; outs() << "Archive : " << Filename; @@ -2466,11 +2533,14 @@ void parseInputMachO(MachOUniversalBinary *UB) { printArchiveHeaders(Filename, A.get(), !NonVerbose, ArchiveMemberOffsets, ArchitectureName); Error Err = Error::success(); + unsigned I = -1; for (auto &C : A->children(Err)) { + ++I; Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (Error E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(std::move(E), Filename, C, ArchitectureName); + reportError(std::move(E), getFileNameForError(C, I), Filename, + ArchitectureName); continue; } if (MachOObjectFile *O = @@ -2481,12 +2551,12 @@ void parseInputMachO(MachOUniversalBinary *UB) { } } if (Err) - report_error(std::move(Err), Filename); + reportError(std::move(Err), Filename); } else { consumeError(AOrErr.takeError()); - error("Mach-O universal file: " + Filename + " for architecture " + - StringRef(I->getArchFlagName()) + - " is not a Mach-O file or an archive file"); + reportError(Filename, "Mach-O universal file for architecture " + + StringRef(I->getArchFlagName()) + + " is not a Mach-O file or an archive file"); } } } @@ -3083,7 +3153,7 @@ static void method_reference(struct DisassembleInfo *info, if (strcmp(*ReferenceName, "_objc_msgSend") == 0) { if (info->selector_name != nullptr) { if (info->class_name != nullptr) { - info->method = llvm::make_unique<char[]>( + info->method = std::make_unique<char[]>( 5 + strlen(info->class_name) + strlen(info->selector_name)); char *method = info->method.get(); if (method != nullptr) { @@ -3097,7 +3167,7 @@ static void method_reference(struct DisassembleInfo *info, } } else { info->method = - llvm::make_unique<char[]>(9 + strlen(info->selector_name)); + std::make_unique<char[]>(9 + strlen(info->selector_name)); char *method = info->method.get(); if (method != nullptr) { if (Arch == Triple::x86_64) @@ -3117,7 +3187,7 @@ static void method_reference(struct DisassembleInfo *info, } else if (strcmp(*ReferenceName, "_objc_msgSendSuper2") == 0) { if (info->selector_name != nullptr) { info->method = - llvm::make_unique<char[]>(17 + strlen(info->selector_name)); + std::make_unique<char[]>(17 + strlen(info->selector_name)); char *method = info->method.get(); if (method != nullptr) { if (Arch == Triple::x86_64) @@ -3217,7 +3287,13 @@ static const char *get_pointer_64(uint64_t Address, uint32_t &offset, continue; if (objc_only) { StringRef SectName; - ((*(info->Sections))[SectIdx]).getName(SectName); + Expected<StringRef> SecNameOrErr = + ((*(info->Sections))[SectIdx]).getName(); + if (SecNameOrErr) + SectName = *SecNameOrErr; + else + consumeError(SecNameOrErr.takeError()); + DataRefImpl Ref = ((*(info->Sections))[SectIdx]).getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); if (SegName != "__OBJC" && SectName != "__cstring") @@ -4009,7 +4085,12 @@ static const SectionRef get_section(MachOObjectFile *O, const char *segname, const char *sectname) { for (const SectionRef &Section : O->sections()) { StringRef SectName; - Section.getName(SectName); + Expected<StringRef> SecNameOrErr = Section.getName(); + if (SecNameOrErr) + SectName = *SecNameOrErr; + else + consumeError(SecNameOrErr.takeError()); + DataRefImpl Ref = Section.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); if (SegName == segname && SectName == sectname) @@ -4026,7 +4107,12 @@ walk_pointer_list_64(const char *listname, const SectionRef S, return; StringRef SectName; - S.getName(SectName); + Expected<StringRef> SecNameOrErr = S.getName(); + if (SecNameOrErr) + SectName = *SecNameOrErr; + else + consumeError(SecNameOrErr.takeError()); + DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; @@ -4075,8 +4161,7 @@ walk_pointer_list_32(const char *listname, const SectionRef S, if (S == SectionRef()) return; - StringRef SectName; - S.getName(SectName); + StringRef SectName = unwrapOrError(S.getName(), O->getFileName()); DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; @@ -5750,7 +5835,12 @@ static void print_message_refs64(SectionRef S, struct DisassembleInfo *info) { return; StringRef SectName; - S.getName(SectName); + Expected<StringRef> SecNameOrErr = S.getName(); + if (SecNameOrErr) + SectName = *SecNameOrErr; + else + consumeError(SecNameOrErr.takeError()); + DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; @@ -5813,7 +5903,12 @@ static void print_message_refs32(SectionRef S, struct DisassembleInfo *info) { return; StringRef SectName; - S.getName(SectName); + Expected<StringRef> SecNameOrErr = S.getName(); + if (SecNameOrErr) + SectName = *SecNameOrErr; + else + consumeError(SecNameOrErr.takeError()); + DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; @@ -5859,7 +5954,12 @@ static void print_image_info64(SectionRef S, struct DisassembleInfo *info) { return; StringRef SectName; - S.getName(SectName); + Expected<StringRef> SecNameOrErr = S.getName(); + if (SecNameOrErr) + SectName = *SecNameOrErr; + else + consumeError(SecNameOrErr.takeError()); + DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; @@ -5916,7 +6016,12 @@ static void print_image_info32(SectionRef S, struct DisassembleInfo *info) { return; StringRef SectName; - S.getName(SectName); + Expected<StringRef> SecNameOrErr = S.getName(); + if (SecNameOrErr) + SectName = *SecNameOrErr; + else + consumeError(SecNameOrErr.takeError()); + DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; @@ -5966,7 +6071,12 @@ static void print_image_info(SectionRef S, struct DisassembleInfo *info) { const char *r; StringRef SectName; - S.getName(SectName); + Expected<StringRef> SecNameOrErr = S.getName(); + if (SecNameOrErr) + SectName = *SecNameOrErr; + else + consumeError(SecNameOrErr.takeError()); + DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SegName = info->O->getSectionFinalSegmentName(Ref); outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; @@ -6001,11 +6111,8 @@ static void printObjc2_64bit_MetaData(MachOObjectFile *O, bool verbose) { CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) { - StringRef SectName; - Section.getName(SectName); + for (const SectionRef &Section : O->sections()) Sections.push_back(Section); - } struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); @@ -6086,11 +6193,8 @@ static void printObjc2_32bit_MetaData(MachOObjectFile *O, bool verbose) { CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) { - StringRef SectName; - Section.getName(SectName); + for (const SectionRef &Section : O->sections()) Sections.push_back(Section); - } struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); @@ -6184,11 +6288,8 @@ static bool printObjc1_32bit_MetaData(MachOObjectFile *O, bool verbose) { CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) { - StringRef SectName; - Section.getName(SectName); + for (const SectionRef &Section : O->sections()) Sections.push_back(Section); - } struct DisassembleInfo info(O, &AddrMap, &Sections, verbose); @@ -6345,11 +6446,8 @@ static void DumpProtocolSection(MachOObjectFile *O, const char *sect, CreateSymbolAddressMap(O, &AddrMap); std::vector<SectionRef> Sections; - for (const SectionRef &Section : O->sections()) { - StringRef SectName; - Section.getName(SectName); + for (const SectionRef &Section : O->sections()) Sections.push_back(Section); - } struct DisassembleInfo info(O, &AddrMap, &Sections, true); @@ -7203,7 +7301,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, std::vector<SectionRef> Sections; std::vector<SymbolRef> Symbols; SmallVector<uint64_t, 8> FoundFns; - uint64_t BaseSegmentAddress; + uint64_t BaseSegmentAddress = 0; getSectionsAndSymbols(MachOOF, Sections, Symbols, FoundFns, BaseSegmentAddress); @@ -7242,10 +7340,24 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, // A separate DSym file path was specified, parse it as a macho file, // get the sections and supply it to the section name parsing machinery. if (!DSYMFile.empty()) { + std::string DSYMPath(DSYMFile); + + // If DSYMPath is a .dSYM directory, append the Mach-O file. + if (llvm::sys::fs::is_directory(DSYMPath) && + llvm::sys::path::extension(DSYMPath) == ".dSYM") { + SmallString<128> ShortName(llvm::sys::path::filename(DSYMPath)); + llvm::sys::path::replace_extension(ShortName, ""); + SmallString<1024> FullPath(DSYMPath); + llvm::sys::path::append(FullPath, "Contents", "Resources", "DWARF", + ShortName); + DSYMPath = FullPath.str(); + } + + // Load the file. ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = - MemoryBuffer::getFileOrSTDIN(DSYMFile); + MemoryBuffer::getFileOrSTDIN(DSYMPath); if (std::error_code EC = BufOrErr.getError()) { - report_error(errorCodeToError(EC), DSYMFile); + reportError(errorCodeToError(EC), DSYMPath); return; } @@ -7255,13 +7367,12 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(DSYMBuf.get()->getMemBufferRef()); if (!BinaryOrErr) { - report_error(BinaryOrErr.takeError(), DSYMFile); + reportError(BinaryOrErr.takeError(), DSYMPath); return; } - // We need to keep the Binary elive with the buffer + // We need to keep the Binary alive with the buffer DSYMBinary = std::move(BinaryOrErr.get()); - if (ObjectFile *O = dyn_cast<ObjectFile>(DSYMBinary.get())) { // this is a Mach-O object file, use it if (MachOObjectFile *MachDSYM = dyn_cast<MachOObjectFile>(&*O)) { @@ -7269,7 +7380,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, } else { WithColor::error(errs(), "llvm-objdump") - << DSYMFile << " is not a Mach-O file type.\n"; + << DSYMPath << " is not a Mach-O file type.\n"; return; } } @@ -7289,19 +7400,19 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, Triple T = MachOObjectFile::getArchTriple(CPUType, CPUSubType, nullptr, &ArchFlag); Expected<std::unique_ptr<MachOObjectFile>> MachDSYM = - UB->getObjectForArch(ArchFlag); + UB->getMachOObjectForArch(ArchFlag); if (!MachDSYM) { - report_error(MachDSYM.takeError(), DSYMFile); + reportError(MachDSYM.takeError(), DSYMPath); return; } - // We need to keep the Binary elive with the buffer + // We need to keep the Binary alive with the buffer DbgObj = &*MachDSYM.get(); DSYMBinary = std::move(*MachDSYM); } else { WithColor::error(errs(), "llvm-objdump") - << DSYMFile << " is not a Mach-O or Universal file type.\n"; + << DSYMPath << " is not a Mach-O or Universal file type.\n"; return; } } @@ -7314,8 +7425,12 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, outs() << "(" << DisSegName << "," << DisSectName << ") section\n"; for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) { - StringRef SectName; - if (Sections[SectIdx].getName(SectName) || SectName != DisSectName) + Expected<StringRef> SecNameOrErr = Sections[SectIdx].getName(); + if (!SecNameOrErr) { + consumeError(SecNameOrErr.takeError()); + continue; + } + if (*SecNameOrErr != DisSectName) continue; DataRefImpl DR = Sections[SectIdx].getRawDataRefImpl(); @@ -7496,24 +7611,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, if (!NoShowRawInsn || Arch == Triple::arm) outs() << "\t"; - // Check the data in code table here to see if this is data not an - // instruction to be disassembled. - DiceTable Dice; - Dice.push_back(std::make_pair(PC, DiceRef())); - dice_table_iterator DTI = - std::search(Dices.begin(), Dices.end(), Dice.begin(), Dice.end(), - compareDiceTableEntries); - if (DTI != Dices.end()) { - uint16_t Length; - DTI->second.getLength(Length); - uint16_t Kind; - DTI->second.getKind(Kind); - Size = DumpDataInCode(Bytes.data() + Index, Length, Kind); - if ((Kind == MachO::DICE_KIND_JUMP_TABLE8) && - (PC == (DTI->first + Length - 1)) && (Length & 1)) - Size++; + if (DumpAndSkipDataInCode(PC, Bytes.data() + Index, Dices, Size)) continue; - } SmallVector<char, 64> AnnotationsBytes; raw_svector_ostream Annotations(AnnotationsBytes); @@ -7588,6 +7687,10 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, MCInst Inst; uint64_t PC = SectAddress + Index; + + if (DumpAndSkipDataInCode(PC, Bytes.data() + Index, Dices, InstSize)) + continue; + SmallVector<char, 64> AnnotationsBytes; raw_svector_ostream Annotations(AnnotationsBytes); if (DisAsm->getInstruction(Inst, InstSize, Bytes.slice(Index), PC, @@ -7724,8 +7827,12 @@ static void findUnwindRelocNameAddend(const MachOObjectFile *Obj, auto Sym = Symbols.upper_bound(Addr); if (Sym == Symbols.begin()) { // The first symbol in the object is after this reference, the best we can - // do is section-relative notation. - RelocSection.getName(Name); + // do is section-relative notation. + if (Expected<StringRef> NameOrErr = RelocSection.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + Addend = Addr - SectionAddr; return; } @@ -7744,7 +7851,11 @@ static void findUnwindRelocNameAddend(const MachOObjectFile *Obj, // There is a symbol before this reference, but it's in a different // section. Probably not helpful to mention it, so use the section name. - RelocSection.getName(Name); + if (Expected<StringRef> NameOrErr = RelocSection.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + Addend = Addr - SectionAddr; } @@ -8109,7 +8220,11 @@ void printMachOUnwindInfo(const MachOObjectFile *Obj) { for (const SectionRef &Section : Obj->sections()) { StringRef SectName; - Section.getName(SectName); + if (Expected<StringRef> NameOrErr = Section.getName()) + SectName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + if (SectName == "__compact_unwind") printMachOCompactUnwindSection(Obj, Symbols, Section); else if (SectName == "__unwind_info") @@ -10191,7 +10306,7 @@ void printMachOExportsTrie(const object::MachOObjectFile *Obj) { outs() << "\n"; } if (Err) - report_error(std::move(Err), Obj->getFileName()); + reportError(std::move(Err), Obj->getFileName()); } //===----------------------------------------------------------------------===// @@ -10212,7 +10327,7 @@ void printMachORebaseTable(object::MachOObjectFile *Obj) { Address, Entry.typeName().str().c_str()); } if (Err) - report_error(std::move(Err), Obj->getFileName()); + reportError(std::move(Err), Obj->getFileName()); } static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { @@ -10264,7 +10379,7 @@ void printMachOBindTable(object::MachOObjectFile *Obj) { << Entry.symbolName() << Attr << "\n"; } if (Err) - report_error(std::move(Err), Obj->getFileName()); + reportError(std::move(Err), Obj->getFileName()); } //===----------------------------------------------------------------------===// @@ -10289,7 +10404,7 @@ void printMachOLazyBindTable(object::MachOObjectFile *Obj) { << Entry.symbolName() << "\n"; } if (Err) - report_error(std::move(Err), Obj->getFileName()); + reportError(std::move(Err), Obj->getFileName()); } //===----------------------------------------------------------------------===// @@ -10321,7 +10436,7 @@ void printMachOWeakBindTable(object::MachOObjectFile *Obj) { << "\n"; } if (Err) - report_error(std::move(Err), Obj->getFileName()); + reportError(std::move(Err), Obj->getFileName()); } // get_dyld_bind_info_symbolname() is used for disassembly and passed an @@ -10331,7 +10446,7 @@ void printMachOWeakBindTable(object::MachOObjectFile *Obj) { static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, struct DisassembleInfo *info) { if (info->bindtable == nullptr) { - info->bindtable = llvm::make_unique<SymbolAddressMap>(); + info->bindtable = std::make_unique<SymbolAddressMap>(); Error Err = Error::success(); for (const object::MachOBindEntry &Entry : info->O->bindTable(Err)) { uint64_t Address = Entry.address(); @@ -10340,7 +10455,7 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, (*info->bindtable)[Address] = name; } if (Err) - report_error(std::move(Err), info->O->getFileName()); + reportError(std::move(Err), info->O->getFileName()); } auto name = info->bindtable->lookup(ReferenceValue); return !name.empty() ? name.data() : nullptr; diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 58981203c59e..34a44b3b7fa9 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -51,6 +51,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/GraphWriter.h" #include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" @@ -341,78 +342,84 @@ static StringRef ToolName; typedef std::vector<std::tuple<uint64_t, StringRef, uint8_t>> SectionSymbolsTy; -static bool shouldKeep(object::SectionRef S) { +namespace { +struct FilterResult { + // True if the section should not be skipped. + bool Keep; + + // True if the index counter should be incremented, even if the section should + // be skipped. For example, sections may be skipped if they are not included + // in the --section flag, but we still want those to count toward the section + // count. + bool IncrementIndex; +}; +} // namespace + +static FilterResult checkSectionFilter(object::SectionRef S) { if (FilterSections.empty()) - return true; - StringRef SecName; - std::error_code error = S.getName(SecName); - if (error) - return false; + return {/*Keep=*/true, /*IncrementIndex=*/true}; + + Expected<StringRef> SecNameOrErr = S.getName(); + if (!SecNameOrErr) { + consumeError(SecNameOrErr.takeError()); + return {/*Keep=*/false, /*IncrementIndex=*/false}; + } + StringRef SecName = *SecNameOrErr; + // StringSet does not allow empty key so avoid adding sections with // no name (such as the section with index 0) here. if (!SecName.empty()) FoundSectionSet.insert(SecName); - return is_contained(FilterSections, SecName); -} -SectionFilter ToolSectionFilter(object::ObjectFile const &O) { - return SectionFilter([](object::SectionRef S) { return shouldKeep(S); }, O); -} - -void error(std::error_code EC) { - if (!EC) - return; - WithColor::error(errs(), ToolName) - << "reading file: " << EC.message() << ".\n"; - errs().flush(); - exit(1); -} - -void error(Error E) { - if (!E) - return; - WithColor::error(errs(), ToolName) << toString(std::move(E)); - exit(1); + // Only show the section if it's in the FilterSections list, but always + // increment so the indexing is stable. + return {/*Keep=*/is_contained(FilterSections, SecName), + /*IncrementIndex=*/true}; } -LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { - WithColor::error(errs(), ToolName) << Message << ".\n"; - errs().flush(); - exit(1); +SectionFilter 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) + *Idx = UINT64_MAX; + return SectionFilter( + [Idx](object::SectionRef S) { + FilterResult Result = checkSectionFilter(S); + if (Idx != nullptr && Result.IncrementIndex) + *Idx += 1; + return Result.Keep; + }, + O); } -void warn(StringRef Message) { - WithColor::warning(errs(), ToolName) << Message << ".\n"; - errs().flush(); +std::string getFileNameForError(const object::Archive::Child &C, + unsigned Index) { + Expected<StringRef> NameOrErr = C.getName(); + if (NameOrErr) + return 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) + ">"; } -static void warn(Twine Message) { +void 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) << Message << "\n"; + WithColor::warning(errs(), ToolName) + << "'" << File << "': " << Message << "\n"; errs().flush(); } -LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, Twine Message) { - WithColor::error(errs(), ToolName) - << "'" << File << "': " << Message << ".\n"; - exit(1); -} - -LLVM_ATTRIBUTE_NORETURN void report_error(Error E, StringRef File) { - assert(E); - std::string Buf; - raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS); - OS.flush(); - WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; +LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Twine Message) { + WithColor::error(errs(), ToolName) << "'" << File << "': " << Message << "\n"; exit(1); } -LLVM_ATTRIBUTE_NORETURN void report_error(Error E, StringRef ArchiveName, - StringRef FileName, - StringRef ArchitectureName) { +LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName, + StringRef ArchiveName, + StringRef ArchitectureName) { assert(E); WithColor::error(errs(), ToolName); if (ArchiveName != "") @@ -429,18 +436,13 @@ LLVM_ATTRIBUTE_NORETURN void report_error(Error E, StringRef ArchiveName, exit(1); } -LLVM_ATTRIBUTE_NORETURN void report_error(Error E, StringRef ArchiveName, - const object::Archive::Child &C, - StringRef ArchitectureName) { - Expected<StringRef> NameOrErr = C.getName(); - // TODO: if we have a error getting the name then it would be nice to print - // the index of which archive member this is and or its offset in the - // archive instead of "???" as the name. - if (!NameOrErr) { - consumeError(NameOrErr.takeError()); - report_error(std::move(E), ArchiveName, "???", ArchitectureName); - } else - report_error(std::move(E), ArchiveName, NameOrErr.get(), ArchitectureName); +static void reportCmdLineWarning(Twine Message) { + WithColor::warning(errs(), ToolName) << Message << "\n"; +} + +LLVM_ATTRIBUTE_NORETURN static void reportCmdLineError(Twine Message) { + WithColor::error(errs(), ToolName) << Message << "\n"; + exit(1); } static void warnOnNoMatchForSections() { @@ -455,37 +457,29 @@ static void warnOnNoMatchForSections() { // Warn only if no section in FilterSections is matched. for (StringRef S : MissingSections) - warn("section '" + S + "' mentioned in a -j/--section option, but not " - "found in any input file"); + reportCmdLineWarning("section '" + S + + "' mentioned in a -j/--section option, but not " + "found in any input file"); } -static const Target *getTarget(const ObjectFile *Obj = nullptr) { +static const Target *getTarget(const ObjectFile *Obj) { // Figure out the target triple. Triple TheTriple("unknown-unknown-unknown"); if (TripleName.empty()) { - if (Obj) - TheTriple = Obj->makeTriple(); + TheTriple = Obj->makeTriple(); } else { TheTriple.setTriple(Triple::normalize(TripleName)); - - // Use the triple, but also try to combine with ARM build attributes. - if (Obj) { - auto Arch = Obj->getArch(); - if (Arch == Triple::arm || Arch == Triple::armeb) - Obj->setARMSubArch(TheTriple); - } + auto Arch = Obj->getArch(); + if (Arch == Triple::arm || Arch == Triple::armeb) + Obj->setARMSubArch(TheTriple); } // Get the target specific parser. std::string Error; const Target *TheTarget = TargetRegistry::lookupTarget(ArchName, TheTriple, Error); - if (!TheTarget) { - if (Obj) - report_error(Obj->getFileName(), "can't find target: " + Error); - else - error("can't find target: " + Error); - } + if (!TheTarget) + reportError(Obj->getFileName(), "can't find target: " + Error); // Update the triple name and return the found target. TripleName = TheTriple.getTriple(); @@ -548,17 +542,22 @@ protected: DILineInfo OldLineInfo; const ObjectFile *Obj = nullptr; std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer; - // File name to file contents of source + // File name to file contents of source. std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache; - // Mark the line endings of the cached source + // Mark the line endings of the cached source. std::unordered_map<std::string, std::vector<StringRef>> LineCache; + // Keep track of missing sources. + StringSet<> MissingSources; + // Only emit 'no debug info' warning once. + bool WarnedNoDebugInfo; private: bool cacheSource(const DILineInfo& LineInfoFile); public: SourcePrinter() = default; - SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) : Obj(Obj) { + SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) + : Obj(Obj), WarnedNoDebugInfo(false) { symbolize::LLVMSymbolizer::Options SymbolizerOpts; SymbolizerOpts.PrintFunctions = DILineInfoSpecifier::FunctionNameKind::None; SymbolizerOpts.Demangle = false; @@ -568,6 +567,7 @@ public: virtual ~SourcePrinter() = default; virtual void printSourceLine(raw_ostream &OS, object::SectionedAddress Address, + StringRef ObjectFilename, StringRef Delimiter = "; "); }; @@ -577,8 +577,12 @@ bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source); } else { auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName); - if (!BufferOrError) + if (!BufferOrError) { + if (MissingSources.insert(LineInfo.FileName).second) + reportWarning("failed to find source " + LineInfo.FileName, + Obj->getFileName()); return false; + } Buffer = std::move(*BufferOrError); } // Chomp the file to get lines @@ -599,20 +603,33 @@ bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { void SourcePrinter::printSourceLine(raw_ostream &OS, object::SectionedAddress Address, + StringRef ObjectFilename, StringRef Delimiter) { if (!Symbolizer) return; DILineInfo LineInfo = DILineInfo(); auto ExpectedLineInfo = Symbolizer->symbolizeCode(*Obj, Address); + std::string ErrorMessage; if (!ExpectedLineInfo) - consumeError(ExpectedLineInfo.takeError()); + ErrorMessage = toString(ExpectedLineInfo.takeError()); else LineInfo = *ExpectedLineInfo; - if ((LineInfo.FileName == "<invalid>") || LineInfo.Line == 0 || - ((OldLineInfo.Line == LineInfo.Line) && - (OldLineInfo.FileName == LineInfo.FileName))) + if (LineInfo.FileName == DILineInfo::BadString) { + if (!WarnedNoDebugInfo) { + std::string Warning = + "failed to parse debug information for " + ObjectFilename.str(); + if (!ErrorMessage.empty()) + Warning += ": " + ErrorMessage; + reportWarning(Warning, ObjectFilename); + WarnedNoDebugInfo = true; + } + return; + } + + if (LineInfo.Line == 0 || ((OldLineInfo.Line == LineInfo.Line) && + (OldLineInfo.FileName == LineInfo.FileName))) return; if (PrintLines) @@ -623,8 +640,14 @@ void SourcePrinter::printSourceLine(raw_ostream &OS, return; auto LineBuffer = LineCache.find(LineInfo.FileName); if (LineBuffer != LineCache.end()) { - if (LineInfo.Line > LineBuffer->second.size()) + 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'; } @@ -646,13 +669,14 @@ static bool hasMappingSymbols(const ObjectFile *Obj) { return isArmElf(Obj) || isAArch64Elf(Obj); } -static void printRelocation(const RelocationRef &Rel, uint64_t Address, - bool Is64Bits) { +static void printRelocation(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); - error(getRelocationValueString(Rel, Val)); + if (Error E = getRelocationValueString(Rel, Val)) + reportError(std::move(E), FileName); outs() << format(Fmt.data(), Address) << Name << "\t" << Val << "\n"; } @@ -663,29 +687,25 @@ public: ArrayRef<uint8_t> Bytes, object::SectionedAddress Address, raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, - SourcePrinter *SP, + SourcePrinter *SP, StringRef ObjectFilename, std::vector<RelocationRef> *Rels = nullptr) { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address); + SP->printSourceLine(OS, Address, ObjectFilename); - { - formatted_raw_ostream FOS(OS); - if (!NoLeadingAddr) - FOS << format("%8" PRIx64 ":", Address.Address); - if (!NoShowRawInsn) { - FOS << ' '; - dumpBytes(Bytes, FOS); - } - FOS.flush(); - // The output of printInst starts with a tab. Print some spaces so that - // the tab has 1 column and advances to the target tab stop. - unsigned TabStop = NoShowRawInsn ? 16 : 40; - unsigned Column = FOS.getColumn(); - FOS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8); - - // The dtor calls flush() to ensure the indent comes before printInst(). + size_t Start = OS.tell(); + if (!NoLeadingAddr) + OS << format("%8" PRIx64 ":", Address.Address); + if (!NoShowRawInsn) { + OS << ' '; + dumpBytes(Bytes, OS); } + // The output of printInst starts with a tab. Print some spaces so that + // the tab has 1 column and advances to the target tab stop. + unsigned TabStop = NoShowRawInsn ? 16 : 40; + unsigned Column = OS.tell() - Start; + OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8); + if (MI) IP.printInst(MI, OS, "", STI); else @@ -711,9 +731,10 @@ public: 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) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ""); + SP->printSourceLine(OS, Address, ObjectFilename, ""); if (!MI) { printLead(Bytes, Address.Address, OS); OS << " <unknown>"; @@ -739,7 +760,7 @@ public: auto PrintReloc = [&]() -> void { while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address.Address)) { if (RelCur->getOffset() == Address.Address) { - printRelocation(*RelCur, Address.Address, false); + printRelocation(ObjectFilename, *RelCur, Address.Address, false); return; } ++RelCur; @@ -750,7 +771,7 @@ public: OS << Separator; Separator = "\n"; if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ""); + SP->printSourceLine(OS, Address, ObjectFilename, ""); printLead(Bytes, Address.Address, OS); OS << Preamble; Preamble = " "; @@ -780,9 +801,10 @@ public: 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) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address); + SP->printSourceLine(OS, Address, ObjectFilename); if (MI) { SmallString<40> InstStr; @@ -831,9 +853,10 @@ public: 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) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address); + SP->printSourceLine(OS, Address, ObjectFilename); if (!NoLeadingAddr) OS << format("%8" PRId64 ":", Address.Address / 8); if (!NoShowRawInsn) { @@ -924,10 +947,12 @@ static void addPltEntries(const ObjectFile *Obj, StringSaver &Saver) { Optional<SectionRef> Plt = None; for (const SectionRef &Section : Obj->sections()) { - StringRef Name; - if (Section.getName(Name)) + Expected<StringRef> SecNameOrErr = Section.getName(); + if (!SecNameOrErr) { + consumeError(SecNameOrErr.takeError()); continue; - if (Name == ".plt") + } + if (*SecNameOrErr == ".plt") Plt = Section; } if (!Plt) @@ -968,9 +993,18 @@ static size_t countSkippableZeroBytes(ArrayRef<uint8_t> Buf) { static std::map<SectionRef, std::vector<RelocationRef>> getRelocsMap(object::ObjectFile const &Obj) { std::map<SectionRef, std::vector<RelocationRef>> Ret; + uint64_t I = (uint64_t)-1; for (SectionRef Sec : Obj.sections()) { - section_iterator Relocated = Sec.getRelocatedSection(); - if (Relocated == Obj.section_end() || !shouldKeep(*Relocated)) + ++I; + Expected<section_iterator> RelocatedOrErr = Sec.getRelocatedSection(); + if (!RelocatedOrErr) + reportError(Obj.getFileName(), + "section (" + Twine(I) + + "): failed to get a relocated section: " + + toString(RelocatedOrErr.takeError())); + + section_iterator Relocated = *RelocatedOrErr; + if (Relocated == Obj.section_end() || !checkSectionFilter(*Relocated).Keep) continue; std::vector<RelocationRef> &V = Ret[*Relocated]; for (const RelocationRef &R : Sec.relocations()) @@ -1137,11 +1171,14 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, if (const auto *COFFObj = dyn_cast<COFFObjectFile>(Obj)) { for (const auto &ExportEntry : COFFObj->export_directories()) { StringRef Name; - error(ExportEntry.getSymbolName(Name)); + if (std::error_code EC = ExportEntry.getSymbolName(Name)) + reportError(errorCodeToError(EC), Obj->getFileName()); if (Name.empty()) continue; + uint32_t RVA; - error(ExportEntry.getExportRVA(RVA)); + if (std::error_code EC = ExportEntry.getExportRVA(RVA)) + reportError(errorCodeToError(EC), Obj->getFileName()); uint64_t VA = COFFObj->getImageBase() + RVA; auto Sec = partition_point( @@ -1210,9 +1247,8 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, DataRefImpl DR = Section.getRawDataRefImpl(); SegmentName = MachO->getSectionFinalSegmentName(DR); } - StringRef SectionName; - error(Section.getName(SectionName)); + 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( @@ -1381,10 +1417,10 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, if (Size == 0) Size = 1; - PIP.printInst( - *IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size), - {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, outs(), - "", *STI, &SP, &Rels); + 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(); @@ -1470,7 +1506,8 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, Offset += AdjustVMA; } - printRelocation(*RelCur, SectionAddr + Offset, Is64Bits); + printRelocation(Obj->getFileName(), *RelCur, SectionAddr + Offset, + Is64Bits); ++RelCur; } } @@ -1482,7 +1519,8 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj, StringSet<> MissingDisasmFuncsSet = set_difference(DisasmFuncsSet, FoundDisasmFuncsSet); for (StringRef MissingDisasmFunc : MissingDisasmFuncsSet.keys()) - warn("failed to disassemble missing function " + MissingDisasmFunc); + reportWarning("failed to disassemble missing function " + MissingDisasmFunc, + FileName); } static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { @@ -1497,24 +1535,24 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { std::unique_ptr<const MCRegisterInfo> MRI( TheTarget->createMCRegInfo(TripleName)); if (!MRI) - report_error(Obj->getFileName(), - "no register info for target " + TripleName); + reportError(Obj->getFileName(), + "no register info for target " + TripleName); // Set up disassembler. std::unique_ptr<const MCAsmInfo> AsmInfo( TheTarget->createMCAsmInfo(*MRI, TripleName)); if (!AsmInfo) - report_error(Obj->getFileName(), - "no assembly info for target " + TripleName); + reportError(Obj->getFileName(), + "no assembly info for target " + TripleName); std::unique_ptr<const MCSubtargetInfo> STI( TheTarget->createMCSubtargetInfo(TripleName, MCPU, Features.getString())); if (!STI) - report_error(Obj->getFileName(), - "no subtarget info for target " + TripleName); + reportError(Obj->getFileName(), + "no subtarget info for target " + TripleName); std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo()); if (!MII) - report_error(Obj->getFileName(), - "no instruction info for target " + TripleName); + reportError(Obj->getFileName(), + "no instruction info for target " + TripleName); MCObjectFileInfo MOFI; MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI); // FIXME: for now initialize MCObjectFileInfo with default values @@ -1523,8 +1561,7 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { std::unique_ptr<MCDisassembler> DisAsm( TheTarget->createMCDisassembler(*STI, Ctx)); if (!DisAsm) - report_error(Obj->getFileName(), - "no disassembler for target " + TripleName); + reportError(Obj->getFileName(), "no disassembler for target " + TripleName); // If we have an ARM object file, we need a second disassembler, because // ARM CPUs have two different instruction sets: ARM mode, and Thumb mode. @@ -1549,8 +1586,8 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI)); if (!IP) - report_error(Obj->getFileName(), - "no instruction printer for target " + TripleName); + reportError(Obj->getFileName(), + "no instruction printer for target " + TripleName); IP->setPrintImmHex(PrintImmHex); PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName)); @@ -1558,7 +1595,8 @@ static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { for (StringRef Opt : DisassemblerOptions) if (!IP->applyTargetSpecificCLOption(Opt)) - error("Unrecognized disassembler option: " + Opt); + reportError(Obj->getFileName(), + "Unrecognized disassembler option: " + Opt); disassembleObject(TheTarget, Obj, Ctx, DisAsm.get(), SecondaryDisAsm.get(), MIA.get(), IP.get(), STI.get(), SecondarySTI.get(), PIP, @@ -1577,16 +1615,21 @@ void printRelocations(const ObjectFile *Obj) { // sections. Usually, there is an only one relocation section for // each relocated section. MapVector<SectionRef, std::vector<SectionRef>> SecToRelSec; - for (const SectionRef &Section : ToolSectionFilter(*Obj)) { + uint64_t Ndx; + for (const SectionRef &Section : ToolSectionFilter(*Obj, &Ndx)) { if (Section.relocation_begin() == Section.relocation_end()) continue; - const SectionRef TargetSec = *Section.getRelocatedSection(); - SecToRelSec[TargetSec].push_back(Section); + Expected<section_iterator> SecOrErr = Section.getRelocatedSection(); + if (!SecOrErr) + reportError(Obj->getFileName(), + "section (" + Twine(Ndx) + + "): unable to get a relocation target: " + + toString(SecOrErr.takeError())); + SecToRelSec[**SecOrErr].push_back(Section); } for (std::pair<SectionRef, std::vector<SectionRef>> &P : SecToRelSec) { - StringRef SecName; - error(P.first.getName(SecName)); + StringRef SecName = unwrapOrError(P.first.getName(), Obj->getFileName()); outs() << "RELOCATION RECORDS FOR [" << SecName << "]:\n"; for (SectionRef Section : P.second) { @@ -1597,7 +1640,9 @@ void printRelocations(const ObjectFile *Obj) { if (Address < StartAddress || Address > StopAddress || getHidden(Reloc)) continue; Reloc.getTypeName(RelocName); - error(getRelocationValueString(Reloc, ValueStr)); + if (Error E = getRelocationValueString(Reloc, ValueStr)) + reportError(std::move(E), Obj->getFileName()); + outs() << format(Fmt.data(), Address) << " " << RelocName << " " << ValueStr << "\n"; } @@ -1613,7 +1658,7 @@ void printDynamicRelocations(const ObjectFile *Obj) { const auto *Elf = dyn_cast<ELFObjectFileBase>(Obj); if (!Elf || Elf->getEType() != ELF::ET_DYN) { - error("not a dynamic object"); + reportError(Obj->getFileName(), "not a dynamic object"); return; } @@ -1629,7 +1674,8 @@ void printDynamicRelocations(const ObjectFile *Obj) { SmallString<32> RelocName; SmallString<32> ValueStr; Reloc.getTypeName(RelocName); - error(getRelocationValueString(Reloc, ValueStr)); + if (Error E = getRelocationValueString(Reloc, ValueStr)) + reportError(std::move(E), Obj->getFileName()); outs() << format(Fmt.data(), Address) << " " << RelocName << " " << ValueStr << "\n"; } @@ -1647,47 +1693,64 @@ static bool shouldDisplayLMA(const ObjectFile *Obj) { return ShowLMA; } +static size_t getMaxSectionNameWidth(const ObjectFile *Obj) { + // Default column width for names is 13 even if no names are that long. + size_t MaxWidth = 13; + for (const SectionRef &Section : ToolSectionFilter(*Obj)) { + StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName()); + MaxWidth = std::max(MaxWidth, Name.size()); + } + return MaxWidth; +} + void printSectionHeaders(const ObjectFile *Obj) { + size_t NameWidth = getMaxSectionNameWidth(Obj); + size_t AddressWidth = 2 * Obj->getBytesInAddress(); bool HasLMAColumn = shouldDisplayLMA(Obj); if (HasLMAColumn) outs() << "Sections:\n" - "Idx Name Size VMA LMA " - "Type\n"; + "Idx " + << left_justify("Name", NameWidth) << " Size " + << left_justify("VMA", AddressWidth) << " " + << left_justify("LMA", AddressWidth) << " Type\n"; else outs() << "Sections:\n" - "Idx Name Size VMA Type\n"; + "Idx " + << left_justify("Name", NameWidth) << " Size " + << left_justify("VMA", AddressWidth) << " Type\n"; - for (const SectionRef &Section : ToolSectionFilter(*Obj)) { - StringRef Name; - error(Section.getName(Name)); + uint64_t Idx; + for (const SectionRef &Section : ToolSectionFilter(*Obj, &Idx)) { + StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName()); uint64_t VMA = Section.getAddress(); if (shouldAdjustVA(Section)) VMA += AdjustVMA; uint64_t Size = Section.getSize(); - bool Text = Section.isText(); - bool Data = Section.isData(); - bool BSS = Section.isBSS(); - std::string Type = (std::string(Text ? "TEXT " : "") + - (Data ? "DATA " : "") + (BSS ? "BSS" : "")); + + std::string Type = Section.isText() ? "TEXT" : ""; + if (Section.isData()) + Type += Type.empty() ? "DATA" : " DATA"; + if (Section.isBSS()) + Type += Type.empty() ? "BSS" : " BSS"; if (HasLMAColumn) - outs() << format("%3d %-13s %08" PRIx64 " %016" PRIx64 " %016" PRIx64 - " %s\n", - (unsigned)Section.getIndex(), Name.str().c_str(), Size, - VMA, getELFSectionLMA(Section), Type.c_str()); + outs() << format("%3" PRIu64 " %-*s %08" PRIx64 " ", Idx, NameWidth, + Name.str().c_str(), Size) + << format_hex_no_prefix(VMA, AddressWidth) << " " + << format_hex_no_prefix(getELFSectionLMA(Section), AddressWidth) + << " " << Type << "\n"; else - outs() << format("%3d %-13s %08" PRIx64 " %016" PRIx64 " %s\n", - (unsigned)Section.getIndex(), Name.str().c_str(), Size, - VMA, Type.c_str()); + outs() << format("%3" PRIu64 " %-*s %08" PRIx64 " ", Idx, NameWidth, + Name.str().c_str(), Size) + << format_hex_no_prefix(VMA, AddressWidth) << " " << Type << "\n"; } outs() << "\n"; } void printSectionContents(const ObjectFile *Obj) { for (const SectionRef &Section : ToolSectionFilter(*Obj)) { - StringRef Name; - error(Section.getName(Name)); + StringRef Name = unwrapOrError(Section.getName(), Obj->getFileName()); uint64_t BaseAddr = Section.getAddress(); uint64_t Size = Section.getSize(); if (!Size) @@ -1741,21 +1804,26 @@ void printSymbolTable(const ObjectFile *O, StringRef ArchiveName, const StringRef FileName = O->getFileName(); for (auto I = O->symbol_begin(), E = O->symbol_end(); I != E; ++I) { const SymbolRef &Symbol = *I; - uint64_t Address = unwrapOrError(Symbol.getAddress(), ArchiveName, FileName, + uint64_t Address = unwrapOrError(Symbol.getAddress(), FileName, ArchiveName, ArchitectureName); if ((Address < StartAddress) || (Address > StopAddress)) continue; - SymbolRef::Type Type = unwrapOrError(Symbol.getType(), ArchiveName, - FileName, ArchitectureName); + SymbolRef::Type Type = unwrapOrError(Symbol.getType(), FileName, + ArchiveName, ArchitectureName); uint32_t Flags = Symbol.getFlags(); - section_iterator Section = unwrapOrError(Symbol.getSection(), ArchiveName, - FileName, ArchitectureName); + section_iterator Section = unwrapOrError(Symbol.getSection(), FileName, + ArchiveName, ArchitectureName); StringRef Name; - if (Type == SymbolRef::ST_Debug && Section != O->section_end()) - Section->getName(Name); - else - Name = unwrapOrError(Symbol.getName(), ArchiveName, FileName, + if (Type == SymbolRef::ST_Debug && Section != O->section_end()) { + if (Expected<StringRef> NameOrErr = Section->getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + } else { + Name = unwrapOrError(Symbol.getName(), FileName, ArchiveName, ArchitectureName); + } bool Global = Flags & SymbolRef::SF_Global; bool Weak = Flags & SymbolRef::SF_Weak; @@ -1801,8 +1869,8 @@ void printSymbolTable(const ObjectFile *O, StringRef ArchiveName, StringRef SegmentName = MachO->getSectionFinalSegmentName(DR); outs() << SegmentName << ","; } - StringRef SectionName; - error(Section->getName(SectionName)); + StringRef SectionName = + unwrapOrError(Section->getName(), O->getFileName()); outs() << SectionName; } @@ -1875,7 +1943,11 @@ void printRawClangAST(const ObjectFile *Obj) { Optional<object::SectionRef> ClangASTSection; for (auto Sec : ToolSectionFilter(*Obj)) { StringRef Name; - Sec.getName(Name); + if (Expected<StringRef> NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + if (Name == ClangASTSectionName) { ClangASTSection = Sec; break; @@ -1907,7 +1979,11 @@ static void printFaultMaps(const ObjectFile *Obj) { for (auto Sec : ToolSectionFilter(*Obj)) { StringRef Name; - Sec.getName(Name); + if (Expected<StringRef> NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + if (Name == FaultMapSectionName) { FaultMapSection = Sec; break; @@ -1946,12 +2022,12 @@ static void printPrivateFileHeaders(const ObjectFile *O, bool OnlyFirst) { printMachOLoadCommands(O); return; } - report_error(O->getFileName(), "Invalid/Unsupported object file format"); + reportError(O->getFileName(), "Invalid/Unsupported object file format"); } static void printFileHeaders(const ObjectFile *O) { if (!O->isELF() && !O->isCOFF()) - report_error(O->getFileName(), "Invalid/Unsupported object file format"); + reportError(O->getFileName(), "Invalid/Unsupported object file format"); Triple::ArchType AT = O->getArch(); outs() << "architecture: " << Triple::getArchTypeName(AT) << "\n"; @@ -2010,6 +2086,43 @@ static void printArchiveChild(StringRef Filename, const Archive::Child &C) { outs() << Name << "\n"; } +// For ELF only now. +static bool shouldWarnForInvalidStartStopAddress(ObjectFile *Obj) { + if (const auto *Elf = dyn_cast<ELFObjectFileBase>(Obj)) { + if (Elf->getEType() != ELF::ET_REL) + return true; + } + return false; +} + +static void checkForInvalidStartStopAddress(ObjectFile *Obj, + uint64_t Start, uint64_t Stop) { + if (!shouldWarnForInvalidStartStopAddress(Obj)) + return; + + for (const SectionRef &Section : Obj->sections()) + if (ELFSectionRef(Section).getFlags() & ELF::SHF_ALLOC) { + uint64_t BaseAddr = Section.getAddress(); + uint64_t Size = Section.getSize(); + if ((Start < BaseAddr + Size) && Stop > BaseAddr) + return; + } + + if (StartAddress.getNumOccurrences() == 0) + reportWarning("no section has address less than 0x" + + Twine::utohexstr(Stop) + " specified by --stop-address", + Obj->getFileName()); + else if (StopAddress.getNumOccurrences() == 0) + reportWarning("no section has address greater than or equal to 0x" + + Twine::utohexstr(Start) + " specified by --start-address", + Obj->getFileName()); + else + reportWarning("no section overlaps the range [0x" + + Twine::utohexstr(Start) + ",0x" + Twine::utohexstr(Stop) + + ") specified by --start-address/--stop-address", + Obj->getFileName()); +} + static void dumpObject(ObjectFile *O, const Archive *A = nullptr, const Archive::Child *C = nullptr) { // Avoid other output when using a raw option. @@ -2022,27 +2135,40 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr, outs() << ":\tfile format " << O->getFileFormatName() << "\n\n"; } + if (StartAddress.getNumOccurrences() || StopAddress.getNumOccurrences()) + checkForInvalidStartStopAddress(O, StartAddress, StopAddress); + + // Note: the order here matches GNU objdump for compatability. StringRef ArchiveName = A ? A->getFileName() : ""; - if (FileHeaders) - printFileHeaders(O); if (ArchiveHeaders && !MachOOpt && C) printArchiveChild(ArchiveName, *C); - if (Disassemble) - disassembleObject(O, Relocations); + if (FileHeaders) + printFileHeaders(O); + if (PrivateHeaders || FirstPrivateHeader) + printPrivateFileHeaders(O, FirstPrivateHeader); + if (SectionHeaders) + printSectionHeaders(O); + if (SymbolTable) + printSymbolTable(O, ArchiveName); + if (DwarfDumpType != DIDT_Null) { + std::unique_ptr<DIContext> DICtx = DWARFContext::create(*O); + // Dump the complete DWARF structure. + DIDumpOptions DumpOpts; + DumpOpts.DumpType = DwarfDumpType; + DICtx->dump(outs(), DumpOpts); + } if (Relocations && !Disassemble) printRelocations(O); if (DynamicRelocations) printDynamicRelocations(O); - if (SectionHeaders) - printSectionHeaders(O); if (SectionContents) printSectionContents(O); - if (SymbolTable) - printSymbolTable(O, ArchiveName); + if (Disassemble) + disassembleObject(O, Relocations); if (UnwindInfo) printUnwindInfo(O); - if (PrivateHeaders || FirstPrivateHeader) - printPrivateFileHeaders(O, FirstPrivateHeader); + + // Mach-O specific options: if (ExportsTrie) printExportsTrie(O); if (Rebase) @@ -2053,17 +2179,12 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr, printLazyBindTable(O); if (WeakBind) printWeakBindTable(O); + + // Other special sections: if (RawClangAST) printRawClangAST(O); if (FaultMapSection) printFaultMaps(O); - if (DwarfDumpType != DIDT_Null) { - std::unique_ptr<DIContext> DICtx = DWARFContext::create(*O); - // Dump the complete DWARF structure. - DIDumpOptions DumpOpts; - DumpOpts.DumpType = DwarfDumpType; - DICtx->dump(outs(), DumpOpts); - } } static void dumpObject(const COFFImportFile *I, const Archive *A, @@ -2086,11 +2207,13 @@ static void dumpObject(const COFFImportFile *I, const Archive *A, /// Dump each object file in \a a; static void dumpArchive(const Archive *A) { Error Err = Error::success(); + unsigned I = -1; for (auto &C : A->children(Err)) { + ++I; Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(std::move(E), A->getFileName(), C); + reportError(std::move(E), getFileNameForError(C, I), A->getFileName()); continue; } if (ObjectFile *O = dyn_cast<ObjectFile>(&*ChildOrErr.get())) @@ -2098,11 +2221,11 @@ static void dumpArchive(const Archive *A) { else if (COFFImportFile *I = dyn_cast<COFFImportFile>(&*ChildOrErr.get())) dumpObject(I, A, &C); else - report_error(errorCodeToError(object_error::invalid_file_type), - A->getFileName()); + reportError(errorCodeToError(object_error::invalid_file_type), + A->getFileName()); } if (Err) - report_error(std::move(Err), A->getFileName()); + reportError(std::move(Err), A->getFileName()); } /// Open file and figure out how to dump it. @@ -2126,7 +2249,7 @@ static void dumpInput(StringRef file) { else if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Binary)) parseInputMachO(UB); else - report_error(errorCodeToError(object_error::invalid_file_type), file); + reportError(errorCodeToError(object_error::invalid_file_type), file); } } // namespace llvm @@ -2147,7 +2270,7 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n"); if (StartAddress >= StopAddress) - error("start address should be less than stop address"); + reportCmdLineError("start address should be less than stop address"); ToolName = argv[0]; diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h index e58d4a05c2e6..43ce02ae0bc2 100644 --- a/tools/llvm-objdump/llvm-objdump.h +++ b/tools/llvm-objdump/llvm-objdump.h @@ -31,6 +31,8 @@ extern cl::opt<bool> Demangle; typedef std::function<bool(llvm::object::SectionRef const &)> FilterPredicate; +/// A filtered iterator for SectionRefs that skips sections based on some given +/// predicate. class SectionFilterIterator { public: SectionFilterIterator(FilterPredicate P, @@ -60,6 +62,8 @@ private: llvm::object::section_iterator End; }; +/// Creates an iterator range of SectionFilterIterators for a given Object and +/// predicate. class SectionFilter { public: SectionFilter(FilterPredicate P, llvm::object::ObjectFile const &O) @@ -79,7 +83,15 @@ private: }; // Various helper functions. -SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O); + +/// Creates a SectionFilter with a standard predicate that conditionally skips +/// sections when the --section objdump flag is provided. +/// +/// Idx is an optional output parameter that keeps track of which section index +/// this is. This may be different than the actual section number, as some +/// sections may be filtered (e.g. symbol tables). +SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O, + uint64_t *Idx = nullptr); Error getELFRelocationValueString(const object::ELFObjectFileBase *Obj, const object::RelocationRef &Rel, @@ -96,8 +108,6 @@ Error getMachORelocationValueString(const object::MachOObjectFile *Obj, uint64_t getELFSectionLMA(const object::ELFSectionRef& Sec); -void error(std::error_code ec); -void error(Error E); bool isRelocAddressLess(object::RelocationRef A, object::RelocationRef B); void parseInputMachO(StringRef Filename); void parseInputMachO(object::MachOUniversalBinary *UB); @@ -129,24 +139,22 @@ void printSectionHeaders(const object::ObjectFile *O); void printSectionContents(const object::ObjectFile *O); void printSymbolTable(const object::ObjectFile *O, StringRef ArchiveName, StringRef ArchitectureName = StringRef()); -void warn(StringRef Message); -LLVM_ATTRIBUTE_NORETURN void error(Twine Message); -LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, Twine Message); -LLVM_ATTRIBUTE_NORETURN void report_error(Error E, StringRef File); -LLVM_ATTRIBUTE_NORETURN void -report_error(Error E, StringRef FileName, StringRef ArchiveName, - StringRef ArchitectureName = StringRef()); -LLVM_ATTRIBUTE_NORETURN void -report_error(Error E, StringRef ArchiveName, const object::Archive::Child &C, - StringRef ArchitectureName = StringRef()); +LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Twine Message); +LLVM_ATTRIBUTE_NORETURN void reportError(Error E, StringRef FileName, + StringRef ArchiveName = "", + StringRef ArchitectureName = ""); +void reportWarning(Twine Message, StringRef File); template <typename T, typename... Ts> T unwrapOrError(Expected<T> EO, Ts &&... Args) { if (EO) return std::move(*EO); - report_error(EO.takeError(), std::forward<Ts>(Args)...); + reportError(EO.takeError(), std::forward<Ts>(Args)...); } +std::string getFileNameForError(const object::Archive::Child &C, + unsigned Index); + } // end namespace llvm #endif diff --git a/tools/llvm-pdbutil/BytesOutputStyle.cpp b/tools/llvm-pdbutil/BytesOutputStyle.cpp index 162d12c120b4..ffc907e09f11 100644 --- a/tools/llvm-pdbutil/BytesOutputStyle.cpp +++ b/tools/llvm-pdbutil/BytesOutputStyle.cpp @@ -457,7 +457,7 @@ BytesOutputStyle::initializeTypes(uint32_t StreamIdx) { uint32_t Count = Tpi->getNumTypeRecords(); auto Offsets = Tpi->getTypeIndexOffsets(); TypeCollection = - llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets); + std::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets); return *TypeCollection; } diff --git a/tools/llvm-pdbutil/DumpOutputStyle.cpp b/tools/llvm-pdbutil/DumpOutputStyle.cpp index 962d4cf88a8a..4d82e0fd9174 100644 --- a/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -1369,9 +1369,10 @@ Error DumpOutputStyle::dumpTypesFromObjectFile() { LazyRandomTypeCollection Types(100); for (const auto &S : getObj().sections()) { - StringRef SectionName; - if (auto EC = S.getName(SectionName)) - return errorCodeToError(EC); + Expected<StringRef> NameOrErr = S.getName(); + if (!NameOrErr) + return NameOrErr.takeError(); + StringRef SectionName = *NameOrErr; // .debug$T is a standard CodeView type section, while .debug$P is the same // format but used for MSVC precompiled header object files. @@ -1551,7 +1552,7 @@ Error DumpOutputStyle::dumpModuleSymsForObj() { Dumper.setSymbolGroup(&Strings); for (auto Symbol : Symbols) { if (auto EC = Visitor.visitSymbolRecord(Symbol)) { - SymbolError = llvm::make_unique<Error>(std::move(EC)); + SymbolError = std::make_unique<Error>(std::move(EC)); return; } } diff --git a/tools/llvm-pdbutil/ExplainOutputStyle.cpp b/tools/llvm-pdbutil/ExplainOutputStyle.cpp index 94faa0463981..3d2490509c03 100644 --- a/tools/llvm-pdbutil/ExplainOutputStyle.cpp +++ b/tools/llvm-pdbutil/ExplainOutputStyle.cpp @@ -64,7 +64,7 @@ Error ExplainOutputStyle::explainPdbFile() { Error ExplainOutputStyle::explainBinaryFile() { std::unique_ptr<BinaryByteStream> Stream = - llvm::make_unique<BinaryByteStream>(File.unknown().getBuffer(), + std::make_unique<BinaryByteStream>(File.unknown().getBuffer(), llvm::support::little); switch (opts::explain::InputType) { case opts::explain::InputFileType::DBIStream: { diff --git a/tools/llvm-pdbutil/InputFile.cpp b/tools/llvm-pdbutil/InputFile.cpp index bd23bfdbe31a..b316882de64d 100644 --- a/tools/llvm-pdbutil/InputFile.cpp +++ b/tools/llvm-pdbutil/InputFile.cpp @@ -66,12 +66,13 @@ getModuleDebugStream(PDBFile &File, StringRef &ModuleName, uint32_t Index) { static inline bool isCodeViewDebugSubsection(object::SectionRef Section, StringRef Name, BinaryStreamReader &Reader) { - StringRef SectionName; - if (Section.getName(SectionName)) - return false; - - if (SectionName != Name) + if (Expected<StringRef> NameOrErr = Section.getName()) { + if (*NameOrErr != Name) + return false; + } else { + consumeError(NameOrErr.takeError()); return false; + } Expected<StringRef> ContentsOrErr = Section.getContents(); if (!ContentsOrErr) { @@ -384,7 +385,7 @@ InputFile::getOrCreateTypeCollection(TypeCollectionKind Kind) { uint32_t Count = Stream.getNumTypeRecords(); auto Offsets = Stream.getTypeIndexOffsets(); Collection = - llvm::make_unique<LazyRandomTypeCollection>(Array, Count, Offsets); + std::make_unique<LazyRandomTypeCollection>(Array, Count, Offsets); return *Collection; } @@ -397,11 +398,11 @@ InputFile::getOrCreateTypeCollection(TypeCollectionKind Kind) { if (!isDebugTSection(Section, Records)) continue; - Types = llvm::make_unique<LazyRandomTypeCollection>(Records, 100); + Types = std::make_unique<LazyRandomTypeCollection>(Records, 100); return *Types; } - Types = llvm::make_unique<LazyRandomTypeCollection>(100); + Types = std::make_unique<LazyRandomTypeCollection>(100); return *Types; } diff --git a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp index e5ae47050678..ebfa50625e76 100644 --- a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -569,8 +569,9 @@ Error MinimalSymbolDumper::visitKnownRecord( Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DefRangeFramePointerRelSym &Def) { AutoIndent Indent(P, 7); - P.formatLine("offset = {0}, range = {1}", Def.Offset, formatRange(Def.Range)); - P.formatLine("gaps = {2}", Def.Offset, + P.formatLine("offset = {0}, range = {1}", Def.Hdr.Offset, + formatRange(Def.Range)); + P.formatLine("gaps = {2}", Def.Hdr.Offset, formatGaps(P.getIndentLevel() + 9, Def.Gaps)); return Error::success(); } diff --git a/tools/llvm-pdbutil/PrettyTypeDumper.cpp b/tools/llvm-pdbutil/PrettyTypeDumper.cpp index e8f8e5aa62c9..2f7a39803ca5 100644 --- a/tools/llvm-pdbutil/PrettyTypeDumper.cpp +++ b/tools/llvm-pdbutil/PrettyTypeDumper.cpp @@ -117,7 +117,7 @@ filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E, continue; } - auto Layout = llvm::make_unique<ClassLayout>(std::move(Class)); + auto Layout = std::make_unique<ClassLayout>(std::move(Class)); if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) { ++Discarded; continue; @@ -259,7 +259,7 @@ void TypeDumper::start(const PDBSymbolExe &Exe) { continue; } - auto Layout = llvm::make_unique<ClassLayout>(std::move(Class)); + auto Layout = std::make_unique<ClassLayout>(std::move(Class)); if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) continue; diff --git a/tools/llvm-pdbutil/llvm-pdbutil.cpp b/tools/llvm-pdbutil/llvm-pdbutil.cpp index 785a98086791..9307300861d4 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -863,8 +863,8 @@ static void pdb2Yaml(StringRef Path) { std::unique_ptr<IPDBSession> Session; auto &File = loadPDB(Path, Session); - auto O = llvm::make_unique<YAMLOutputStyle>(File); - O = llvm::make_unique<YAMLOutputStyle>(File); + auto O = std::make_unique<YAMLOutputStyle>(File); + O = std::make_unique<YAMLOutputStyle>(File); ExitOnErr(O->dump()); } @@ -872,7 +872,7 @@ static void pdb2Yaml(StringRef Path) { static void dumpRaw(StringRef Path) { InputFile IF = ExitOnErr(InputFile::open(Path)); - auto O = llvm::make_unique<DumpOutputStyle>(IF); + auto O = std::make_unique<DumpOutputStyle>(IF); ExitOnErr(O->dump()); } @@ -880,7 +880,7 @@ static void dumpBytes(StringRef Path) { std::unique_ptr<IPDBSession> Session; auto &File = loadPDB(Path, Session); - auto O = llvm::make_unique<BytesOutputStyle>(File); + auto O = std::make_unique<BytesOutputStyle>(File); ExitOnErr(O->dump()); } @@ -1347,7 +1347,7 @@ static void explain() { ExitOnErr(InputFile::open(opts::explain::InputFilename.front(), true)); for (uint64_t Off : opts::explain::Offsets) { - auto O = llvm::make_unique<ExplainOutputStyle>(IF, Off); + auto O = std::make_unique<ExplainOutputStyle>(IF, Off); ExitOnErr(O->dump()); } diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp index 16d3ebe3fcbc..41e9abb82b1f 100644 --- a/tools/llvm-profdata/llvm-profdata.cpp +++ b/tools/llvm-profdata/llvm-profdata.cpp @@ -26,6 +26,7 @@ #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/WithColor.h" #include "llvm/Support/raw_ostream.h" @@ -37,6 +38,7 @@ enum ProfileFormat { PF_None = 0, PF_Text, PF_Compact_Binary, + PF_Ext_Binary, PF_GCC, PF_Binary }; @@ -84,6 +86,15 @@ static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { namespace { enum ProfileKinds { instr, sample }; +enum FailureMode { failIfAnyAreInvalid, failIfAllAreInvalid }; +} + +static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, + StringRef Whence = "") { + if (FailMode == failIfAnyAreInvalid) + exitWithErrorCode(EC, Whence); + else + warn(EC.message(), Whence); } static void handleMergeWriterError(Error E, StringRef WhenceFile = "", @@ -136,7 +147,7 @@ public: if (!BufOrError) exitWithErrorCode(BufOrError.getError(), InputFile); - auto Remapper = llvm::make_unique<SymbolRemapper>(); + auto Remapper = std::make_unique<SymbolRemapper>(); Remapper->File = std::move(BufOrError.get()); for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); @@ -173,33 +184,16 @@ typedef SmallVector<WeightedFile, 5> WeightedFileVector; struct WriterContext { std::mutex Lock; InstrProfWriter Writer; - Error Err; - std::string ErrWhence; + std::vector<std::pair<Error, std::string>> Errors; std::mutex &ErrLock; SmallSet<instrprof_error, 4> &WriterErrorCodes; WriterContext(bool IsSparse, std::mutex &ErrLock, SmallSet<instrprof_error, 4> &WriterErrorCodes) - : Lock(), Writer(IsSparse), Err(Error::success()), ErrWhence(""), - ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {} + : Lock(), Writer(IsSparse), Errors(), ErrLock(ErrLock), + WriterErrorCodes(WriterErrorCodes) {} }; -/// Determine whether an error is fatal for profile merging. -static bool isFatalError(instrprof_error IPE) { - switch (IPE) { - default: - return true; - case instrprof_error::success: - case instrprof_error::eof: - case instrprof_error::unknown_function: - case instrprof_error::hash_mismatch: - case instrprof_error::count_mismatch: - case instrprof_error::counter_overflow: - case instrprof_error::value_site_count_mismatch: - return false; - } -} - /// Computer the overlap b/w profile BaseFilename and TestFileName, /// and store the program level result to Overlap. static void overlapInput(const std::string &BaseFilename, @@ -212,7 +206,7 @@ static void overlapInput(const std::string &BaseFilename, // Skip the empty profiles by returning sliently. instrprof_error IPE = InstrProfError::take(std::move(E)); if (IPE != instrprof_error::empty_raw_profile) - WC->Err = make_error<InstrProfError>(IPE); + WC->Errors.emplace_back(make_error<InstrProfError>(IPE), TestFilename); return; } @@ -231,21 +225,17 @@ static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, WriterContext *WC) { std::unique_lock<std::mutex> CtxGuard{WC->Lock}; - // If there's a pending hard error, don't do more work. - if (WC->Err) - return; - // Copy the filename, because llvm::ThreadPool copied the input "const // WeightedFile &" by value, making a reference to the filename within it // invalid outside of this packaged task. - WC->ErrWhence = Input.Filename; + std::string Filename = Input.Filename; auto ReaderOrErr = InstrProfReader::create(Input.Filename); if (Error E = ReaderOrErr.takeError()) { // Skip the empty profiles by returning sliently. instrprof_error IPE = InstrProfError::take(std::move(E)); if (IPE != instrprof_error::empty_raw_profile) - WC->Err = make_error<InstrProfError>(IPE); + WC->Errors.emplace_back(make_error<InstrProfError>(IPE), Filename); return; } @@ -253,9 +243,11 @@ static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, bool IsIRProfile = Reader->isIRLevelProfile(); bool HasCSIRProfile = Reader->hasCSIRLevelProfile(); if (WC->Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { - WC->Err = make_error<StringError>( - "Merge IR generated profile with Clang generated profile.", - std::error_code()); + WC->Errors.emplace_back( + make_error<StringError>( + "Merge IR generated profile with Clang generated profile.", + std::error_code()), + Filename); return; } @@ -278,30 +270,23 @@ static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, FuncName, firstTime); }); } - if (Reader->hasError()) { - if (Error E = Reader->getError()) { - instrprof_error IPE = InstrProfError::take(std::move(E)); - if (isFatalError(IPE)) - WC->Err = make_error<InstrProfError>(IPE); - } - } + if (Reader->hasError()) + if (Error E = Reader->getError()) + WC->Errors.emplace_back(std::move(E), Filename); } /// Merge the \p Src writer context into \p Dst. static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { - // If we've already seen a hard error, continuing with the merge would - // clobber it. - if (Dst->Err || Src->Err) - return; + for (auto &ErrorPair : Src->Errors) + Dst->Errors.push_back(std::move(ErrorPair)); + Src->Errors.clear(); - bool Reported = false; Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { - if (Reported) { - consumeError(std::move(E)); - return; - } - Reported = true; - Dst->Err = std::move(E); + instrprof_error IPE = InstrProfError::take(std::move(E)); + std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock}; + bool firstTime = Dst->WriterErrorCodes.insert(IPE).second; + if (firstTime) + warn(toString(make_error<InstrProfError>(IPE))); }); } @@ -309,12 +294,12 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, - unsigned NumThreads) { + unsigned NumThreads, FailureMode FailMode) { if (OutputFilename.compare("-") == 0) exitWithError("Cannot write indexed profdata format to stdout."); if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && - OutputFormat != PF_Text) + OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) exitWithError("Unknown format is specified."); std::mutex ErrorLock; @@ -328,7 +313,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, // Initialize the writer contexts. SmallVector<std::unique_ptr<WriterContext>, 4> Contexts; for (unsigned I = 0; I < NumThreads; ++I) - Contexts.emplace_back(llvm::make_unique<WriterContext>( + Contexts.emplace_back(std::make_unique<WriterContext>( OutputSparse, ErrorLock, WriterErrorCodes)); if (NumThreads == 1) { @@ -364,23 +349,21 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, } while (Mid > 0); } - // Handle deferred hard errors encountered during merging. + // Handle deferred errors encountered during merging. If the number of errors + // is equal to the number of inputs the merge failed. + unsigned NumErrors = 0; for (std::unique_ptr<WriterContext> &WC : Contexts) { - if (!WC->Err) - continue; - if (!WC->Err.isA<InstrProfError>()) - exitWithError(std::move(WC->Err), WC->ErrWhence); - - instrprof_error IPE = InstrProfError::take(std::move(WC->Err)); - if (isFatalError(IPE)) - exitWithError(make_error<InstrProfError>(IPE), WC->ErrWhence); - else - warn(toString(make_error<InstrProfError>(IPE)), - WC->ErrWhence); + for (auto &ErrorPair : WC->Errors) { + ++NumErrors; + warn(toString(std::move(ErrorPair.first)), ErrorPair.second); + } } + if (NumErrors == Inputs.size() || + (NumErrors > 0 && FailMode == failIfAnyAreInvalid)) + exitWithError("No profiles could be merged."); std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); + raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::OF_None); if (EC) exitWithErrorCode(EC, OutputFilename); @@ -425,21 +408,78 @@ remapSamples(const sampleprof::FunctionSamples &Samples, } static sampleprof::SampleProfileFormat FormatMap[] = { - sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary, - sampleprof::SPF_GCC, sampleprof::SPF_Binary}; + sampleprof::SPF_None, + sampleprof::SPF_Text, + sampleprof::SPF_Compact_Binary, + sampleprof::SPF_Ext_Binary, + sampleprof::SPF_GCC, + sampleprof::SPF_Binary}; + +static std::unique_ptr<MemoryBuffer> +getInputFileBuf(const StringRef &InputFile) { + if (InputFile == "") + return {}; + + auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); + if (!BufOrError) + exitWithErrorCode(BufOrError.getError(), InputFile); + + return std::move(*BufOrError); +} + +static void populateProfileSymbolList(MemoryBuffer *Buffer, + sampleprof::ProfileSymbolList &PSL) { + if (!Buffer) + return; + + SmallVector<StringRef, 32> SymbolVec; + StringRef Data = Buffer->getBuffer(); + Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + + for (StringRef symbol : SymbolVec) + PSL.add(symbol); +} + +static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, + ProfileFormat OutputFormat, + MemoryBuffer *Buffer, + sampleprof::ProfileSymbolList &WriterList, + bool CompressAllSections) { + populateProfileSymbolList(Buffer, WriterList); + if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary) + warn("Profile Symbol list is not empty but the output format is not " + "ExtBinary format. The list will be lost in the output. "); + + Writer.setProfileSymbolList(&WriterList); + + if (CompressAllSections) { + 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(); + } + } +} static void mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, StringRef OutputFilename, - ProfileFormat OutputFormat) { + ProfileFormat OutputFormat, + StringRef ProfileSymbolListFile, + bool CompressAllSections, FailureMode FailMode) { using namespace sampleprof; StringMap<FunctionSamples> ProfileMap; SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; LLVMContext Context; + sampleprof::ProfileSymbolList WriterList; for (const auto &Input : Inputs) { auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); - if (std::error_code EC = ReaderOrErr.getError()) - exitWithErrorCode(EC, Input.Filename); + if (std::error_code EC = ReaderOrErr.getError()) { + warnOrExitGivenError(FailMode, EC, Input.Filename); + continue; + } // We need to keep the readers around until after all the files are // read so that we do not lose the function names stored in each @@ -447,8 +487,11 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs, // merged profile map. Readers.push_back(std::move(ReaderOrErr.get())); const auto Reader = Readers.back().get(); - if (std::error_code EC = Reader->read()) - exitWithErrorCode(EC, Input.Filename); + if (std::error_code EC = Reader->read()) { + warnOrExitGivenError(FailMode, EC, Input.Filename); + Readers.pop_back(); + continue; + } StringMap<FunctionSamples> &Profiles = Reader->getProfiles(); for (StringMap<FunctionSamples>::iterator I = Profiles.begin(), @@ -466,6 +509,11 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs, handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); } } + + std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList = + Reader->getProfileSymbolList(); + if (ReaderList) + WriterList.merge(*ReaderList); } auto WriterOrErr = SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); @@ -473,6 +521,11 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs, exitWithErrorCode(EC, OutputFilename); auto Writer = std::move(WriterOrErr.get()); + // WriterList will have StringRef refering to string in Buffer. + // Make sure Buffer lives as long as WriterList. + auto Buffer = getInputFileBuf(ProfileSymbolListFile); + handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList, + CompressAllSections); Writer->write(ProfileMap); } @@ -487,18 +540,6 @@ static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { return {FileName, Weight}; } -static std::unique_ptr<MemoryBuffer> -getInputFilenamesFileBuf(const StringRef &InputFilenamesFile) { - if (InputFilenamesFile == "") - return {}; - - auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFilenamesFile); - if (!BufOrError) - exitWithErrorCode(BufOrError.getError(), InputFilenamesFile); - - return std::move(*BufOrError); -} - static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) { StringRef Filename = WF.Filename; uint64_t Weight = WF.Weight; @@ -583,12 +624,20 @@ static int merge_main(int argc, const char *argv[]) { clEnumVal(sample, "Sample profile"))); cl::opt<ProfileFormat> OutputFormat( cl::desc("Format of output profile"), cl::init(PF_Binary), - cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), - clEnumValN(PF_Compact_Binary, "compbinary", - "Compact binary encoding"), - clEnumValN(PF_Text, "text", "Text encoding"), - clEnumValN(PF_GCC, "gcc", - "GCC encoding (only meaningful for -sample)"))); + cl::values( + clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), + clEnumValN(PF_Compact_Binary, "compbinary", + "Compact binary encoding"), + clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding"), + clEnumValN(PF_Text, "text", "Text encoding"), + clEnumValN(PF_GCC, "gcc", + "GCC encoding (only meaningful for -sample)"))); + cl::opt<FailureMode> FailureMode( + "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"), + cl::values(clEnumValN(failIfAnyAreInvalid, "any", + "Fail if any profile is invalid."), + clEnumValN(failIfAllAreInvalid, "all", + "Fail only if all profiles are invalid."))); cl::opt<bool> OutputSparse("sparse", cl::init(false), cl::desc("Generate a sparse profile (only meaningful for -instr)")); cl::opt<unsigned> NumThreads( @@ -596,6 +645,14 @@ static int merge_main(int argc, const char *argv[]) { cl::desc("Number of merge threads to use (default: autodetect)")); cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), cl::aliasopt(NumThreads)); + cl::opt<std::string> ProfileSymbolListFile( + "prof-sym-list", cl::init(""), + cl::desc("Path to file containing the list of function symbols " + "used to populate profile symbol list")); + cl::opt<bool> CompressAllSections( + "compress-all-sections", cl::init(false), cl::Hidden, + cl::desc("Compress all sections when writing the profile (only " + "meaningful for -extbinary)")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); @@ -607,7 +664,7 @@ static int merge_main(int argc, const char *argv[]) { // Make sure that the file buffer stays alive for the duration of the // weighted input vector's lifetime. - auto Buffer = getInputFilenamesFileBuf(InputFilenamesFile); + auto Buffer = getInputFileBuf(InputFilenamesFile); parseInputFilenamesFile(Buffer.get(), WeightedInputs); if (WeightedInputs.empty()) @@ -626,10 +683,11 @@ static int merge_main(int argc, const char *argv[]) { if (ProfileKind == instr) mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat, OutputSparse, NumThreads); + OutputFormat, OutputSparse, NumThreads, FailureMode); else mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat); + OutputFormat, ProfileSymbolListFile, CompressAllSections, + FailureMode); return 0; } @@ -644,7 +702,7 @@ static void overlapInstrProfile(const std::string &BaseFilename, WriterContext Context(false, ErrorLock, WriterErrorCodes); WeightedFile WeightedInput{BaseFilename, 1}; OverlapStats Overlap; - Error E = Overlap.accumuateCounts(BaseFilename, TestFilename, IsCS); + Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS); if (E) exitWithError(std::move(E), "Error in getting profile count sums"); if (Overlap.Base.CountSum < 1.0f) { @@ -682,7 +740,7 @@ static int overlap_main(int argc, const char *argv[]) { cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); std::error_code EC; - raw_fd_ostream OS(Output.data(), EC, sys::fs::F_Text); + raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_Text); if (EC) exitWithErrorCode(EC, Output); @@ -944,10 +1002,21 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, return 0; } +static void showSectionInfo(sampleprof::SampleProfileReader *Reader, + raw_fd_ostream &OS) { + if (!Reader->dumpSectionInfo(OS)) { + WithColor::warning() << "-show-sec-info-only is only supported for " + << "sample profile in extbinary format and is " + << "ignored for other formats.\n"; + return; + } +} + static int showSampleProfile(const std::string &Filename, bool ShowCounts, bool ShowAllFunctions, const std::string &ShowFunction, - raw_fd_ostream &OS) { + bool ShowProfileSymbolList, + bool ShowSectionInfoOnly, raw_fd_ostream &OS) { using namespace sampleprof; LLVMContext Context; auto ReaderOrErr = SampleProfileReader::create(Filename, Context); @@ -955,6 +1024,12 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, exitWithErrorCode(EC, Filename); auto Reader = std::move(ReaderOrErr.get()); + + if (ShowSectionInfoOnly) { + showSectionInfo(Reader.get(), OS); + return 0; + } + if (std::error_code EC = Reader->read()) exitWithErrorCode(EC, Filename); @@ -963,6 +1038,12 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, else Reader->dumpFunctionProfile(ShowFunction, OS); + if (ShowProfileSymbolList) { + std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList = + Reader->getProfileSymbolList(); + ReaderList->dump(OS); + } + return 0; } @@ -1015,6 +1096,15 @@ static int show_main(int argc, const char *argv[]) { "list-below-cutoff", cl::init(false), cl::desc("Only output names of functions whose max count values are " "below the cutoff value")); + cl::opt<bool> ShowProfileSymbolList( + "show-prof-sym-list", cl::init(false), + cl::desc("Show profile symbol list if it exists in the profile. ")); + cl::opt<bool> ShowSectionInfoOnly( + "show-sec-info-only", cl::init(false), + cl::desc("Show the information of each section in the sample profile. " + "The flag is only usable when the sample profile is in " + "extbinary format")); + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); if (OutputFilename.empty()) @@ -1027,7 +1117,7 @@ static int show_main(int argc, const char *argv[]) { } std::error_code EC; - raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_Text); if (EC) exitWithErrorCode(EC, OutputFilename); @@ -1042,7 +1132,8 @@ static int show_main(int argc, const char *argv[]) { OnlyListBelow, ShowFunction, TextFormat, OS); else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, - ShowFunction, OS); + ShowFunction, ShowProfileSymbolList, + ShowSectionInfoOnly, OS); } int main(int argc, const char *argv[]) { diff --git a/tools/llvm-readobj/ARMEHABIPrinter.h b/tools/llvm-readobj/ARMEHABIPrinter.h index 11f9d6166a59..2c0912038c31 100644 --- a/tools/llvm-readobj/ARMEHABIPrinter.h +++ b/tools/llvm-readobj/ARMEHABIPrinter.h @@ -329,6 +329,7 @@ class PrinterContext { ScopedPrinter &SW; const object::ELFFile<ET> *ELF; + StringRef FileName; const Elf_Shdr *Symtab; ArrayRef<Elf_Word> ShndxTable; @@ -352,8 +353,8 @@ class PrinterContext { public: PrinterContext(ScopedPrinter &SW, const object::ELFFile<ET> *ELF, - const Elf_Shdr *Symtab) - : SW(SW), ELF(ELF), Symtab(Symtab) {} + StringRef FileName, const Elf_Shdr *Symtab) + : SW(SW), ELF(ELF), FileName(FileName), Symtab(Symtab) {} void PrintUnwindInformation() const; }; @@ -369,10 +370,10 @@ PrinterContext<ET>::FunctionAtAddress(unsigned Section, return readobj_error::unknown_symbol; auto StrTableOrErr = ELF->getStringTableForSymtab(*Symtab); if (!StrTableOrErr) - error(StrTableOrErr.takeError()); + reportError(StrTableOrErr.takeError(), FileName); StringRef StrTable = *StrTableOrErr; - for (const Elf_Sym &Sym : unwrapOrError(ELF->symbols(Symtab))) + for (const Elf_Sym &Sym : unwrapOrError(FileName, ELF->symbols(Symtab))) if (Sym.st_shndx == Section && Sym.st_value == Address && Sym.getType() == ELF::STT_FUNC) { auto NameOrErr = Sym.getName(StrTable); @@ -398,16 +399,16 @@ PrinterContext<ET>::FindExceptionTable(unsigned IndexSectionIndex, /// handling table. Use this symbol to recover the actual exception handling /// table. - for (const Elf_Shdr &Sec : unwrapOrError(ELF->sections())) { + for (const Elf_Shdr &Sec : unwrapOrError(FileName, ELF->sections())) { if (Sec.sh_type != ELF::SHT_REL || Sec.sh_info != IndexSectionIndex) continue; auto SymTabOrErr = ELF->getSection(Sec.sh_link); if (!SymTabOrErr) - error(SymTabOrErr.takeError()); + reportError(SymTabOrErr.takeError(), FileName); const Elf_Shdr *SymTab = *SymTabOrErr; - for (const Elf_Rel &R : unwrapOrError(ELF->rels(&Sec))) { + for (const Elf_Rel &R : unwrapOrError(FileName, ELF->rels(&Sec))) { if (R.r_offset != static_cast<unsigned>(IndexTableOffset)) continue; @@ -417,7 +418,7 @@ PrinterContext<ET>::FindExceptionTable(unsigned IndexSectionIndex, RelA.r_addend = 0; const Elf_Sym *Symbol = - unwrapOrError(ELF->getRelocationSymbol(&RelA, SymTab)); + unwrapOrError(FileName, ELF->getRelocationSymbol(&RelA, SymTab)); auto Ret = ELF->getSection(Symbol, SymTab, ShndxTable); if (!Ret) @@ -570,7 +571,7 @@ void PrinterContext<ET>::PrintUnwindInformation() const { DictScope UI(SW, "UnwindInformation"); int SectionIndex = 0; - for (const Elf_Shdr &Sec : unwrapOrError(ELF->sections())) { + for (const Elf_Shdr &Sec : unwrapOrError(FileName, ELF->sections())) { if (Sec.sh_type == ELF::SHT_ARM_EXIDX) { DictScope UIT(SW, "UnwindIndexTable"); diff --git a/tools/llvm-readobj/ARMWinEHPrinter.cpp b/tools/llvm-readobj/ARMWinEHPrinter.cpp index 4de14e2e78d5..3e026f58871b 100644 --- a/tools/llvm-readobj/ARMWinEHPrinter.cpp +++ b/tools/llvm-readobj/ARMWinEHPrinter.cpp @@ -842,8 +842,10 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, if ((int64_t)(Contents.size() - Offset - 4 * HeaderWords(XData) - (XData.E() ? 0 : XData.EpilogueCount() * 4) - - (XData.X() ? 8 : 0)) < (int64_t)ByteCodeLength) + (XData.X() ? 8 : 0)) < (int64_t)ByteCodeLength) { + SW.flush(); report_fatal_error("Malformed unwind data"); + } if (XData.E()) { ArrayRef<uint8_t> UC = XData.UnwindByteCode(); @@ -1039,10 +1041,7 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF, } FunctionAddress = *FunctionAddressOrErr; } else { - const pe32_header *PEHeader; - if (COFF.getPE32Header(PEHeader)) - return false; - FunctionAddress = PEHeader->ImageBase + RF.BeginAddress; + FunctionAddress = COFF.getPE32Header()->ImageBase + RF.BeginAddress; } SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index 4c2e39dfa3cc..9b2c6adb9d93 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -60,6 +60,10 @@ using namespace llvm::codeview; using namespace llvm::support; using namespace llvm::Win64EH; +static inline Error createError(const Twine &Err) { + return make_error<StringError>(Err, object_error::parse_failed); +} + namespace { struct LoadConfigTables { @@ -167,9 +171,6 @@ private: void printDelayImportedSymbols( const DelayImportDirectoryEntryRef &I, iterator_range<imported_symbol_iterator> Range); - ErrorOr<const coff_resource_dir_entry &> - getResourceDirectoryTableEntry(const coff_resource_dir_table &Table, - uint32_t Index); typedef DenseMap<const coff_section*, std::vector<RelocationRef> > RelocMapTy; @@ -627,14 +628,10 @@ void COFFDumper::printFileHeaders() { // Print PE header. This header does not exist if this is an object file and // not an executable. - const pe32_header *PEHeader = nullptr; - error(Obj->getPE32Header(PEHeader)); - if (PEHeader) + if (const pe32_header *PEHeader = Obj->getPE32Header()) printPEHeader<pe32_header>(PEHeader); - const pe32plus_header *PEPlusHeader = nullptr; - error(Obj->getPE32PlusHeader(PEPlusHeader)); - if (PEPlusHeader) + if (const pe32plus_header *PEPlusHeader = Obj->getPE32PlusHeader()) printPEHeader<pe32plus_header>(PEPlusHeader); if (const dos_header *DH = Obj->getDOSHeader()) @@ -728,7 +725,9 @@ void COFFDumper::printCOFFDebugDirectory() { if (D.Type == COFF::IMAGE_DEBUG_TYPE_CODEVIEW) { const codeview::DebugInfo *DebugInfo; StringRef PDBFileName; - error(Obj->getDebugPDBInfo(&D, DebugInfo, PDBFileName)); + if (std::error_code EC = Obj->getDebugPDBInfo(&D, DebugInfo, PDBFileName)) + reportError(errorCodeToError(EC), Obj->getFileName()); + DictScope PDBScope(W, "PDBInfo"); W.printHex("PDBSignature", DebugInfo->Signature.CVSignature); if (DebugInfo->Signature.CVSignature == OMF::Signature::PDB70) { @@ -740,8 +739,9 @@ void COFFDumper::printCOFFDebugDirectory() { // FIXME: Type values of 12 and 13 are commonly observed but are not in // the documented type enum. Figure out what they mean. ArrayRef<uint8_t> RawData; - error( - Obj->getRvaAndSizeAsBytes(D.AddressOfRawData, D.SizeOfData, RawData)); + if (std::error_code EC = Obj->getRvaAndSizeAsBytes(D.AddressOfRawData, + D.SizeOfData, RawData)) + reportError(errorCodeToError(EC), Obj->getFileName()); W.printBinaryBlock("RawData", RawData); } } @@ -750,8 +750,11 @@ void COFFDumper::printCOFFDebugDirectory() { void COFFDumper::printRVATable(uint64_t TableVA, uint64_t Count, uint64_t EntrySize, PrintExtraCB PrintExtra) { uintptr_t TableStart, TableEnd; - error(Obj->getVaPtr(TableVA, TableStart)); - error(Obj->getVaPtr(TableVA + Count * EntrySize - 1, TableEnd)); + if (std::error_code EC = Obj->getVaPtr(TableVA, TableStart)) + reportError(errorCodeToError(EC), Obj->getFileName()); + if (std::error_code EC = + Obj->getVaPtr(TableVA + Count * EntrySize - 1, TableEnd)) + reportError(errorCodeToError(EC), Obj->getFileName()); TableEnd++; for (uintptr_t I = TableStart; I < TableEnd; I += EntrySize) { uint32_t RVA = *reinterpret_cast<const ulittle32_t *>(I); @@ -887,16 +890,14 @@ void COFFDumper::printBaseOfDataField(const pe32plus_header *) {} void COFFDumper::printCodeViewDebugInfo() { // Print types first to build CVUDTNames, then print symbols. for (const SectionRef &S : Obj->sections()) { - StringRef SectionName; - error(S.getName(SectionName)); + StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName()); // .debug$T is a standard CodeView type section, while .debug$P is the same // format but used for MSVC precompiled header object files. if (SectionName == ".debug$T" || SectionName == ".debug$P") printCodeViewTypeSection(SectionName, S); } for (const SectionRef &S : Obj->sections()) { - StringRef SectionName; - error(S.getName(SectionName)); + StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName()); if (SectionName == ".debug$S") printCodeViewSymbolSection(SectionName, S); } @@ -908,32 +909,40 @@ void COFFDumper::initializeFileAndStringTables(BinaryStreamReader &Reader) { // The section consists of a number of subsection in the following format: // |SubSectionType|SubSectionSize|Contents...| uint32_t SubType, SubSectionSize; - error(Reader.readInteger(SubType)); - error(Reader.readInteger(SubSectionSize)); + + if (Error E = Reader.readInteger(SubType)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = Reader.readInteger(SubSectionSize)) + reportError(std::move(E), Obj->getFileName()); StringRef Contents; - error(Reader.readFixedString(Contents, SubSectionSize)); + if (Error E = Reader.readFixedString(Contents, SubSectionSize)) + reportError(std::move(E), Obj->getFileName()); BinaryStreamRef ST(Contents, support::little); switch (DebugSubsectionKind(SubType)) { case DebugSubsectionKind::FileChecksums: - error(CVFileChecksumTable.initialize(ST)); + if (Error E = CVFileChecksumTable.initialize(ST)) + reportError(std::move(E), Obj->getFileName()); break; case DebugSubsectionKind::StringTable: - error(CVStringTable.initialize(ST)); + if (Error E = CVStringTable.initialize(ST)) + reportError(std::move(E), Obj->getFileName()); break; default: break; } uint32_t PaddedSize = alignTo(SubSectionSize, 4); - error(Reader.skip(PaddedSize - SubSectionSize)); + if (Error E = Reader.skip(PaddedSize - SubSectionSize)) + reportError(std::move(E), Obj->getFileName()); } } void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, const SectionRef &Section) { - StringRef SectionContents = unwrapOrError(Section.getContents()); + StringRef SectionContents = + unwrapOrError(Obj->getFileName(), Section.getContents()); StringRef Data = SectionContents; SmallVector<StringRef, 10> FunctionNames; @@ -944,10 +953,13 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, W.printNumber("Section", SectionName, Obj->getSectionID(Section)); uint32_t Magic; - error(consume(Data, Magic)); + if (Error E = consume(Data, Magic)) + reportError(std::move(E), Obj->getFileName()); + W.printHex("Magic", Magic); if (Magic != COFF::DEBUG_SECTION_MAGIC) - return error(object_error::parse_failed); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); BinaryStreamReader FSReader(Data, support::little); initializeFileAndStringTables(FSReader); @@ -957,8 +969,10 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, // The section consists of a number of subsection in the following format: // |SubSectionType|SubSectionSize|Contents...| uint32_t SubType, SubSectionSize; - error(consume(Data, SubType)); - error(consume(Data, SubSectionSize)); + if (Error E = consume(Data, SubType)) + reportError(std::move(E), Obj->getFileName()); + if (Error E = consume(Data, SubSectionSize)) + reportError(std::move(E), Obj->getFileName()); ListScope S(W, "Subsection"); // Dump the subsection as normal even if the ignore bit is set. @@ -971,7 +985,8 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, // Get the contents of the subsection. if (SubSectionSize > Data.size()) - return error(object_error::parse_failed); + return reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); StringRef Contents = Data.substr(0, SubSectionSize); // Add SubSectionSize to the current offset and align that offset to find @@ -980,7 +995,8 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, size_t NextOffset = SectionOffset + SubSectionSize; NextOffset = alignTo(NextOffset, 4); if (NextOffset > SectionContents.size()) - return error(object_error::parse_failed); + return reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); Data = SectionContents.drop_front(NextOffset); // Optionally print the subsection bytes in case our parsing gets confused @@ -1010,17 +1026,21 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, if (SubSectionSize < 12) { // There should be at least three words to store two function // relocations and size of the code. - error(object_error::parse_failed); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); return; } StringRef LinkageName; - error(resolveSymbolName(Obj->getCOFFSection(Section), SectionOffset, - LinkageName)); + if (std::error_code EC = resolveSymbolName(Obj->getCOFFSection(Section), + SectionOffset, LinkageName)) + reportError(errorCodeToError(EC), Obj->getFileName()); + W.printString("LinkageName", LinkageName); if (FunctionLineTables.count(LinkageName) != 0) { // Saw debug info for this function already? - error(object_error::parse_failed); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); return; } @@ -1033,17 +1053,21 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, BinaryStreamReader SR(Contents, llvm::support::little); DebugFrameDataSubsectionRef FrameData; - error(FrameData.initialize(SR)); + if (Error E = FrameData.initialize(SR)) + reportError(std::move(E), Obj->getFileName()); StringRef LinkageName; - error(resolveSymbolName(Obj->getCOFFSection(Section), SectionContents, - FrameData.getRelocPtr(), LinkageName)); + if (std::error_code EC = + resolveSymbolName(Obj->getCOFFSection(Section), SectionContents, + FrameData.getRelocPtr(), LinkageName)) + reportError(errorCodeToError(EC), Obj->getFileName()); W.printString("LinkageName", LinkageName); // To find the active frame description, search this array for the // smallest PC range that includes the current PC. for (const auto &FD : FrameData) { - StringRef FrameFunc = error(CVStringTable.getString(FD.FrameFunc)); + StringRef FrameFunc = unwrapOrError( + Obj->getFileName(), CVStringTable.getString(FD.FrameFunc)); DictScope S(W, "FrameData"); W.printHex("RvaStart", FD.RvaStart); @@ -1094,7 +1118,8 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, BinaryStreamReader Reader(FunctionLineTables[Name], support::little); DebugLinesSubsectionRef LineInfo; - error(LineInfo.initialize(Reader)); + if (Error E = LineInfo.initialize(Reader)) + reportError(std::move(E), Obj->getFileName()); W.printHex("Flags", LineInfo.header()->Flags); W.printHex("CodeSize", LineInfo.header()->CodeSize); @@ -1105,7 +1130,8 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, uint32_t ColumnIndex = 0; for (const auto &Line : Entry.LineNumbers) { if (Line.Offset >= LineInfo.header()->CodeSize) { - error(object_error::parse_failed); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); return; } @@ -1136,21 +1162,20 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, StringRef SectionContents) { ArrayRef<uint8_t> BinaryData(Subsection.bytes_begin(), Subsection.bytes_end()); - auto CODD = llvm::make_unique<COFFObjectDumpDelegate>(*this, Section, Obj, + auto CODD = std::make_unique<COFFObjectDumpDelegate>(*this, Section, Obj, SectionContents); CVSymbolDumper CVSD(W, Types, CodeViewContainer::ObjectFile, std::move(CODD), CompilationCPUType, opts::CodeViewSubsectionBytes); CVSymbolArray Symbols; BinaryStreamReader Reader(BinaryData, llvm::support::little); - if (auto EC = Reader.readArray(Symbols, Reader.getLength())) { - consumeError(std::move(EC)); + if (Error E = Reader.readArray(Symbols, Reader.getLength())) { W.flush(); - error(object_error::parse_failed); + reportError(std::move(E), Obj->getFileName()); } - if (auto EC = CVSD.dump(Symbols)) { + if (Error E = CVSD.dump(Symbols)) { W.flush(); - error(std::move(EC)); + reportError(std::move(E), Obj->getFileName()); } CompilationCPUType = CVSD.getCompilationCPUType(); W.flush(); @@ -1159,12 +1184,14 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, void COFFDumper::printCodeViewFileChecksums(StringRef Subsection) { BinaryStreamRef Stream(Subsection, llvm::support::little); DebugChecksumsSubsectionRef Checksums; - error(Checksums.initialize(Stream)); + if (Error E = Checksums.initialize(Stream)) + reportError(std::move(E), Obj->getFileName()); for (auto &FC : Checksums) { DictScope S(W, "FileChecksum"); - StringRef Filename = error(CVStringTable.getString(FC.FileNameOffset)); + StringRef Filename = unwrapOrError( + Obj->getFileName(), CVStringTable.getString(FC.FileNameOffset)); W.printHex("Filename", Filename, FC.FileNameOffset); W.printHex("ChecksumSize", FC.Checksum.size()); W.printEnum("ChecksumKind", uint8_t(FC.Kind), @@ -1177,7 +1204,8 @@ void COFFDumper::printCodeViewFileChecksums(StringRef Subsection) { void COFFDumper::printCodeViewInlineeLines(StringRef Subsection) { BinaryStreamReader SR(Subsection, llvm::support::little); DebugInlineeLinesSubsectionRef Lines; - error(Lines.initialize(SR)); + if (Error E = Lines.initialize(SR)) + reportError(std::move(E), Obj->getFileName()); for (auto &Line : Lines) { DictScope S(W, "InlineeSourceLine"); @@ -1198,15 +1226,18 @@ void COFFDumper::printCodeViewInlineeLines(StringRef Subsection) { StringRef COFFDumper::getFileNameForFileOffset(uint32_t FileOffset) { // The file checksum subsection should precede all references to it. if (!CVFileChecksumTable.valid() || !CVStringTable.valid()) - error(object_error::parse_failed); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); auto Iter = CVFileChecksumTable.getArray().at(FileOffset); // Check if the file checksum table offset is valid. if (Iter == CVFileChecksumTable.end()) - error(object_error::parse_failed); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); - return error(CVStringTable.getString(Iter->FileNameOffset)); + return unwrapOrError(Obj->getFileName(), + CVStringTable.getString(Iter->FileNameOffset)); } void COFFDumper::printFileNameForOffset(StringRef Label, uint32_t FileOffset) { @@ -1219,35 +1250,38 @@ void COFFDumper::mergeCodeViewTypes(MergingTypeTableBuilder &CVIDs, GlobalTypeTableBuilder &GlobalCVTypes, bool GHash) { for (const SectionRef &S : Obj->sections()) { - StringRef SectionName; - error(S.getName(SectionName)); + StringRef SectionName = unwrapOrError(Obj->getFileName(), S.getName()); if (SectionName == ".debug$T") { - StringRef Data = unwrapOrError(S.getContents()); + StringRef Data = unwrapOrError(Obj->getFileName(), S.getContents()); uint32_t Magic; - error(consume(Data, Magic)); + if (Error E = consume(Data, Magic)) + reportError(std::move(E), Obj->getFileName()); + if (Magic != 4) - error(object_error::parse_failed); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); CVTypeArray Types; BinaryStreamReader Reader(Data, llvm::support::little); if (auto EC = Reader.readArray(Types, Reader.getLength())) { consumeError(std::move(EC)); W.flush(); - error(object_error::parse_failed); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); } SmallVector<TypeIndex, 128> SourceToDest; Optional<uint32_t> PCHSignature; if (GHash) { std::vector<GloballyHashedType> Hashes = GloballyHashedType::hashTypes(Types); - if (auto EC = + if (Error E = mergeTypeAndIdRecords(GlobalCVIDs, GlobalCVTypes, SourceToDest, Types, Hashes, PCHSignature)) - return error(std::move(EC)); + return reportError(std::move(E), Obj->getFileName()); } else { - if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types, + if (Error E = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types, PCHSignature)) - return error(std::move(EC)); + return reportError(std::move(E), Obj->getFileName()); } } } @@ -1258,20 +1292,25 @@ void COFFDumper::printCodeViewTypeSection(StringRef SectionName, ListScope D(W, "CodeViewTypes"); W.printNumber("Section", SectionName, Obj->getSectionID(Section)); - StringRef Data = unwrapOrError(Section.getContents()); + StringRef Data = unwrapOrError(Obj->getFileName(), Section.getContents()); if (opts::CodeViewSubsectionBytes) W.printBinaryBlock("Data", Data); uint32_t Magic; - error(consume(Data, Magic)); + if (Error E = consume(Data, Magic)) + reportError(std::move(E), Obj->getFileName()); + W.printHex("Magic", Magic); if (Magic != COFF::DEBUG_SECTION_MAGIC) - return error(object_error::parse_failed); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); Types.reset(Data, 100); TypeDumpVisitor TDV(Types, &W, opts::CodeViewSubsectionBytes); - error(codeview::visitTypeStream(Types, TDV)); + if (Error E = codeview::visitTypeStream(Types, TDV)) + reportError(std::move(E), Obj->getFileName()); + W.flush(); } @@ -1282,8 +1321,7 @@ void COFFDumper::printSectionHeaders() { ++SectionNumber; const coff_section *Section = Obj->getCOFFSection(Sec); - StringRef Name; - error(Sec.getName(Name)); + StringRef Name = unwrapOrError(Obj->getFileName(), Sec.getName()); DictScope D(W, "Section"); W.printNumber("Number", SectionNumber); @@ -1318,7 +1356,7 @@ void COFFDumper::printSectionHeaders() { if (opts::SectionData && !(Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)) { - StringRef Data = unwrapOrError(Sec.getContents()); + StringRef Data = unwrapOrError(Obj->getFileName(), Sec.getContents()); W.printBinaryBlock("SectionData", Data); } } @@ -1330,8 +1368,7 @@ void COFFDumper::printRelocations() { int SectionNumber = 0; for (const SectionRef &Section : Obj->sections()) { ++SectionNumber; - StringRef Name; - error(Section.getName(Name)); + StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); bool PrintedGroup = false; for (const RelocationRef &Reloc : Section.relocations()) { @@ -1362,7 +1399,9 @@ void COFFDumper::printRelocation(const SectionRef &Section, int64_t SymbolIndex = -1; if (Symbol != Obj->symbol_end()) { Expected<StringRef> SymbolNameOrErr = Symbol->getName(); - error(errorToErrorCode(SymbolNameOrErr.takeError())); + if (!SymbolNameOrErr) + reportError(SymbolNameOrErr.takeError(), Obj->getFileName()); + SymbolName = *SymbolNameOrErr; SymbolIndex = Obj->getSymbolIndex(Obj->getCOFFSymbol(*Symbol)); } @@ -1439,7 +1478,8 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { for (uint8_t I = 0; I < Symbol.getNumberOfAuxSymbols(); ++I) { if (Symbol.isFunctionDefinition()) { const coff_aux_function_definition *Aux; - error(getSymbolAuxData(Obj, Symbol, I, Aux)); + if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux)) + reportError(errorCodeToError(EC), Obj->getFileName()); DictScope AS(W, "AuxFunctionDef"); W.printNumber("TagIndex", Aux->TagIndex); @@ -1449,15 +1489,16 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { } else if (Symbol.isAnyUndefined()) { const coff_aux_weak_external *Aux; - error(getSymbolAuxData(Obj, Symbol, I, Aux)); + 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; - std::error_code EC = errorToErrorCode(Linked.takeError()); - if (EC || (EC = Obj->getSymbolName(*Linked, LinkedName))) { - LinkedName = ""; - error(EC); - } + if (std::error_code EC = Obj->getSymbolName(*Linked, LinkedName)) + reportError(errorCodeToError(EC), Obj->getFileName()); DictScope AS(W, "AuxWeakExternal"); W.printNumber("Linked", LinkedName, Aux->TagIndex); @@ -1466,8 +1507,8 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { } else if (Symbol.isFileRecord()) { const char *FileName; - error(getSymbolAuxData(Obj, Symbol, I, FileName)); - + if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, FileName)) + reportError(errorCodeToError(EC), Obj->getFileName()); DictScope AS(W, "AuxFileRecord"); StringRef Name(FileName, Symbol.getNumberOfAuxSymbols() * @@ -1476,7 +1517,8 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { break; } else if (Symbol.isSectionDefinition()) { const coff_aux_section_definition *Aux; - error(getSymbolAuxData(Obj, Symbol, I, Aux)); + if (std::error_code EC = getSymbolAuxData(Obj, Symbol, I, Aux)) + reportError(errorCodeToError(EC), Obj->getFileName()); int32_t AuxNumber = Aux->getNumber(Symbol.isBigObj()); @@ -1493,26 +1535,27 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { const coff_section *Assoc; StringRef AssocName = ""; if (std::error_code EC = Obj->getSection(AuxNumber, Assoc)) - error(EC); + reportError(errorCodeToError(EC), Obj->getFileName()); Expected<StringRef> Res = getSectionName(Obj, AuxNumber, Assoc); if (!Res) - error(Res.takeError()); + reportError(Res.takeError(), Obj->getFileName()); AssocName = *Res; W.printNumber("AssocSection", AssocName, AuxNumber); } } else if (Symbol.isCLRToken()) { const coff_aux_clr_token *Aux; - error(getSymbolAuxData(Obj, Symbol, I, 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; - std::error_code EC = errorToErrorCode(ReferredSym.takeError()); - if (EC || (EC = Obj->getSymbolName(*ReferredSym, ReferredName))) { - ReferredName = ""; - error(EC); - } + if (std::error_code EC = Obj->getSymbolName(*ReferredSym, ReferredName)) + reportError(errorCodeToError(EC), Obj->getFileName()); DictScope AS(W, "AuxCLRToken"); W.printNumber("AuxType", Aux->AuxType); @@ -1578,9 +1621,11 @@ void COFFDumper::printImportedSymbols( iterator_range<imported_symbol_iterator> Range) { for (const ImportedSymbolRef &I : Range) { StringRef Sym; - error(I.getSymbolName(Sym)); + if (std::error_code EC = I.getSymbolName(Sym)) + reportError(errorCodeToError(EC), Obj->getFileName()); uint16_t Ordinal; - error(I.getOrdinal(Ordinal)); + if (std::error_code EC = I.getOrdinal(Ordinal)) + reportError(errorCodeToError(EC), Obj->getFileName()); W.printNumber("Symbol", Sym, Ordinal); } } @@ -1592,12 +1637,17 @@ void COFFDumper::printDelayImportedSymbols( for (const ImportedSymbolRef &S : Range) { DictScope Import(W, "Import"); StringRef Sym; - error(S.getSymbolName(Sym)); + if (std::error_code EC = S.getSymbolName(Sym)) + reportError(errorCodeToError(EC), Obj->getFileName()); + uint16_t Ordinal; - error(S.getOrdinal(Ordinal)); + if (std::error_code EC = S.getOrdinal(Ordinal)) + reportError(errorCodeToError(EC), Obj->getFileName()); W.printNumber("Symbol", Sym, Ordinal); + uint64_t Addr; - error(I.getImportAddress(Index++, Addr)); + if (std::error_code EC = I.getImportAddress(Index++, Addr)) + reportError(errorCodeToError(EC), Obj->getFileName()); W.printHex("Address", Addr); } } @@ -1607,13 +1657,16 @@ void COFFDumper::printCOFFImports() { for (const ImportDirectoryEntryRef &I : Obj->import_directories()) { DictScope Import(W, "Import"); StringRef Name; - error(I.getName(Name)); + if (std::error_code EC = I.getName(Name)) + reportError(errorCodeToError(EC), Obj->getFileName()); W.printString("Name", Name); uint32_t ILTAddr; - error(I.getImportLookupTableRVA(ILTAddr)); + if (std::error_code EC = I.getImportLookupTableRVA(ILTAddr)) + reportError(errorCodeToError(EC), Obj->getFileName()); W.printHex("ImportLookupTableRVA", ILTAddr); uint32_t IATAddr; - error(I.getImportAddressTableRVA(IATAddr)); + if (std::error_code EC = I.getImportAddressTableRVA(IATAddr)) + reportError(errorCodeToError(EC), 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. @@ -1627,10 +1680,12 @@ void COFFDumper::printCOFFImports() { for (const DelayImportDirectoryEntryRef &I : Obj->delay_import_directories()) { DictScope Import(W, "DelayImport"); StringRef Name; - error(I.getName(Name)); + if (std::error_code EC = I.getName(Name)) + reportError(errorCodeToError(EC), Obj->getFileName()); W.printString("Name", Name); const delay_import_directory_table_entry *Table; - error(I.getDelayImportTable(Table)); + if (std::error_code EC = I.getDelayImportTable(Table)) + reportError(errorCodeToError(EC), Obj->getFileName()); W.printHex("Attributes", Table->Attributes); W.printHex("ModuleHandle", Table->ModuleHandle); W.printHex("ImportAddressTable", Table->DelayImportAddressTable); @@ -1648,9 +1703,12 @@ void COFFDumper::printCOFFExports() { StringRef Name; uint32_t Ordinal, RVA; - error(E.getSymbolName(Name)); - error(E.getOrdinal(Ordinal)); - error(E.getExportRVA(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()); W.printNumber("Ordinal", Ordinal); W.printString("Name", Name); @@ -1660,13 +1718,12 @@ void COFFDumper::printCOFFExports() { void COFFDumper::printCOFFDirectives() { for (const SectionRef &Section : Obj->sections()) { - StringRef Name; - - error(Section.getName(Name)); + StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); if (Name != ".drectve") continue; - StringRef Contents = unwrapOrError(Section.getContents()); + StringRef Contents = + unwrapOrError(Obj->getFileName(), Section.getContents()); W.printString("Directive(s)", Contents); } } @@ -1689,8 +1746,10 @@ void COFFDumper::printCOFFBaseReloc() { for (const BaseRelocRef &I : Obj->base_relocs()) { uint8_t Type; uint32_t RVA; - error(I.getRVA(RVA)); - error(I.getType(Type)); + 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()); DictScope Import(W, "Entry"); W.printString("Type", getBaseRelocTypeName(Type)); W.printHex("Address", RVA); @@ -1700,16 +1759,18 @@ void COFFDumper::printCOFFBaseReloc() { void COFFDumper::printCOFFResources() { ListScope ResourcesD(W, "Resources"); for (const SectionRef &S : Obj->sections()) { - StringRef Name; - error(S.getName(Name)); + StringRef Name = unwrapOrError(Obj->getFileName(), S.getName()); if (!Name.startswith(".rsrc")) continue; - StringRef Ref = unwrapOrError(S.getContents()); + StringRef Ref = unwrapOrError(Obj->getFileName(), S.getContents()); if ((Name == ".rsrc") || (Name == ".rsrc$01")) { - ResourceSectionRef RSF(Ref); - auto &BaseTable = unwrapOrError(RSF.getBaseTable()); + ResourceSectionRef RSF; + Error E = RSF.load(Obj, S); + if (E) + reportError(std::move(E), Obj->getFileName()); + auto &BaseTable = unwrapOrError(Obj->getFileName(), RSF.getBaseTable()); W.printNumber("Total Number of Resources", countTotalTableEntries(RSF, BaseTable, "Type")); W.printHex("Base Table Address", @@ -1729,14 +1790,15 @@ COFFDumper::countTotalTableEntries(ResourceSectionRef RSF, uint32_t TotalEntries = 0; for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; i++) { - auto Entry = unwrapOrError(getResourceDirectoryTableEntry(Table, i)); + auto Entry = unwrapOrError(Obj->getFileName(), RSF.getTableEntry(Table, i)); if (Entry.Offset.isSubDir()) { StringRef NextLevel; if (Level == "Name") NextLevel = "Language"; else NextLevel = "Name"; - auto &NextTable = unwrapOrError(RSF.getEntrySubDir(Entry)); + auto &NextTable = + unwrapOrError(Obj->getFileName(), RSF.getEntrySubDir(Entry)); TotalEntries += countTotalTableEntries(RSF, NextTable, NextLevel); } else { TotalEntries += 1; @@ -1755,13 +1817,13 @@ void COFFDumper::printResourceDirectoryTable( // Iterate through level in resource directory tree. for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; i++) { - auto Entry = unwrapOrError(getResourceDirectoryTableEntry(Table, i)); + auto Entry = unwrapOrError(Obj->getFileName(), RSF.getTableEntry(Table, i)); StringRef Name; SmallString<20> IDStr; raw_svector_ostream OS(IDStr); if (i < Table.NumberOfNameEntries) { ArrayRef<UTF16> RawEntryNameString = - unwrapOrError(RSF.getEntryNameString(Entry)); + unwrapOrError(Obj->getFileName(), RSF.getEntryNameString(Entry)); std::vector<UTF16> EndianCorrectedNameString; if (llvm::sys::IsBigEndianHost) { EndianCorrectedNameString.resize(RawEntryNameString.size() + 1); @@ -1772,14 +1834,14 @@ void COFFDumper::printResourceDirectoryTable( } std::string EntryNameString; if (!llvm::convertUTF16ToUTF8String(RawEntryNameString, EntryNameString)) - error(object_error::parse_failed); + reportError(errorCodeToError(object_error::parse_failed), + Obj->getFileName()); OS << ": "; OS << EntryNameString; } else { if (Level == "Type") { OS << ": "; printResourceTypeName(Entry.Identifier.ID, OS); - IDStr = IDStr.slice(0, IDStr.find_first_of(")", 0) + 1); } else { OS << ": (ID " << Entry.Identifier.ID << ")"; } @@ -1793,7 +1855,8 @@ void COFFDumper::printResourceDirectoryTable( NextLevel = "Language"; else NextLevel = "Name"; - auto &NextTable = unwrapOrError(RSF.getEntrySubDir(Entry)); + auto &NextTable = + unwrapOrError(Obj->getFileName(), RSF.getEntrySubDir(Entry)); printResourceDirectoryTable(RSF, NextTable, NextLevel); } else { W.printHex("Entry Offset", Entry.Offset.value()); @@ -1804,24 +1867,29 @@ void COFFDumper::printResourceDirectoryTable( W.printNumber("Major Version", Table.MajorVersion); W.printNumber("Minor Version", Table.MinorVersion); W.printNumber("Characteristics", Table.Characteristics); + ListScope DataScope(W, "Data"); + auto &DataEntry = + unwrapOrError(Obj->getFileName(), RSF.getEntryData(Entry)); + W.printHex("DataRVA", DataEntry.DataRVA); + W.printNumber("DataSize", DataEntry.DataSize); + W.printNumber("Codepage", DataEntry.Codepage); + W.printNumber("Reserved", DataEntry.Reserved); + StringRef Contents = + unwrapOrError(Obj->getFileName(), RSF.getContents(DataEntry)); + W.printBinaryBlock("Data", Contents); } } } -ErrorOr<const coff_resource_dir_entry &> -COFFDumper::getResourceDirectoryTableEntry(const coff_resource_dir_table &Table, - uint32_t Index) { - if (Index >= (uint32_t)(Table.NumberOfNameEntries + Table.NumberOfIDEntries)) - return object_error::parse_failed; - auto TablePtr = reinterpret_cast<const coff_resource_dir_entry *>(&Table + 1); - return TablePtr[Index]; -} - void COFFDumper::printStackMap() const { object::SectionRef StackMapSection; for (auto Sec : Obj->sections()) { StringRef Name; - Sec.getName(Name); + if (Expected<StringRef> NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + if (Name == ".llvm_stackmaps") { StackMapSection = Sec; break; @@ -1831,7 +1899,8 @@ void COFFDumper::printStackMap() const { if (StackMapSection == object::SectionRef()) return; - StringRef StackMapContents = unwrapOrError(StackMapSection.getContents()); + StringRef StackMapContents = + unwrapOrError(Obj->getFileName(), StackMapSection.getContents()); ArrayRef<uint8_t> StackMapContentsArray = arrayRefFromStringRef(StackMapContents); @@ -1847,7 +1916,11 @@ void COFFDumper::printAddrsig() { object::SectionRef AddrsigSection; for (auto Sec : Obj->sections()) { StringRef Name; - Sec.getName(Name); + if (Expected<StringRef> NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + if (Name == ".llvm_addrsig") { AddrsigSection = Sec; break; @@ -1857,7 +1930,8 @@ void COFFDumper::printAddrsig() { if (AddrsigSection == object::SectionRef()) return; - StringRef AddrsigContents = unwrapOrError(AddrsigSection.getContents()); + StringRef AddrsigContents = + unwrapOrError(Obj->getFileName(), AddrsigSection.getContents()); ArrayRef<uint8_t> AddrsigContentsArray(AddrsigContents.bytes_begin(), AddrsigContents.size()); @@ -1869,15 +1943,15 @@ void COFFDumper::printAddrsig() { const char *Err; uint64_t SymIndex = decodeULEB128(Cur, &Size, End, &Err); if (Err) - reportError(Err); + reportError(createError(Err), Obj->getFileName()); Expected<COFFSymbolRef> Sym = Obj->getSymbol(SymIndex); + if (!Sym) + reportError(Sym.takeError(), Obj->getFileName()); + StringRef SymName; - std::error_code EC = errorToErrorCode(Sym.takeError()); - if (EC || (EC = Obj->getSymbolName(*Sym, SymName))) { - SymName = ""; - error(EC); - } + if (std::error_code EC = Obj->getSymbolName(*Sym, SymName)) + reportError(errorCodeToError(EC), Obj->getFileName()); W.printNumber("Sym", SymName, SymIndex); Cur += Size; @@ -1891,7 +1965,8 @@ void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer, { ListScope S(Writer, "MergedTypeStream"); TypeDumpVisitor TDV(TpiTypes, &Writer, opts::CodeViewSubsectionBytes); - error(codeview::visitTypeStream(TpiTypes, TDV)); + if (Error Err = codeview::visitTypeStream(TpiTypes, TDV)) + reportError(std::move(Err), "<?>"); Writer.flush(); } @@ -1902,7 +1977,8 @@ void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer, ListScope S(Writer, "MergedIDStream"); TypeDumpVisitor TDV(TpiTypes, &Writer, opts::CodeViewSubsectionBytes); TDV.setIpiTypes(IpiTypes); - error(codeview::visitTypeStream(IpiTypes, TDV)); + if (Error Err = codeview::visitTypeStream(IpiTypes, TDV)) + reportError(std::move(Err), "<?>"); Writer.flush(); } } diff --git a/tools/llvm-readobj/DwarfCFIEHPrinter.h b/tools/llvm-readobj/DwarfCFIEHPrinter.h index 7055510ef2f2..0a365d4fe72a 100644 --- a/tools/llvm-readobj/DwarfCFIEHPrinter.h +++ b/tools/llvm-readobj/DwarfCFIEHPrinter.h @@ -44,12 +44,12 @@ public: void printUnwindInformation() const; }; -template <class ELFO> -static const typename ELFO::Elf_Shdr *findSectionByAddress(const ELFO *Obj, - uint64_t Addr) { - auto Sections = Obj->sections(); +template <class ELFT> +static const typename object::ELFObjectFile<ELFT>::Elf_Shdr * +findSectionByAddress(const object::ELFObjectFile<ELFT> *ObjF, uint64_t Addr) { + auto Sections = ObjF->getELFFile()->sections(); if (Error E = Sections.takeError()) - reportError(toString(std::move(E))); + reportError(std::move(E), ObjF->getFileName()); for (const auto &Shdr : *Sections) if (Shdr.sh_addr == Addr) @@ -64,13 +64,15 @@ void PrinterContext<ELFT>::printUnwindInformation() const { auto PHs = Obj->program_headers(); if (Error E = PHs.takeError()) - reportError(toString(std::move(E))); + 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("p_memsz does not match p_filesz for GNU_EH_FRAME"); + reportError(object::createError( + "p_memsz does not match p_filesz for GNU_EH_FRAME"), + ObjF->getFileName()); break; } } @@ -81,12 +83,12 @@ void PrinterContext<ELFT>::printUnwindInformation() const { auto Sections = Obj->sections(); if (Error E = Sections.takeError()) - reportError(toString(std::move(E))); + reportError(std::move(E), ObjF->getFileName()); for (const auto &Shdr : *Sections) { auto SectionName = Obj->getSectionName(&Shdr); if (Error E = SectionName.takeError()) - reportError(toString(std::move(E))); + reportError(std::move(E), ObjF->getFileName()); if (*SectionName == ".eh_frame") printEHFrame(&Shdr); @@ -97,49 +99,52 @@ template <typename ELFT> void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset, uint64_t EHFrameHdrAddress, uint64_t EHFrameHdrSize) const { - ListScope L(W, "EH_FRAME Header"); + DictScope L(W, "EHFrameHeader"); W.startLine() << format("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress); W.startLine() << format("Offset: 0x%" PRIx64 "\n", EHFrameHdrOffset); W.startLine() << format("Size: 0x%" PRIx64 "\n", EHFrameHdrSize); const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); - const auto *EHFrameHdrShdr = findSectionByAddress(Obj, EHFrameHdrAddress); + const auto *EHFrameHdrShdr = findSectionByAddress(ObjF, EHFrameHdrAddress); if (EHFrameHdrShdr) { auto SectionName = Obj->getSectionName(EHFrameHdrShdr); if (Error E = SectionName.takeError()) - reportError(toString(std::move(E))); + reportError(std::move(E), ObjF->getFileName()); W.printString("Corresponding Section", *SectionName); } - DataExtractor DE( - StringRef(reinterpret_cast<const char *>(Obj->base()) + EHFrameHdrOffset, - EHFrameHdrSize), - ELFT::TargetEndianness == support::endianness::little, - ELFT::Is64Bits ? 8 : 4); + DataExtractor DE(makeArrayRef(Obj->base() + EHFrameHdrOffset, EHFrameHdrSize), + ELFT::TargetEndianness == support::endianness::little, + ELFT::Is64Bits ? 8 : 4); DictScope D(W, "Header"); - uint32_t Offset = 0; + uint64_t Offset = 0; auto Version = DE.getU8(&Offset); W.printNumber("version", Version); if (Version != 1) - reportError("only version 1 of .eh_frame_hdr is supported"); + reportError( + object::createError("only version 1 of .eh_frame_hdr is supported"), + ObjF->getFileName()); uint64_t EHFramePtrEnc = DE.getU8(&Offset); W.startLine() << format("eh_frame_ptr_enc: 0x%" PRIx64 "\n", EHFramePtrEnc); if (EHFramePtrEnc != (dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4)) - reportError("unexpected encoding eh_frame_ptr_enc"); + reportError(object::createError("unexpected encoding eh_frame_ptr_enc"), + ObjF->getFileName()); uint64_t FDECountEnc = DE.getU8(&Offset); W.startLine() << format("fde_count_enc: 0x%" PRIx64 "\n", FDECountEnc); if (FDECountEnc != dwarf::DW_EH_PE_udata4) - reportError("unexpected encoding fde_count_enc"); + reportError(object::createError("unexpected encoding fde_count_enc"), + ObjF->getFileName()); uint64_t TableEnc = DE.getU8(&Offset); W.startLine() << format("table_enc: 0x%" PRIx64 "\n", TableEnc); if (TableEnc != (dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4)) - reportError("unexpected encoding table_enc"); + reportError(object::createError("unexpected encoding table_enc"), + ObjF->getFileName()); auto EHFramePtr = DE.getSigned(&Offset, 4) + EHFrameHdrAddress + 4; W.startLine() << format("eh_frame_ptr: 0x%" PRIx64 "\n", EHFramePtr); @@ -158,7 +163,8 @@ void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset, W.startLine() << format("address: 0x%" PRIx64 "\n", Address); if (InitialPC < PrevPC) - reportError("initial_location is out of order"); + reportError(object::createError("initial_location is out of order"), + ObjF->getFileName()); PrevPC = InitialPC; ++NumEntries; @@ -178,7 +184,7 @@ void PrinterContext<ELFT>::printEHFrame( const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); auto Result = Obj->getSectionContents(EHFrameShdr); if (Error E = Result.takeError()) - reportError(toString(std::move(E))); + reportError(std::move(E), ObjF->getFileName()); auto Contents = Result.get(); DWARFDataExtractor DE( diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index 4e1cb7d544e7..57144882c4b4 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/STLExtras.h" @@ -36,6 +37,7 @@ #include "llvm/Object/ELFTypes.h" #include "llvm/Object/Error.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/RelocationResolver.h" #include "llvm/Object/StackMapParser.h" #include "llvm/Support/AMDGPUMetadata.h" #include "llvm/Support/ARMAttributeParser.h" @@ -61,6 +63,7 @@ #include <memory> #include <string> #include <system_error> +#include <unordered_set> #include <vector> using namespace llvm; @@ -119,9 +122,9 @@ template <class ELFT> class DumpStyle; /// the size, entity size and virtual address are different entries in arbitrary /// order (DT_REL, DT_RELSZ, DT_RELENT for example). struct DynRegionInfo { - DynRegionInfo() = default; - DynRegionInfo(const void *A, uint64_t S, uint64_t ES) - : Addr(A), Size(S), EntSize(ES) {} + DynRegionInfo(StringRef ObjName) : FileName(ObjName) {} + DynRegionInfo(const void *A, uint64_t S, uint64_t ES, StringRef ObjName) + : Addr(A), Size(S), EntSize(ES), FileName(ObjName) {} /// Address in current address space. const void *Addr = nullptr; @@ -130,14 +133,18 @@ struct DynRegionInfo { /// Size of each entity in the region. uint64_t EntSize = 0; + /// Name of the file. Used for error reporting. + StringRef FileName; + 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("invalid section size (" + Twine(Size) + - ") or entity size (" + Twine(EntSize) + ")"); + reportWarning(createError("invalid section size (" + Twine(Size) + + ") or entity size (" + Twine(EntSize) + ")"), + FileName); return {Start, Start}; } return {Start, Start + (Size / EntSize)}; @@ -166,11 +173,7 @@ public: void printVersionInfo() override; void printGroupSections() override; - void printAttributes() override; - void printMipsPLTGOT() override; - void printMipsABIFlags() override; - void printMipsReginfo() override; - void printMipsOptions() override; + void printArchSpecificInfo() override; void printStackMap() const override; @@ -182,6 +185,7 @@ public: void printNotes() override; void printELFLinkerOptions() override; + void printStackSizes() override; const object::ELFObjectFile<ELFT> *getElfObject() const { return ObjF; }; @@ -195,20 +199,27 @@ private: if (DRI.Addr < Obj->base() || reinterpret_cast<const uint8_t *>(DRI.Addr) + DRI.Size > Obj->base() + Obj->getBufSize()) - error(llvm::object::object_error::parse_failed); + reportError(errorCodeToError(llvm::object::object_error::parse_failed), + ObjF->getFileName()); return DRI; } DynRegionInfo createDRIFrom(const Elf_Phdr *P, uintX_t EntSize) { - return checkDRI( - {ObjF->getELFFile()->base() + P->p_offset, P->p_filesz, EntSize}); + return checkDRI({ObjF->getELFFile()->base() + P->p_offset, P->p_filesz, + EntSize, ObjF->getFileName()}); } DynRegionInfo createDRIFrom(const Elf_Shdr *S) { - return checkDRI( - {ObjF->getELFFile()->base() + S->sh_offset, S->sh_size, S->sh_entsize}); + return checkDRI({ObjF->getELFFile()->base() + S->sh_offset, S->sh_size, + S->sh_entsize, ObjF->getFileName()}); } + void printAttributes(); + void printMipsReginfo(); + void printMipsOptions(); + + std::pair<const Elf_Phdr *, const Elf_Shdr *> + findDynamic(const ELFFile<ELFT> *Obj); void loadDynamicTable(const ELFFile<ELFT> *Obj); void parseDynamicTable(); @@ -226,7 +237,7 @@ private: DynRegionInfo DynSymRegion; DynRegionInfo DynamicTable; StringRef DynamicStringTable; - StringRef SOName = "<Not found>"; + std::string SOName = "<Not found>"; const Elf_Hash *HashTable = nullptr; const Elf_GnuHash *GnuHashTable = nullptr; const Elf_Shdr *DotSymtabSec = nullptr; @@ -291,7 +302,8 @@ public: void getSectionNameIndex(const Elf_Sym *Symbol, const Elf_Sym *FirstSym, StringRef &SectionName, unsigned &SectionIndex) const; - std::string getStaticSymbolName(uint32_t Index) const; + Expected<std::string> getStaticSymbolName(uint32_t Index) const; + std::string getDynamicString(uint64_t Value) const; StringRef getSymbolVersionByIndex(StringRef StrTab, uint32_t VersionSymbolIndex, bool &IsDefault) const; @@ -328,16 +340,27 @@ void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const { } else { if (!DotSymtabSec) return; - StrTable = unwrapOrError(Obj->getStringTableForSymtab(*DotSymtabSec)); - Syms = unwrapOrError(Obj->symbols(DotSymtabSec)); - SymtabName = unwrapOrError(Obj->getSectionName(DotSymtabSec)); + StrTable = unwrapOrError(ObjF->getFileName(), + Obj->getStringTableForSymtab(*DotSymtabSec)); + Syms = unwrapOrError(ObjF->getFileName(), Obj->symbols(DotSymtabSec)); + SymtabName = + unwrapOrError(ObjF->getFileName(), Obj->getSectionName(DotSymtabSec)); Entries = DotSymtabSec->getEntityCount(); } if (Syms.begin() == Syms.end()) return; - ELFDumperStyle->printSymtabMessage(Obj, SymtabName, Entries); + + // The st_other field has 2 logical parts. The first two bits hold the symbol + // visibility (STV_*) and the remainder hold other platform-specific values. + bool NonVisibilityBitsUsed = llvm::find_if(Syms, [](const Elf_Sym &S) { + return S.st_other & ~0x3; + }) != Syms.end(); + + ELFDumperStyle->printSymtabMessage(Obj, SymtabName, Entries, + NonVisibilityBitsUsed); for (const auto &Sym : Syms) - ELFDumperStyle->printSymbol(Obj, &Sym, Syms.begin(), StrTable, IsDynamic); + ELFDumperStyle->printSymbol(Obj, &Sym, Syms.begin(), StrTable, IsDynamic, + NonVisibilityBitsUsed); } template <class ELFT> class MipsGOTParser; @@ -346,8 +369,20 @@ template <typename ELFT> class DumpStyle { public: using Elf_Shdr = typename ELFT::Shdr; using Elf_Sym = typename ELFT::Sym; + using Elf_Addr = typename ELFT::Addr; + + 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(); + }; + } - DumpStyle(ELFDumper<ELFT> *Dumper) : Dumper(Dumper) {} virtual ~DumpStyle() = default; virtual void printFileHeaders(const ELFFile<ELFT> *Obj) = 0; @@ -360,10 +395,10 @@ public: virtual void printDynamic(const ELFFile<ELFT> *Obj) {} virtual void printDynamicRelocations(const ELFFile<ELFT> *Obj) = 0; virtual void printSymtabMessage(const ELFFile<ELFT> *Obj, StringRef Name, - size_t Offset) {} + size_t Offset, bool NonVisibilityBitsUsed) {} virtual void printSymbol(const ELFFile<ELFT> *Obj, const Elf_Sym *Symbol, const Elf_Sym *FirstSym, StringRef StrTable, - bool IsDynamic) = 0; + bool IsDynamic, bool NonVisibilityBitsUsed) = 0; virtual void printProgramHeaders(const ELFFile<ELFT> *Obj, bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) = 0; @@ -378,11 +413,31 @@ public: virtual void printAddrsig(const ELFFile<ELFT> *Obj) = 0; virtual void printNotes(const ELFFile<ELFT> *Obj) = 0; virtual void printELFLinkerOptions(const ELFFile<ELFT> *Obj) = 0; + virtual void printStackSizes(const ELFObjectFile<ELFT> *Obj) = 0; + void printNonRelocatableStackSizes(const ELFObjectFile<ELFT> *Obj, + std::function<void()> PrintHeader); + void printRelocatableStackSizes(const ELFObjectFile<ELFT> *Obj, + std::function<void()> PrintHeader); + void printFunctionStackSize(const ELFObjectFile<ELFT> *Obj, uint64_t SymValue, + SectionRef FunctionSec, + const StringRef SectionName, DataExtractor Data, + uint64_t *Offset); + void printStackSize(const ELFObjectFile<ELFT> *Obj, RelocationRef Rel, + SectionRef FunctionSec, + const StringRef &StackSizeSectionName, + const RelocationResolver &Resolver, DataExtractor Data); + virtual void printStackSizeEntry(uint64_t Size, StringRef FuncName) = 0; virtual void printMipsGOT(const MipsGOTParser<ELFT> &Parser) = 0; virtual void printMipsPLT(const MipsGOTParser<ELFT> &Parser) = 0; + virtual void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) = 0; const ELFDumper<ELFT> *dumper() const { return Dumper; } +protected: + std::function<Error(const Twine &Msg)> WarningHandler; + StringRef FileName; + private: + std::unordered_set<std::string> Warnings; const ELFDumper<ELFT> *Dumper; }; @@ -407,8 +462,8 @@ public: void printHashSymbols(const ELFO *Obj) override; void printDynamic(const ELFFile<ELFT> *Obj) override; void printDynamicRelocations(const ELFO *Obj) override; - void printSymtabMessage(const ELFO *Obj, StringRef Name, - size_t Offset) override; + void printSymtabMessage(const ELFO *Obj, StringRef Name, size_t Offset, + bool NonVisibilityBitsUsed) override; void printProgramHeaders(const ELFO *Obj, bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) override; void printVersionSymbolSection(const ELFFile<ELFT> *Obj, @@ -422,8 +477,11 @@ public: void printAddrsig(const ELFFile<ELFT> *Obj) override; void printNotes(const ELFFile<ELFT> *Obj) override; void printELFLinkerOptions(const ELFFile<ELFT> *Obj) override; + void printStackSizes(const ELFObjectFile<ELFT> *Obj) override; + void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override; + void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) override; private: struct Field { @@ -484,7 +542,8 @@ private: void printRelocation(const ELFO *Obj, const Elf_Sym *Sym, StringRef SymbolName, const Elf_Rela &R, bool IsRela); void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First, - StringRef StrTable, bool IsDynamic) override; + 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); @@ -525,8 +584,11 @@ public: void printAddrsig(const ELFFile<ELFT> *Obj) override; void printNotes(const ELFFile<ELFT> *Obj) override; void printELFLinkerOptions(const ELFFile<ELFT> *Obj) override; + void printStackSizes(const ELFObjectFile<ELFT> *Obj) override; + void printStackSizeEntry(uint64_t Size, StringRef FuncName) override; void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override; void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override; + void printMipsABIFlags(const ELFObjectFile<ELFT> *Obj) override; private: void printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab); @@ -534,7 +596,8 @@ private: void printSymbols(const ELFO *Obj); void printDynamicSymbols(const ELFO *Obj); void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First, - StringRef StrTable, bool IsDynamic) override; + StringRef StrTable, bool IsDynamic, + bool /*NonVisibilityBitsUsed*/) override; void printProgramHeaders(const ELFO *Obj); void printSectionMapping(const ELFO *Obj) {} @@ -680,9 +743,9 @@ StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab, sizeof(Elf_Sym); // Get the corresponding version index entry. - const Elf_Versym *Versym = - unwrapOrError(ObjF->getELFFile()->template getEntry<Elf_Versym>( - SymbolVersionSection, EntryIndex)); + const Elf_Versym *Versym = unwrapOrError( + ObjF->getFileName(), ObjF->getELFFile()->template getEntry<Elf_Versym>( + SymbolVersionSection, EntryIndex)); return this->getSymbolVersionByIndex(StrTab, Versym->vs_index, IsDefault); } @@ -691,15 +754,22 @@ static std::string maybeDemangle(StringRef Name) { } template <typename ELFT> -std::string ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const { +Expected<std::string> +ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const { const ELFFile<ELFT> *Obj = ObjF->getELFFile(); - StringRef StrTable = - unwrapOrError(Obj->getStringTableForSymtab(*DotSymtabSec)); - Elf_Sym_Range Syms = unwrapOrError(Obj->symbols(DotSymtabSec)); - if (Index >= Syms.size()) - reportError("Invalid symbol index"); - const Elf_Sym *Sym = &Syms[Index]; - return maybeDemangle(unwrapOrError(Sym->getName(StrTable))); + Expected<const typename ELFT::Sym *> SymOrErr = + Obj->getSymbol(DotSymtabSec, Index); + if (!SymOrErr) + return SymOrErr.takeError(); + + Expected<StringRef> StrTabOrErr = Obj->getStringTableForSymtab(*DotSymtabSec); + if (!StrTabOrErr) + return StrTabOrErr.takeError(); + + Expected<StringRef> NameOrErr = (*SymOrErr)->getName(*StrTabOrErr); + if (!NameOrErr) + return NameOrErr.takeError(); + return maybeDemangle(*NameOrErr); } template <typename ELFT> @@ -717,7 +787,7 @@ StringRef ELFDumper<ELFT>::getSymbolVersionByIndex(StringRef StrTab, // Lookup this symbol in the version table. LoadVersionMap(); if (VersionIndex >= VersionMap.size() || VersionMap[VersionIndex].isNull()) - reportError("Invalid version entry"); + reportError(createError("Invalid version entry"), ObjF->getFileName()); const VersionMapEntry &Entry = VersionMap[VersionIndex]; // Get the version name string. @@ -731,7 +801,7 @@ StringRef ELFDumper<ELFT>::getSymbolVersionByIndex(StringRef StrTab, IsDefault = false; } if (NameOffset >= StrTab.size()) - reportError("Invalid string offset"); + reportError(createError("Invalid string offset"), ObjF->getFileName()); return StrTab.data() + NameOffset; } @@ -739,14 +809,14 @@ template <typename ELFT> std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable, bool IsDynamic) const { - std::string SymbolName = - maybeDemangle(unwrapOrError(Symbol->getName(StrTable))); + std::string SymbolName = maybeDemangle( + unwrapOrError(ObjF->getFileName(), Symbol->getName(StrTable))); if (SymbolName.empty() && Symbol->getType() == ELF::STT_SECTION) { unsigned SectionIndex; StringRef SectionName; - Elf_Sym_Range Syms = - unwrapOrError(ObjF->getELFFile()->symbols(DotSymtabSec)); + Elf_Sym_Range Syms = unwrapOrError( + ObjF->getFileName(), ObjF->getELFFile()->symbols(DotSymtabSec)); getSectionNameIndex(Symbol, Syms.begin(), SectionName, SectionIndex); return SectionName; } @@ -783,31 +853,32 @@ void ELFDumper<ELFT>::getSectionNameIndex(const Elf_Sym *Symbol, SectionName = "Reserved"; else { if (SectionIndex == SHN_XINDEX) - SectionIndex = unwrapOrError(object::getExtendedSymbolTableIndex<ELFT>( - Symbol, FirstSym, ShndxTable)); + SectionIndex = unwrapOrError(ObjF->getFileName(), + object::getExtendedSymbolTableIndex<ELFT>( + Symbol, FirstSym, ShndxTable)); const ELFFile<ELFT> *Obj = ObjF->getELFFile(); const typename ELFT::Shdr *Sec = - unwrapOrError(Obj->getSection(SectionIndex)); - SectionName = unwrapOrError(Obj->getSectionName(Sec)); + unwrapOrError(ObjF->getFileName(), Obj->getSection(SectionIndex)); + SectionName = unwrapOrError(ObjF->getFileName(), Obj->getSectionName(Sec)); } } template <class ELFO> static const typename ELFO::Elf_Shdr * -findNotEmptySectionByAddress(const ELFO *Obj, uint64_t Addr) { - for (const auto &Shdr : unwrapOrError(Obj->sections())) +findNotEmptySectionByAddress(const ELFO *Obj, StringRef FileName, + uint64_t Addr) { + for (const auto &Shdr : unwrapOrError(FileName, Obj->sections())) if (Shdr.sh_addr == Addr && Shdr.sh_size > 0) return &Shdr; return nullptr; } template <class ELFO> -static const typename ELFO::Elf_Shdr *findSectionByName(const ELFO &Obj, - StringRef Name) { - for (const auto &Shdr : unwrapOrError(Obj.sections())) { - if (Name == unwrapOrError(Obj.getSectionName(&Shdr))) +static const typename ELFO::Elf_Shdr * +findSectionByName(const ELFO &Obj, StringRef FileName, StringRef Name) { + for (const auto &Shdr : unwrapOrError(FileName, Obj.sections())) + if (Name == unwrapOrError(FileName, Obj.getSectionName(&Shdr))) return &Shdr; - } return nullptr; } @@ -1356,10 +1427,12 @@ static const char *getElfMipsOptionsOdkType(unsigned Odk) { } template <typename ELFT> -void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) { +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(Obj->program_headers())) { + for (const Elf_Phdr &Phdr : + unwrapOrError(ObjF->getFileName(), Obj->program_headers())) { if (Phdr.p_type != ELF::PT_DYNAMIC) continue; DynamicPhdr = &Phdr; @@ -1368,61 +1441,132 @@ void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) { // Try to locate the .dynamic section in the sections header table. const Elf_Shdr *DynamicSec = nullptr; - for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { + for (const Elf_Shdr &Sec : + unwrapOrError(ObjF->getFileName(), Obj->sections())) { if (Sec.sh_type != ELF::SHT_DYNAMIC) continue; DynamicSec = &Sec; break; } - // Information in the section header has priority over the information - // in a PT_DYNAMIC header. + if (DynamicPhdr && DynamicPhdr->p_offset + DynamicPhdr->p_filesz > + ObjF->getMemoryBufferRef().getBufferSize()) { + reportWarning( + createError( + "PT_DYNAMIC segment offset + size exceeds the size of the file"), + ObjF->getFileName()); + // Don't use the broken dynamic header. + DynamicPhdr = nullptr; + } + + if (DynamicPhdr && DynamicSec) { + StringRef Name = + unwrapOrError(ObjF->getFileName(), Obj->getSectionName(DynamicSec)); + if (DynamicSec->sh_addr + DynamicSec->sh_size > + DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz || + DynamicSec->sh_addr < DynamicPhdr->p_vaddr) + reportWarning(createError("The SHT_DYNAMIC section '" + Name + + "' is not contained within the " + "PT_DYNAMIC segment"), + ObjF->getFileName()); + + if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr) + reportWarning(createError("The SHT_DYNAMIC section '" + Name + + "' is not at the start of " + "PT_DYNAMIC segment"), + ObjF->getFileName()); + } + + return std::make_pair(DynamicPhdr, DynamicSec); +} + +template <typename ELFT> +void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) { + const Elf_Phdr *DynamicPhdr; + const Elf_Shdr *DynamicSec; + std::tie(DynamicPhdr, DynamicSec) = findDynamic(Obj); + if (!DynamicPhdr && !DynamicSec) + return; + + DynRegionInfo FromPhdr(ObjF->getFileName()); + bool IsPhdrTableValid = false; + if (DynamicPhdr) { + FromPhdr = createDRIFrom(DynamicPhdr, sizeof(Elf_Dyn)); + IsPhdrTableValid = !FromPhdr.getAsArrayRef<Elf_Dyn>().empty(); + } + + // Locate the dynamic table described in a section header. // Ignore sh_entsize and use the expected value for entry size explicitly. - // This allows us to dump the dynamic sections with a broken sh_entsize + // This allows us to dump dynamic sections with a broken sh_entsize // field. + DynRegionInfo FromSec(ObjF->getFileName()); + bool IsSecTableValid = false; if (DynamicSec) { - DynamicTable = checkDRI({ObjF->getELFFile()->base() + DynamicSec->sh_offset, - DynamicSec->sh_size, sizeof(Elf_Dyn)}); - parseDynamicTable(); + FromSec = + checkDRI({ObjF->getELFFile()->base() + DynamicSec->sh_offset, + DynamicSec->sh_size, sizeof(Elf_Dyn), ObjF->getFileName()}); + IsSecTableValid = !FromSec.getAsArrayRef<Elf_Dyn>().empty(); } - // If we have a PT_DYNAMIC header, we will either check the found dynamic - // section or take the dynamic table data directly from the header. - if (!DynamicPhdr) + // When we only have information from one of the SHT_DYNAMIC section header or + // PT_DYNAMIC program header, just use that. + if (!DynamicPhdr || !DynamicSec) { + if ((DynamicPhdr && IsPhdrTableValid) || (DynamicSec && IsSecTableValid)) { + DynamicTable = DynamicPhdr ? FromPhdr : FromSec; + parseDynamicTable(); + } else { + reportWarning(createError("no valid dynamic table was found"), + ObjF->getFileName()); + } return; + } - if (DynamicPhdr->p_offset + DynamicPhdr->p_filesz > - ObjF->getMemoryBufferRef().getBufferSize()) - reportError( - "PT_DYNAMIC segment offset + size exceeds the size of the file"); + // At this point we have tables found from the section header and from the + // dynamic segment. Usually they match, but we have to do sanity checks to + // verify that. - if (!DynamicSec) { - DynamicTable = createDRIFrom(DynamicPhdr, sizeof(Elf_Dyn)); - parseDynamicTable(); + if (FromPhdr.Addr != FromSec.Addr) + reportWarning(createError("SHT_DYNAMIC section header and PT_DYNAMIC " + "program header disagree about " + "the location of the dynamic table"), + ObjF->getFileName()); + + if (!IsPhdrTableValid && !IsSecTableValid) { + reportWarning(createError("no valid dynamic table was found"), + ObjF->getFileName()); return; } - StringRef Name = unwrapOrError(Obj->getSectionName(DynamicSec)); - if (DynamicSec->sh_addr + DynamicSec->sh_size > - DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz || - DynamicSec->sh_addr < DynamicPhdr->p_vaddr) - reportWarning("The SHT_DYNAMIC section '" + Name + - "' is not contained within the " - "PT_DYNAMIC segment"); + // Information in the PT_DYNAMIC program header has priority over the information + // in a section header. + if (IsPhdrTableValid) { + if (!IsSecTableValid) + reportWarning( + createError( + "SHT_DYNAMIC dynamic table is invalid: PT_DYNAMIC will be used"), + ObjF->getFileName()); + DynamicTable = FromPhdr; + } else { + reportWarning( + createError( + "PT_DYNAMIC dynamic table is invalid: SHT_DYNAMIC will be used"), + ObjF->getFileName()); + DynamicTable = FromSec; + } - if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr) - reportWarning("The SHT_DYNAMIC section '" + Name + - "' is not at the start of " - "PT_DYNAMIC segment"); + parseDynamicTable(); } template <typename ELFT> ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, - ScopedPrinter &Writer) - : ObjDumper(Writer), ObjF(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()) { const ELFFile<ELFT> *Obj = ObjF->getELFFile(); - - for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { + for (const Elf_Shdr &Sec : + unwrapOrError(ObjF->getFileName(), Obj->sections())) { switch (Sec.sh_type) { case ELF::SHT_SYMTAB: if (!DotSymtabSec) @@ -1433,16 +1577,17 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, DynSymRegion = createDRIFrom(&Sec); // This is only used (if Elf_Shdr present)for naming section in GNU // style - DynSymtabName = unwrapOrError(Obj->getSectionName(&Sec)); + DynSymtabName = + unwrapOrError(ObjF->getFileName(), Obj->getSectionName(&Sec)); if (Expected<StringRef> E = Obj->getStringTableForSymtab(Sec)) DynamicStringTable = *E; else - warn(E.takeError()); + reportWarning(E.takeError(), ObjF->getFileName()); } break; case ELF::SHT_SYMTAB_SHNDX: - ShndxTable = unwrapOrError(Obj->getSHNDXTable(Sec)); + ShndxTable = unwrapOrError(ObjF->getFileName(), Obj->getSHNDXTable(Sec)); break; case ELF::SHT_GNU_versym: if (!SymbolVersionSection) @@ -1547,10 +1692,13 @@ template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() { auto toMappedAddr = [&](uint64_t Tag, uint64_t VAddr) -> const uint8_t * { auto MappedAddrOrError = ObjF->getELFFile()->toMappedAddr(VAddr); if (!MappedAddrOrError) { - reportWarning("Unable to parse DT_" + - Twine(getTypeString( - ObjF->getELFFile()->getHeader()->e_machine, Tag)) + - ": " + llvm::toString(MappedAddrOrError.takeError())); + Error Err = + createError("Unable to parse DT_" + + Twine(getTypeString( + ObjF->getELFFile()->getHeader()->e_machine, Tag)) + + ": " + llvm::toString(MappedAddrOrError.takeError())); + + reportWarning(std::move(Err), ObjF->getFileName()); return nullptr; } return MappedAddrOrError.get(); @@ -1576,10 +1724,29 @@ template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() { case ELF::DT_STRSZ: StringTableSize = Dyn.getVal(); break; - case ELF::DT_SYMTAB: - DynSymRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); - DynSymRegion.EntSize = sizeof(Elf_Sym); + 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 (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); + } break; + } case ELF::DT_RELA: DynRelaRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); break; @@ -1619,8 +1786,9 @@ template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() { else if (Dyn.getVal() == DT_RELA) DynPLTRelRegion.EntSize = sizeof(Elf_Rela); else - reportError(Twine("unknown DT_PLTREL value of ") + - Twine((uint64_t)Dyn.getVal())); + reportError(createError(Twine("unknown DT_PLTREL value of ") + + Twine((uint64_t)Dyn.getVal())), + ObjF->getFileName()); break; case ELF::DT_JMPREL: DynPLTRelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr()); @@ -1632,8 +1800,7 @@ template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() { } if (StringTableBegin) DynamicStringTable = StringRef(StringTableBegin, StringTableSize); - if (SONameOffset && SONameOffset < DynamicStringTable.size()) - SOName = DynamicStringTable.data() + SONameOffset; + SOName = getDynamicString(SONameOffset); } template <typename ELFT> @@ -1715,6 +1882,10 @@ template <class ELFT> void ELFDumper<ELFT>::printELFLinkerOptions() { ELFDumperStyle->printELFLinkerOptions(ObjF->getELFFile()); } +template <class ELFT> void ELFDumper<ELFT>::printStackSizes() { + ELFDumperStyle->printStackSizes(ObjF); +} + #define LLVM_READOBJ_DT_FLAG_ENT(prefix, enum) \ { #enum, prefix##_##enum } @@ -1953,13 +2124,7 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, {DT_RPATH, "Library rpath"}, {DT_RUNPATH, "Library runpath"}, }; - OS << TagNames.at(Type) << ": "; - if (DynamicStringTable.empty()) - OS << "<String table is empty or was not found> "; - else if (Value < DynamicStringTable.size()) - OS << "[" << StringRef(DynamicStringTable.data() + Value) << "]"; - else - OS << "<Invalid offset 0x" << utohexstr(Value) << ">"; + OS << TagNames.at(Type) << ": [" << getDynamicString(Value) << "]"; break; } case DT_FLAGS: @@ -1974,6 +2139,15 @@ void ELFDumper<ELFT>::printDynamicEntry(raw_ostream &OS, uint64_t Type, } } +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(); +} + template <class ELFT> void ELFDumper<ELFT>::printUnwindInfo() { DwarfCFIEH::PrinterContext<ELFT> Ctx(W, ObjF); Ctx.printUnwindInformation(); @@ -1985,7 +2159,8 @@ template <> void ELFDumper<ELF32LE>::printUnwindInfo() { const ELFFile<ELF32LE> *Obj = ObjF->getELFFile(); const unsigned Machine = Obj->getHeader()->e_machine; if (Machine == EM_ARM) { - ARM::EHABI::PrinterContext<ELF32LE> Ctx(W, Obj, DotSymtabSec); + ARM::EHABI::PrinterContext<ELF32LE> Ctx(W, Obj, ObjF->getFileName(), + DotSymtabSec); Ctx.PrintUnwindInformation(); } DwarfCFIEH::PrinterContext<ELF32LE> Ctx(W, ObjF); @@ -2001,17 +2176,10 @@ template <class ELFT> void ELFDumper<ELFT>::printDynamicTable() { template <class ELFT> void ELFDumper<ELFT>::printNeededLibraries() { ListScope D(W, "NeededLibraries"); - using LibsTy = std::vector<StringRef>; - LibsTy Libs; - + std::vector<std::string> Libs; for (const auto &Entry : dynamic_table()) - if (Entry.d_tag == ELF::DT_NEEDED) { - uint64_t Value = Entry.d_un.d_val; - if (Value < DynamicStringTable.size()) - Libs.push_back(StringRef(DynamicStringTable.data() + Value)); - else - Libs.push_back("<Library name index out of range>"); - } + if (Entry.d_tag == ELF::DT_NEEDED) + Libs.push_back(getDynamicString(Entry.d_un.d_val)); llvm::stable_sort(Libs); @@ -2042,7 +2210,7 @@ template <typename ELFT> void ELFDumper<ELFT>::printGnuHashTable() { Elf_Sym_Range Syms = dynamic_symbols(); unsigned NumSyms = std::distance(Syms.begin(), Syms.end()); if (!NumSyms) - reportError("No dynamic symbol section"); + reportError(createError("No dynamic symbol section"), ObjF->getFileName()); W.printHexList("Values", GnuHashTable->values(NumSyms)); } @@ -2050,6 +2218,30 @@ template <typename ELFT> void ELFDumper<ELFT>::printLoadName() { W.printString("LoadName", SOName); } +template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + switch (Obj->getHeader()->e_machine) { + case EM_ARM: + printAttributes(); + break; + case EM_MIPS: { + ELFDumperStyle->printMipsABIFlags(ObjF); + printMipsOptions(); + printMipsReginfo(); + + MipsGOTParser<ELFT> Parser(Obj, ObjF->getFileName(), dynamic_table(), + dynamic_symbols()); + if (Parser.hasGot()) + ELFDumperStyle->printMipsGOT(Parser); + if (Parser.hasPlt()) + ELFDumperStyle->printMipsPLT(Parser); + break; + } + default: + break; + } +} + template <class ELFT> void ELFDumper<ELFT>::printAttributes() { W.startLine() << "Attributes not implemented.\n"; } @@ -2064,11 +2256,13 @@ template <> void ELFDumper<ELF32LE>::printAttributes() { } DictScope BA(W, "BuildAttributes"); - for (const ELFO::Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { + for (const ELFO::Elf_Shdr &Sec : + unwrapOrError(ObjF->getFileName(), Obj->sections())) { if (Sec.sh_type != ELF::SHT_ARM_ATTRIBUTES) continue; - ArrayRef<uint8_t> Contents = unwrapOrError(Obj->getSectionContents(&Sec)); + 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'; @@ -2092,7 +2286,8 @@ public: const bool IsStatic; const ELFO * const Obj; - MipsGOTParser(const ELFO *Obj, Elf_Dyn_Range DynTable, Elf_Sym_Range DynSyms); + MipsGOTParser(const ELFO *Obj, StringRef FileName, Elf_Dyn_Range DynTable, + Elf_Sym_Range DynSyms); bool hasGot() const { return !GotEntries.empty(); } bool hasPlt() const { return !PltEntries.empty(); } @@ -2126,6 +2321,8 @@ private: const Elf_Shdr *PltSec; const Elf_Shdr *PltRelSec; const Elf_Shdr *PltSymTable; + StringRef FileName; + Elf_Sym_Range GotDynSyms; StringRef PltStrTable; @@ -2136,21 +2333,24 @@ private: } // end anonymous namespace template <class ELFT> -MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, Elf_Dyn_Range DynTable, +MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, StringRef FileName, + Elf_Dyn_Range DynTable, Elf_Sym_Range DynSyms) : IsStatic(DynTable.empty()), Obj(Obj), GotSec(nullptr), LocalNum(0), - GlobalNum(0), PltSec(nullptr), PltRelSec(nullptr), PltSymTable(nullptr) { + GlobalNum(0), PltSec(nullptr), PltRelSec(nullptr), PltSymTable(nullptr), + FileName(FileName) { // 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 // Find static GOT secton. if (IsStatic) { - GotSec = findSectionByName(*Obj, ".got"); + GotSec = findSectionByName(*Obj, FileName, ".got"); if (!GotSec) - reportError("Cannot find .got section"); + return; - ArrayRef<uint8_t> Content = unwrapOrError(Obj->getSectionContents(GotSec)); + ArrayRef<uint8_t> Content = + unwrapOrError(FileName, Obj->getSectionContents(GotSec)); GotEntries = Entries(reinterpret_cast<const Entry *>(Content.data()), Content.size() / sizeof(Entry)); LocalNum = GotEntries.size(); @@ -2194,17 +2394,21 @@ MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, Elf_Dyn_Range DynTable, size_t DynSymTotal = DynSyms.size(); if (*DtGotSym > DynSymTotal) - reportError("MIPS_GOTSYM exceeds a number of dynamic symbols"); + reportError( + createError("MIPS_GOTSYM exceeds a number of dynamic symbols"), + FileName); - GotSec = findNotEmptySectionByAddress(Obj, *DtPltGot); + GotSec = findNotEmptySectionByAddress(Obj, FileName, *DtPltGot); if (!GotSec) - reportError("There is no not empty GOT section at 0x" + - Twine::utohexstr(*DtPltGot)); + 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(Obj->getSectionContents(GotSec)); + 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); @@ -2217,23 +2421,24 @@ MipsGOTParser<ELFT>::MipsGOTParser(const ELFO *Obj, Elf_Dyn_Range DynTable, if (!DtJmpRel) report_fatal_error("Cannot find JMPREL dynamic table tag."); - PltSec = findNotEmptySectionByAddress(Obj, *DtMipsPltGot); + 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, *DtJmpRel); + PltRelSec = findNotEmptySectionByAddress(Obj, FileName, * DtJmpRel); if (!PltRelSec) report_fatal_error("There is no not empty RELPLT section at 0x" + Twine::utohexstr(*DtJmpRel)); ArrayRef<uint8_t> PltContent = - unwrapOrError(Obj->getSectionContents(PltSec)); + unwrapOrError(FileName, Obj->getSectionContents(PltSec)); PltEntries = Entries(reinterpret_cast<const Entry *>(PltContent.data()), PltContent.size() / sizeof(Entry)); - PltSymTable = unwrapOrError(Obj->getSection(PltRelSec->sh_link)); - PltStrTable = unwrapOrError(Obj->getStringTableForSymtab(*PltSymTable)); + PltSymTable = unwrapOrError(FileName, Obj->getSection(PltRelSec->sh_link)); + PltStrTable = + unwrapOrError(FileName, Obj->getStringTableForSymtab(*PltSymTable)); } } @@ -2334,26 +2539,16 @@ const typename MipsGOTParser<ELFT>::Elf_Sym * MipsGOTParser<ELFT>::getPltSym(const Entry *E) const { int64_t Offset = std::distance(getPltEntries().data(), E); if (PltRelSec->sh_type == ELF::SHT_REL) { - Elf_Rel_Range Rels = unwrapOrError(Obj->rels(PltRelSec)); - return unwrapOrError(Obj->getRelocationSymbol(&Rels[Offset], PltSymTable)); + Elf_Rel_Range Rels = unwrapOrError(FileName, Obj->rels(PltRelSec)); + return unwrapOrError(FileName, + Obj->getRelocationSymbol(&Rels[Offset], PltSymTable)); } else { - Elf_Rela_Range Rels = unwrapOrError(Obj->relas(PltRelSec)); - return unwrapOrError(Obj->getRelocationSymbol(&Rels[Offset], PltSymTable)); + Elf_Rela_Range Rels = unwrapOrError(FileName, Obj->relas(PltRelSec)); + return unwrapOrError(FileName, + Obj->getRelocationSymbol(&Rels[Offset], PltSymTable)); } } -template <class ELFT> void ELFDumper<ELFT>::printMipsPLTGOT() { - const ELFFile<ELFT> *Obj = ObjF->getELFFile(); - if (Obj->getHeader()->e_machine != EM_MIPS) - reportError("MIPS PLT GOT is available for MIPS targets only"); - - MipsGOTParser<ELFT> Parser(Obj, dynamic_table(), dynamic_symbols()); - if (Parser.hasGot()) - ELFDumperStyle->printMipsGOT(Parser); - if (Parser.hasPlt()) - ELFDumperStyle->printMipsPLT(Parser); -} - static const EnumEntry<unsigned> ElfMipsISAExtType[] = { {"None", Mips::AFL_EXT_NONE}, {"Broadcom SB-1", Mips::AFL_EXT_SB1}, @@ -2427,41 +2622,6 @@ static int getMipsRegisterSize(uint8_t Flag) { } } -template <class ELFT> void ELFDumper<ELFT>::printMipsABIFlags() { - const ELFFile<ELFT> *Obj = ObjF->getELFFile(); - const Elf_Shdr *Shdr = findSectionByName(*Obj, ".MIPS.abiflags"); - if (!Shdr) { - W.startLine() << "There is no .MIPS.abiflags section in the file.\n"; - return; - } - ArrayRef<uint8_t> Sec = unwrapOrError(Obj->getSectionContents(Shdr)); - if (Sec.size() != sizeof(Elf_Mips_ABIFlags<ELFT>)) { - W.startLine() << "The .MIPS.abiflags section has a wrong size.\n"; - return; - } - - auto *Flags = reinterpret_cast<const Elf_Mips_ABIFlags<ELFT> *>(Sec.data()); - - raw_ostream &OS = W.getOStream(); - DictScope GS(W, "MIPS ABI Flags"); - - W.printNumber("Version", Flags->version); - W.startLine() << "ISA: "; - if (Flags->isa_rev <= 1) - OS << format("MIPS%u", Flags->isa_level); - else - OS << format("MIPS%ur%u", Flags->isa_level, Flags->isa_rev); - OS << "\n"; - W.printEnum("ISA Extension", Flags->isa_ext, makeArrayRef(ElfMipsISAExtType)); - W.printFlags("ASEs", Flags->ases, makeArrayRef(ElfMipsASEFlags)); - W.printEnum("FP ABI", Flags->fp_abi, makeArrayRef(ElfMipsFpABIType)); - W.printNumber("GPR size", getMipsRegisterSize(Flags->gpr_size)); - W.printNumber("CPR1 size", getMipsRegisterSize(Flags->cpr1_size)); - W.printNumber("CPR2 size", getMipsRegisterSize(Flags->cpr2_size)); - W.printFlags("Flags 1", Flags->flags1, makeArrayRef(ElfMipsFlags1)); - W.printHex("Flags 2", Flags->flags2); -} - template <class ELFT> static void printMipsReginfoData(ScopedPrinter &W, const Elf_Mips_RegInfo<ELFT> &Reginfo) { @@ -2475,12 +2635,13 @@ static void printMipsReginfoData(ScopedPrinter &W, template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() { const ELFFile<ELFT> *Obj = ObjF->getELFFile(); - const Elf_Shdr *Shdr = findSectionByName(*Obj, ".reginfo"); + const Elf_Shdr *Shdr = findSectionByName(*Obj, ObjF->getFileName(), ".reginfo"); if (!Shdr) { W.startLine() << "There is no .reginfo section in the file.\n"; return; } - ArrayRef<uint8_t> Sec = unwrapOrError(Obj->getSectionContents(Shdr)); + ArrayRef<uint8_t> Sec = + unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(Shdr)); if (Sec.size() != sizeof(Elf_Mips_RegInfo<ELFT>)) { W.startLine() << "The .reginfo section has a wrong size.\n"; return; @@ -2493,7 +2654,8 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() { template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() { const ELFFile<ELFT> *Obj = ObjF->getELFFile(); - const Elf_Shdr *Shdr = findSectionByName(*Obj, ".MIPS.options"); + const Elf_Shdr *Shdr = + findSectionByName(*Obj, ObjF->getFileName(), ".MIPS.options"); if (!Shdr) { W.startLine() << "There is no .MIPS.options section in the file.\n"; return; @@ -2501,7 +2663,8 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() { DictScope GS(W, "MIPS Options"); - ArrayRef<uint8_t> Sec = unwrapOrError(Obj->getSectionContents(Shdr)); + ArrayRef<uint8_t> Sec = + unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(Shdr)); while (!Sec.empty()) { if (Sec.size() < sizeof(Elf_Mips_Options<ELFT>)) { W.startLine() << "The .MIPS.options section has a wrong size.\n"; @@ -2524,8 +2687,9 @@ 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(Obj->sections())) { - StringRef Name = unwrapOrError(Obj->getSectionName(&Sec)); + for (const auto &Sec : unwrapOrError(ObjF->getFileName(), Obj->sections())) { + StringRef Name = + unwrapOrError(ObjF->getFileName(), Obj->getSectionName(&Sec)); if (Name == ".llvm_stackmaps") { StackMapSection = &Sec; break; @@ -2535,8 +2699,8 @@ template <class ELFT> void ELFDumper<ELFT>::printStackMap() const { if (!StackMapSection) return; - ArrayRef<uint8_t> StackMapContentsArray = - unwrapOrError(Obj->getSectionContents(StackMapSection)); + ArrayRef<uint8_t> StackMapContentsArray = unwrapOrError( + ObjF->getFileName(), Obj->getSectionContents(StackMapSection)); prettyPrintStackMap( W, StackMapParser<ELFT::TargetEndianness>(StackMapContentsArray)); @@ -2560,24 +2724,26 @@ static inline void printFields(formatted_raw_ostream &OS, StringRef Str1, } template <class ELFT> -static std::string getSectionHeadersNumString(const ELFFile<ELFT> *Obj) { +static std::string getSectionHeadersNumString(const ELFFile<ELFT> *Obj, + StringRef FileName) { const typename ELFT::Ehdr *ElfHeader = Obj->getHeader(); if (ElfHeader->e_shnum != 0) return to_string(ElfHeader->e_shnum); - ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(Obj->sections()); + ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(FileName, Obj->sections()); if (Arr.empty()) return "0"; return "0 (" + to_string(Arr[0].sh_size) + ")"; } template <class ELFT> -static std::string getSectionHeaderTableIndexString(const ELFFile<ELFT> *Obj) { +static std::string getSectionHeaderTableIndexString(const ELFFile<ELFT> *Obj, + StringRef FileName) { const typename ELFT::Ehdr *ElfHeader = Obj->getHeader(); if (ElfHeader->e_shstrndx != SHN_XINDEX) return to_string(ElfHeader->e_shstrndx); - ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(Obj->sections()); + ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(FileName, Obj->sections()); if (Arr.empty()) return "65535 (corrupt: out of range)"; return to_string(ElfHeader->e_shstrndx) + " (" + to_string(Arr[0].sh_link) + @@ -2639,9 +2805,9 @@ template <class ELFT> void GNUStyle<ELFT>::printFileHeaders(const ELFO *Obj) { printFields(OS, "Number of program headers:", Str); Str = to_string(e->e_shentsize) + " (bytes)"; printFields(OS, "Size of section headers:", Str); - Str = getSectionHeadersNumString(Obj); + Str = getSectionHeadersNumString(Obj, this->FileName); printFields(OS, "Number of section headers:", Str); - Str = getSectionHeaderTableIndexString(Obj); + Str = getSectionHeaderTableIndexString(Obj, this->FileName); printFields(OS, "Section header string table index:", Str); } @@ -2663,26 +2829,29 @@ struct GroupSection { }; template <class ELFT> -std::vector<GroupSection> getGroups(const ELFFile<ELFT> *Obj) { +std::vector<GroupSection> getGroups(const ELFFile<ELFT> *Obj, + StringRef FileName) { using Elf_Shdr = typename ELFT::Shdr; using Elf_Sym = typename ELFT::Sym; using Elf_Word = typename ELFT::Word; std::vector<GroupSection> Ret; uint64_t I = 0; - for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { + for (const Elf_Shdr &Sec : unwrapOrError(FileName, Obj->sections())) { ++I; if (Sec.sh_type != ELF::SHT_GROUP) continue; - const Elf_Shdr *Symtab = unwrapOrError(Obj->getSection(Sec.sh_link)); - StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*Symtab)); - const Elf_Sym *Sym = - unwrapOrError(Obj->template getEntry<Elf_Sym>(Symtab, Sec.sh_info)); - auto Data = - unwrapOrError(Obj->template getSectionContentsAsArray<Elf_Word>(&Sec)); + const Elf_Shdr *Symtab = + unwrapOrError(FileName, Obj->getSection(Sec.sh_link)); + StringRef StrTable = + unwrapOrError(FileName, Obj->getStringTableForSymtab(*Symtab)); + const Elf_Sym *Sym = unwrapOrError( + FileName, Obj->template getEntry<Elf_Sym>(Symtab, Sec.sh_info)); + auto Data = unwrapOrError( + FileName, Obj->template getSectionContentsAsArray<Elf_Word>(&Sec)); - StringRef Name = unwrapOrError(Obj->getSectionName(&Sec)); + StringRef Name = unwrapOrError(FileName, Obj->getSectionName(&Sec)); StringRef Signature = StrTable.data() + Sym->st_name; Ret.push_back({Name, maybeDemangle(Signature), @@ -2695,8 +2864,8 @@ std::vector<GroupSection> getGroups(const ELFFile<ELFT> *Obj) { std::vector<GroupMember> &GM = Ret.back().Members; for (uint32_t Ndx : Data.slice(1)) { - auto Sec = unwrapOrError(Obj->getSection(Ndx)); - const StringRef Name = unwrapOrError(Obj->getSectionName(Sec)); + auto Sec = unwrapOrError(FileName, Obj->getSection(Ndx)); + const StringRef Name = unwrapOrError(FileName, Obj->getSectionName(Sec)); GM.push_back({Name, Ndx}); } } @@ -2715,7 +2884,7 @@ mapSectionsToGroups(ArrayRef<GroupSection> Groups) { } // namespace template <class ELFT> void GNUStyle<ELFT>::printGroupSections(const ELFO *Obj) { - std::vector<GroupSection> V = getGroups<ELFT>(Obj); + std::vector<GroupSection> V = getGroups<ELFT>(Obj, this->FileName); DenseMap<uint64_t, const GroupSection *> Map = mapSectionsToGroups(V); for (const GroupSection &G : V) { OS << "\n" @@ -2745,14 +2914,17 @@ 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(Obj->getRelocationSymbol(&R, SymTab)); + 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(Obj->getSectionName(Sec)); + TargetName = unwrapOrError(this->FileName, Obj->getSectionName(Sec)); } else if (Sym) { - StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTab)); + StringRef StrTable = + unwrapOrError(this->FileName, Obj->getStringTableForSymtab(*SymTab)); TargetName = this->dumper()->getFullSymbolName( Sym, StrTable, SymTab->sh_type == SHT_DYNSYM /* IsDynamic */); } @@ -2821,21 +2993,21 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocHeader(unsigned SType) { template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) { bool HasRelocSections = false; - for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { + for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, 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 && Sec.sh_type != ELF::SHT_ANDROID_RELR) continue; HasRelocSections = true; - StringRef Name = unwrapOrError(Obj->getSectionName(&Sec)); + StringRef Name = unwrapOrError(this->FileName, Obj->getSectionName(&Sec)); unsigned Entries = Sec.getEntityCount(); std::vector<Elf_Rela> AndroidRelas; if (Sec.sh_type == ELF::SHT_ANDROID_REL || Sec.sh_type == ELF::SHT_ANDROID_RELA) { // Android's packed relocation section needs to be unpacked first // to get the actual number of entries. - AndroidRelas = unwrapOrError(Obj->android_relas(&Sec)); + AndroidRelas = unwrapOrError(this->FileName, Obj->android_relas(&Sec)); Entries = AndroidRelas.size(); } std::vector<Elf_Rela> RelrRelas; @@ -2843,8 +3015,8 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) { Sec.sh_type == ELF::SHT_ANDROID_RELR)) { // .relr.dyn relative relocation section needs to be unpacked first // to get the actual number of entries. - Elf_Relr_Range Relrs = unwrapOrError(Obj->relrs(&Sec)); - RelrRelas = unwrapOrError(Obj->decode_relrs(Relrs)); + Elf_Relr_Range Relrs = unwrapOrError(this->FileName, Obj->relrs(&Sec)); + RelrRelas = unwrapOrError(this->FileName, Obj->decode_relrs(Relrs)); Entries = RelrRelas.size(); } uintX_t Offset = Sec.sh_offset; @@ -2852,10 +3024,11 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) { << to_hexString(Offset, false) << " contains " << Entries << " entries:\n"; printRelocHeader(Sec.sh_type); - const Elf_Shdr *SymTab = unwrapOrError(Obj->getSection(Sec.sh_link)); + const Elf_Shdr *SymTab = + unwrapOrError(this->FileName, Obj->getSection(Sec.sh_link)); switch (Sec.sh_type) { case ELF::SHT_REL: - for (const auto &R : unwrapOrError(Obj->rels(&Sec))) { + for (const auto &R : unwrapOrError(this->FileName, Obj->rels(&Sec))) { Elf_Rela Rela; Rela.r_offset = R.r_offset; Rela.r_info = R.r_info; @@ -2864,13 +3037,13 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) { } break; case ELF::SHT_RELA: - for (const auto &R : unwrapOrError(Obj->relas(&Sec))) + for (const auto &R : unwrapOrError(this->FileName, Obj->relas(&Sec))) printRelocation(Obj, SymTab, R, true); break; case ELF::SHT_RELR: case ELF::SHT_ANDROID_RELR: if (opts::RawRelr) - for (const auto &R : unwrapOrError(Obj->relrs(&Sec))) + for (const auto &R : unwrapOrError(this->FileName, Obj->relrs(&Sec))) OS << to_string(format_hex_no_prefix(R, ELFT::Is64Bits ? 16 : 8)) << "\n"; else @@ -2992,6 +3165,12 @@ static std::string getSectionTypeString(unsigned Arch, unsigned Type) { return "LLVM_ADDRSIG"; case SHT_LLVM_DEPENDENT_LIBRARIES: return "LLVM_DEPENDENT_LIBRARIES"; + case SHT_LLVM_SYMPART: + return "LLVM_SYMPART"; + case SHT_LLVM_PART_EHDR: + return "LLVM_PART_EHDR"; + case SHT_LLVM_PART_PHDR: + return "LLVM_PART_PHDR"; // FIXME: Parse processor specific GNU attributes case SHT_GNU_ATTRIBUTES: return "ATTRIBUTES"; @@ -3010,29 +3189,9 @@ static std::string getSectionTypeString(unsigned Arch, unsigned Type) { } template <class ELFT> -static StringRef getSectionName(const typename ELFT::Shdr &Sec, - const ELFObjectFile<ELFT> &ElfObj, - ArrayRef<typename ELFT::Shdr> Sections) { - const ELFFile<ELFT> &Obj = *ElfObj.getELFFile(); - uint32_t Index = Obj.getHeader()->e_shstrndx; - if (Index == ELF::SHN_XINDEX) - Index = Sections[0].sh_link; - if (!Index) // no section string table. - return ""; - // TODO: Test a case when the sh_link of the section with index 0 is broken. - if (Index >= Sections.size()) - reportError(ElfObj.getFileName(), - createError("section header string table index " + - Twine(Index) + " does not exist")); - StringRef Data = toStringRef(unwrapOrError( - Obj.template getSectionContentsAsArray<uint8_t>(&Sections[Index]))); - return unwrapOrError(Obj.getSectionName(&Sec, Data)); -} - -template <class ELFT> void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { unsigned Bias = ELFT::Is64Bits ? 0 : 8; - ArrayRef<Elf_Shdr> Sections = unwrapOrError(Obj->sections()); + ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections()); OS << "There are " << to_string(Sections.size()) << " section headers, starting at offset " << "0x" << to_hexString(Obj->getHeader()->e_shoff, false) << ":\n\n"; @@ -3050,7 +3209,8 @@ void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { size_t SectionIndex = 0; for (const Elf_Shdr &Sec : Sections) { Fields[0].Str = to_string(SectionIndex); - Fields[1].Str = getSectionName(Sec, *ElfObj, Sections); + Fields[1].Str = unwrapOrError<StringRef>( + ElfObj->getFileName(), Obj->getSectionName(&Sec, this->WarningHandler)); Fields[2].Str = getSectionTypeString(Obj->getHeader()->e_machine, Sec.sh_type); Fields[3].Str = @@ -3089,7 +3249,8 @@ void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { template <class ELFT> void GNUStyle<ELFT>::printSymtabMessage(const ELFO *Obj, StringRef Name, - size_t Entries) { + size_t Entries, + bool NonVisibilityBitsUsed) { if (!Name.empty()) OS << "\nSymbol table '" << Name << "' contains " << Entries << " entries:\n"; @@ -3097,9 +3258,13 @@ void GNUStyle<ELFT>::printSymtabMessage(const ELFO *Obj, StringRef Name, OS << "\n Symbol table for image:\n"; if (ELFT::Is64Bits) - OS << " Num: Value Size Type Bind Vis Ndx Name\n"; + OS << " Num: Value Size Type Bind Vis"; else - OS << " Num: Value Size Type Bind Vis Ndx Name\n"; + OS << " Num: Value Size Type Bind Vis"; + + if (NonVisibilityBitsUsed) + OS << " "; + OS << " Ndx Name\n"; } template <class ELFT> @@ -3115,10 +3280,11 @@ std::string GNUStyle<ELFT>::getSymbolSectionNdx(const ELFO *Obj, case ELF::SHN_COMMON: return "COM"; case ELF::SHN_XINDEX: - return to_string( - format_decimal(unwrapOrError(object::getExtendedSymbolTableIndex<ELFT>( - Symbol, FirstSym, this->dumper()->getShndxTable())), - 3)); + return to_string(format_decimal( + unwrapOrError(this->FileName, + object::getExtendedSymbolTableIndex<ELFT>( + Symbol, FirstSym, this->dumper()->getShndxTable())), + 3)); default: // Find if: // Processor specific @@ -3142,7 +3308,7 @@ 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 IsDynamic, bool NonVisibilityBitsUsed) { static int Idx = 0; static bool Dynamic = true; @@ -3156,7 +3322,7 @@ void GNUStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, unsigned Bias = ELFT::Is64Bits ? 8 : 0; Field Fields[8] = {0, 8, 17 + Bias, 23 + Bias, - 31 + Bias, 38 + Bias, 47 + Bias, 51 + Bias}; + 31 + Bias, 38 + Bias, 48 + Bias, 51 + Bias}; Fields[0].Str = to_string(format_decimal(Idx++, 6)) + ":"; Fields[1].Str = to_string( format_hex_no_prefix(Symbol->st_value, ELFT::Is64Bits ? 16 : 8)); @@ -3173,7 +3339,13 @@ void GNUStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, printEnum(Symbol->getBinding(), makeArrayRef(ElfSymbolBindings)); Fields[5].Str = printEnum(Symbol->getVisibility(), makeArrayRef(ElfSymbolVisibilities)); + if (Symbol->st_other & ~0x3) + Fields[5].Str += + " [<other: " + to_string(format_hex(Symbol->st_other, 2)) + ">]"; + + Fields[6].Column += NonVisibilityBitsUsed ? 13 : 0; Fields[6].Str = getSymbolSectionNdx(Obj, Symbol, FirstSym); + Fields[7].Str = this->dumper()->getFullSymbolName(Symbol, StrTable, IsDynamic); for (auto &Entry : Fields) @@ -3193,7 +3365,7 @@ void GNUStyle<ELFT>::printHashedSymbol(const ELFO *Obj, const Elf_Sym *FirstSym, const auto Symbol = FirstSym + Sym; Fields[2].Str = to_string( - format_hex_no_prefix(Symbol->st_value, ELFT::Is64Bits ? 18 : 8)); + format_hex_no_prefix(Symbol->st_value, ELFT::Is64Bits ? 16 : 8)); Fields[3].Str = to_string(format_decimal(Symbol->st_size, 5)); unsigned char SymbolType = Symbol->getType(); @@ -3246,10 +3418,21 @@ template <class ELFT> void GNUStyle<ELFT>::printHashSymbols(const ELFO *Obj) { 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; + + 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, &DynSyms[0], Ch, StringTable, Buc); + Visited[Ch] = true; } } } @@ -3380,7 +3563,8 @@ void GNUStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { unsigned Width = ELFT::Is64Bits ? 18 : 10; unsigned SizeWidth = ELFT::Is64Bits ? 8 : 7; - for (const auto &Phdr : unwrapOrError(Obj->program_headers())) { + for (const auto &Phdr : + unwrapOrError(this->FileName, Obj->program_headers())) { 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)); @@ -3404,10 +3588,11 @@ void GNUStyle<ELFT>::printSectionMapping(const ELFO *Obj) { OS << "\n Section to Segment mapping:\n Segment Sections...\n"; DenseSet<const Elf_Shdr *> BelongsToSegment; int Phnum = 0; - for (const Elf_Phdr &Phdr : unwrapOrError(Obj->program_headers())) { + for (const Elf_Phdr &Phdr : + unwrapOrError(this->FileName, Obj->program_headers())) { std::string Sections; OS << format(" %2.2d ", Phnum++); - for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { + for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) { // Check if each section is in a segment and then print mapping. // 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 @@ -3418,7 +3603,9 @@ void GNUStyle<ELFT>::printSectionMapping(const ELFO *Obj) { if (!TbssInNonTLS && checkTLSSections(Phdr, Sec) && checkoffsets(Phdr, Sec) && checkVMA(Phdr, Sec) && checkPTDynamic(Phdr, Sec) && (Sec.sh_type != ELF::SHT_NULL)) { - Sections += unwrapOrError(Obj->getSectionName(&Sec)).str() + " "; + Sections += + unwrapOrError(this->FileName, Obj->getSectionName(&Sec)).str() + + " "; BelongsToSegment.insert(&Sec); } } @@ -3428,9 +3615,10 @@ 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(Obj->sections())) { + for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) { if (BelongsToSegment.find(&Sec) == BelongsToSegment.end()) - Sections += unwrapOrError(Obj->getSectionName(&Sec)).str() + ' '; + Sections += + unwrapOrError(this->FileName, Obj->getSectionName(&Sec)).str() + ' '; } if (!Sections.empty()) { OS << " None " << Sections << '\n'; @@ -3438,14 +3626,40 @@ void GNUStyle<ELFT>::printSectionMapping(const ELFO *Obj) { } } +namespace { +template <class ELFT> struct RelSymbol { + const typename ELFT::Sym *Sym; + std::string Name; +}; + +template <class ELFT> +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 { + reportWarning( + createError("unable to get name of the dynamic symbol with index " + + Twine(SymIndex) + ": " + toString(ErrOrName.takeError())), + FileName); + Name = "<corrupt>"; + } + + return {Sym, std::move(Name)}; +} +} // namespace + template <class ELFT> void GNUStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela R, bool IsRela) { - uint32_t SymIndex = R.getSymbol(Obj->isMips64EL()); - const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex; - std::string SymbolName = maybeDemangle( - unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable()))); - printRelocation(Obj, Sym, SymbolName, R, IsRela); + RelSymbol<ELFT> S = getSymbolForReloc(Obj, this->FileName, this->dumper(), R); + printRelocation(Obj, S.Sym, S.Name, R, IsRela); } template <class ELFT> void GNUStyle<ELFT>::printDynamic(const ELFO *Obj) { @@ -3518,7 +3732,8 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { << " contains " << DynRelrRegion.Size << " bytes:\n"; printRelocHeader(ELF::SHT_REL); Elf_Relr_Range Relrs = this->dumper()->dyn_relrs(); - std::vector<Elf_Rela> RelrRelas = unwrapOrError(Obj->decode_relrs(Relrs)); + std::vector<Elf_Rela> RelrRelas = + unwrapOrError(this->FileName, Obj->decode_relrs(Relrs)); for (const Elf_Rela &Rela : RelrRelas) { printDynamicRelocation(Obj, Rela, false); } @@ -3550,14 +3765,15 @@ template <class ELFT> static void printGNUVersionSectionProlog(formatted_raw_ostream &OS, const Twine &Name, unsigned EntriesNum, const ELFFile<ELFT> *Obj, - const typename ELFT::Shdr *Sec) { - StringRef SecName = unwrapOrError(Obj->getSectionName(Sec)); + const typename ELFT::Shdr *Sec, + StringRef FileName) { + StringRef SecName = unwrapOrError(FileName, Obj->getSectionName(Sec)); OS << Name << " section '" << SecName << "' " << "contains " << EntriesNum << " entries:\n"; const typename ELFT::Shdr *SymTab = - unwrapOrError(Obj->getSection(Sec->sh_link)); - StringRef SymTabName = unwrapOrError(Obj->getSectionName(SymTab)); + unwrapOrError(FileName, Obj->getSection(Sec->sh_link)); + StringRef SymTabName = unwrapOrError(FileName, Obj->getSectionName(SymTab)); OS << " Addr: " << format_hex_no_prefix(Sec->sh_addr, 16) << " Offset: " << format_hex(Sec->sh_offset, 8) << " Link: " << Sec->sh_link << " (" << SymTabName << ")\n"; @@ -3570,7 +3786,8 @@ void GNUStyle<ELFT>::printVersionSymbolSection(const ELFFile<ELFT> *Obj, return; unsigned Entries = Sec->sh_size / sizeof(Elf_Versym); - printGNUVersionSectionProlog(OS, "Version symbols", Entries, Obj, Sec); + printGNUVersionSectionProlog(OS, "Version symbols", Entries, Obj, Sec, + this->FileName); const uint8_t *VersymBuf = reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); @@ -3642,14 +3859,17 @@ void GNUStyle<ELFT>::printVersionDefinitionSection(const ELFFile<ELFT> *Obj, return; unsigned VerDefsNum = Sec->sh_info; - printGNUVersionSectionProlog(OS, "Version definition", VerDefsNum, Obj, Sec); + printGNUVersionSectionProlog(OS, "Version definition", VerDefsNum, Obj, Sec, + this->FileName); - const Elf_Shdr *StrTabSec = unwrapOrError(Obj->getSection(Sec->sh_link)); + const Elf_Shdr *StrTabSec = + unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); StringRef StringTable( reinterpret_cast<const char *>(Obj->base() + StrTabSec->sh_offset), (size_t)StrTabSec->sh_size); - const uint8_t *VerdefBuf = unwrapOrError(Obj->getSectionContents(Sec)).data(); + const uint8_t *VerdefBuf = + unwrapOrError(this->FileName, Obj->getSectionContents(Sec)).data(); const uint8_t *Begin = VerdefBuf; while (VerDefsNum--) { @@ -3684,11 +3904,14 @@ void GNUStyle<ELFT>::printVersionDependencySection(const ELFFile<ELFT> *Obj, return; unsigned VerneedNum = Sec->sh_info; - printGNUVersionSectionProlog(OS, "Version needs", VerneedNum, Obj, Sec); + printGNUVersionSectionProlog(OS, "Version needs", VerneedNum, Obj, Sec, + this->FileName); - ArrayRef<uint8_t> SecData = unwrapOrError(Obj->getSectionContents(Sec)); + ArrayRef<uint8_t> SecData = + unwrapOrError(this->FileName, Obj->getSectionContents(Sec)); - const Elf_Shdr *StrTabSec = unwrapOrError(Obj->getSection(Sec->sh_link)); + const Elf_Shdr *StrTabSec = + unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); StringRef StringTable = { reinterpret_cast<const char *>(Obj->base() + StrTabSec->sh_offset), (size_t)StrTabSec->sh_size}; @@ -3745,9 +3968,21 @@ void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) { // Go over all buckets and and note chain lengths of each bucket (total // unique chain lengths). for (size_t B = 0; B < NBucket; B++) { - for (size_t C = Buckets[B]; C > 0 && C < NChain; C = Chains[C]) + 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++; + } TotalSyms += ChainLen[B]; } @@ -3829,7 +4064,7 @@ void GNUStyle<ELFT>::printCGProfile(const ELFFile<ELFT> *Obj) { template <class ELFT> void GNUStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) { - OS << "GNUStyle::printAddrsig not implemented\n"; + reportError(createError("--addrsig: not implemented"), this->FileName); } static StringRef getGenericNoteTypeName(const uint32_t NT) { @@ -3850,6 +4085,86 @@ static StringRef getGenericNoteTypeName(const uint32_t NT) { return ""; } +static StringRef getCoreNoteTypeName(const uint32_t NT) { + static const struct { + uint32_t ID; + const char *Name; + } Notes[] = { + {ELF::NT_PRSTATUS, "NT_PRSTATUS (prstatus structure)"}, + {ELF::NT_FPREGSET, "NT_FPREGSET (floating point registers)"}, + {ELF::NT_PRPSINFO, "NT_PRPSINFO (prpsinfo structure)"}, + {ELF::NT_TASKSTRUCT, "NT_TASKSTRUCT (task structure)"}, + {ELF::NT_AUXV, "NT_AUXV (auxiliary vector)"}, + {ELF::NT_PSTATUS, "NT_PSTATUS (pstatus structure)"}, + {ELF::NT_FPREGS, "NT_FPREGS (floating point registers)"}, + {ELF::NT_PSINFO, "NT_PSINFO (psinfo structure)"}, + {ELF::NT_LWPSTATUS, "NT_LWPSTATUS (lwpstatus_t structure)"}, + {ELF::NT_LWPSINFO, "NT_LWPSINFO (lwpsinfo_t structure)"}, + {ELF::NT_WIN32PSTATUS, "NT_WIN32PSTATUS (win32_pstatus structure)"}, + + {ELF::NT_PPC_VMX, "NT_PPC_VMX (ppc Altivec registers)"}, + {ELF::NT_PPC_VSX, "NT_PPC_VSX (ppc VSX registers)"}, + {ELF::NT_PPC_TAR, "NT_PPC_TAR (ppc TAR register)"}, + {ELF::NT_PPC_PPR, "NT_PPC_PPR (ppc PPR register)"}, + {ELF::NT_PPC_DSCR, "NT_PPC_DSCR (ppc DSCR register)"}, + {ELF::NT_PPC_EBB, "NT_PPC_EBB (ppc EBB registers)"}, + {ELF::NT_PPC_PMU, "NT_PPC_PMU (ppc PMU registers)"}, + {ELF::NT_PPC_TM_CGPR, "NT_PPC_TM_CGPR (ppc checkpointed GPR registers)"}, + {ELF::NT_PPC_TM_CFPR, + "NT_PPC_TM_CFPR (ppc checkpointed floating point registers)"}, + {ELF::NT_PPC_TM_CVMX, + "NT_PPC_TM_CVMX (ppc checkpointed Altivec registers)"}, + {ELF::NT_PPC_TM_CVSX, "NT_PPC_TM_CVSX (ppc checkpointed VSX registers)"}, + {ELF::NT_PPC_TM_SPR, "NT_PPC_TM_SPR (ppc TM special purpose registers)"}, + {ELF::NT_PPC_TM_CTAR, "NT_PPC_TM_CTAR (ppc checkpointed TAR register)"}, + {ELF::NT_PPC_TM_CPPR, "NT_PPC_TM_CPPR (ppc checkpointed PPR register)"}, + {ELF::NT_PPC_TM_CDSCR, + "NT_PPC_TM_CDSCR (ppc checkpointed DSCR register)"}, + + {ELF::NT_386_TLS, "NT_386_TLS (x86 TLS information)"}, + {ELF::NT_386_IOPERM, "NT_386_IOPERM (x86 I/O permissions)"}, + {ELF::NT_X86_XSTATE, "NT_X86_XSTATE (x86 XSAVE extended state)"}, + + {ELF::NT_S390_HIGH_GPRS, + "NT_S390_HIGH_GPRS (s390 upper register halves)"}, + {ELF::NT_S390_TIMER, "NT_S390_TIMER (s390 timer register)"}, + {ELF::NT_S390_TODCMP, "NT_S390_TODCMP (s390 TOD comparator register)"}, + {ELF::NT_S390_TODPREG, + "NT_S390_TODPREG (s390 TOD programmable register)"}, + {ELF::NT_S390_CTRS, "NT_S390_CTRS (s390 control registers)"}, + {ELF::NT_S390_PREFIX, "NT_S390_PREFIX (s390 prefix register)"}, + {ELF::NT_S390_LAST_BREAK, + "NT_S390_LAST_BREAK (s390 last breaking event address)"}, + {ELF::NT_S390_SYSTEM_CALL, + "NT_S390_SYSTEM_CALL (s390 system call restart data)"}, + {ELF::NT_S390_TDB, "NT_S390_TDB (s390 transaction diagnostic block)"}, + {ELF::NT_S390_VXRS_LOW, + "NT_S390_VXRS_LOW (s390 vector registers 0-15 upper half)"}, + {ELF::NT_S390_VXRS_HIGH, + "NT_S390_VXRS_HIGH (s390 vector registers 16-31)"}, + {ELF::NT_S390_GS_CB, "NT_S390_GS_CB (s390 guarded-storage registers)"}, + {ELF::NT_S390_GS_BC, + "NT_S390_GS_BC (s390 guarded-storage broadcast control)"}, + + {ELF::NT_ARM_VFP, "NT_ARM_VFP (arm VFP registers)"}, + {ELF::NT_ARM_TLS, "NT_ARM_TLS (AArch TLS registers)"}, + {ELF::NT_ARM_HW_BREAK, + "NT_ARM_HW_BREAK (AArch hardware breakpoint registers)"}, + {ELF::NT_ARM_HW_WATCH, + "NT_ARM_HW_WATCH (AArch hardware watchpoint registers)"}, + + {ELF::NT_FILE, "NT_FILE (mapped files)"}, + {ELF::NT_PRXFPREG, "NT_PRXFPREG (user_xfpregs structure)"}, + {ELF::NT_SIGINFO, "NT_SIGINFO (siginfo_t data)"}, + }; + + for (const auto &Note : Notes) + if (Note.ID == NT) + return Note.Name; + + return ""; +} + static std::string getGNUNoteTypeName(const uint32_t NT) { static const struct { uint32_t ID; @@ -4207,13 +4522,85 @@ static AMDGPUNote getAMDGPUNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) { } } +struct CoreFileMapping { + uint64_t Start, End, Offset; + StringRef Filename; +}; + +struct CoreNote { + uint64_t PageSize; + std::vector<CoreFileMapping> Mappings; +}; + +static Expected<CoreNote> readCoreNote(DataExtractor Desc) { + // Expected format of the NT_FILE note description: + // 1. # of file mappings (call it N) + // 2. Page size + // 3. N (start, end, offset) triples + // 4. N packed filenames (null delimited) + // Each field is an Elf_Addr, except for filenames which are char* strings. + + CoreNote Ret; + const int Bytes = Desc.getAddressSize(); + + if (!Desc.isValidOffsetForAddress(2)) + return createStringError(object_error::parse_failed, + "malformed note: header too short"); + if (Desc.getData().back() != 0) + return createStringError(object_error::parse_failed, + "malformed note: not NUL terminated"); + + uint64_t DescOffset = 0; + uint64_t FileCount = Desc.getAddress(&DescOffset); + Ret.PageSize = Desc.getAddress(&DescOffset); + + if (!Desc.isValidOffsetForAddress(3 * FileCount * Bytes)) + return createStringError(object_error::parse_failed, + "malformed note: too short for number of files"); + + uint64_t FilenamesOffset = 0; + DataExtractor Filenames( + Desc.getData().drop_front(DescOffset + 3 * FileCount * Bytes), + Desc.isLittleEndian(), Desc.getAddressSize()); + + Ret.Mappings.resize(FileCount); + for (CoreFileMapping &Mapping : Ret.Mappings) { + if (!Filenames.isValidOffsetForDataOfSize(FilenamesOffset, 1)) + return createStringError(object_error::parse_failed, + "malformed note: too few filenames"); + Mapping.Start = Desc.getAddress(&DescOffset); + Mapping.End = Desc.getAddress(&DescOffset); + Mapping.Offset = Desc.getAddress(&DescOffset); + Mapping.Filename = Filenames.getCStrRef(&FilenamesOffset); + } + + return Ret; +} + +template <typename ELFT> +static void printCoreNote(raw_ostream &OS, const CoreNote &Note) { + // Length of "0x<address>" string. + const int FieldWidth = ELFT::Is64Bits ? 18 : 10; + + OS << " Page size: " << format_decimal(Note.PageSize, 0) << '\n'; + OS << " " << right_justify("Start", FieldWidth) << " " + << right_justify("End", FieldWidth) << " " + << right_justify("Page Offset", FieldWidth) << '\n'; + for (const CoreFileMapping &Mapping : Note.Mappings) { + OS << " " << format_hex(Mapping.Start, FieldWidth) << " " + << format_hex(Mapping.End, FieldWidth) << " " + << format_hex(Mapping.Offset, FieldWidth) << "\n " + << Mapping.Filename << '\n'; + } +} + template <class ELFT> void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { auto PrintHeader = [&](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"; + << " Owner Data size \tDescription\n"; }; auto ProcessNote = [&](const Elf_Note &Note) { @@ -4221,55 +4608,81 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { ArrayRef<uint8_t> Descriptor = Note.getDesc(); Elf_Word Type = Note.getType(); - OS << " " << Name << std::string(22 - Name.size(), ' ') + // Print the note owner/type. + OS << " " << left_justify(Name, 20) << ' ' << format_hex(Descriptor.size(), 10) << '\t'; - if (Name == "GNU") { OS << getGNUNoteTypeName(Type) << '\n'; - printGNUNote<ELFT>(OS, Type, Descriptor); } else if (Name == "FreeBSD") { OS << getFreeBSDNoteTypeName(Type) << '\n'; } else if (Name == "AMD") { OS << getAMDNoteTypeName(Type) << '\n'; + } else if (Name == "AMDGPU") { + OS << getAMDGPUNoteTypeName(Type) << '\n'; + } else { + StringRef NoteType = Obj->getHeader()->e_type == ELF::ET_CORE + ? getCoreNoteTypeName(Type) + : getGenericNoteTypeName(Type); + if (!NoteType.empty()) + OS << NoteType << '\n'; + else + OS << "Unknown note type: (" << format_hex(Type, 10) << ")\n"; + } + + // Print the description, or fallback to printing raw bytes for unknown + // owners. + if (Name == "GNU") { + printGNUNote<ELFT>(OS, Type, Descriptor); + } else if (Name == "AMD") { const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); if (!N.Type.empty()) OS << " " << N.Type << ":\n " << N.Value << '\n'; } else if (Name == "AMDGPU") { - OS << getAMDGPUNoteTypeName(Type) << '\n'; const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); if (!N.Type.empty()) OS << " " << N.Type << ":\n " << N.Value << '\n'; - } else { - StringRef NoteType = getGenericNoteTypeName(Type); - if (!NoteType.empty()) - OS << NoteType; - else - OS << "Unknown note type: (" << format_hex(Type, 10) << ')'; + } else if (Name == "CORE") { + if (Type == ELF::NT_FILE) { + DataExtractor DescExtractor(Descriptor, + ELFT::TargetEndianness == support::little, + sizeof(Elf_Addr)); + Expected<CoreNote> Note = readCoreNote(DescExtractor); + if (Note) + printCoreNote<ELFT>(OS, *Note); + else + reportWarning(Note.takeError(), this->FileName); + } + } else if (!Descriptor.empty()) { + OS << " description data:"; + for (uint8_t B : Descriptor) + OS << " " << format("%02x", B); + OS << '\n'; } - OS << '\n'; }; - if (Obj->getHeader()->e_type == ELF::ET_CORE) { - for (const auto &P : unwrapOrError(Obj->program_headers())) { - if (P.p_type != PT_NOTE) + ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, 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(P.p_offset, P.p_filesz); + PrintHeader(S.sh_offset, S.sh_size); Error Err = Error::success(); - for (const auto &Note : Obj->notes(P, Err)) + for (const auto &Note : Obj->notes(S, Err)) ProcessNote(Note); if (Err) - error(std::move(Err)); + reportError(std::move(Err), this->FileName); } } else { - for (const auto &S : unwrapOrError(Obj->sections())) { - if (S.sh_type != SHT_NOTE) + for (const auto &P : + unwrapOrError(this->FileName, Obj->program_headers())) { + if (P.p_type != PT_NOTE) continue; - PrintHeader(S.sh_offset, S.sh_size); + PrintHeader(P.p_offset, P.p_filesz); Error Err = Error::success(); - for (const auto &Note : Obj->notes(S, Err)) + for (const auto &Note : Obj->notes(P, Err)) ProcessNote(Note); if (Err) - error(std::move(Err)); + reportError(std::move(Err), this->FileName); } } } @@ -4279,6 +4692,294 @@ void GNUStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) { OS << "printELFLinkerOptions not implemented!\n"; } +// Used for printing section names in places where possible errors can be +// ignored. +static StringRef getSectionName(const SectionRef &Sec) { + Expected<StringRef> NameOrErr = Sec.getName(); + if (NameOrErr) + return *NameOrErr; + consumeError(NameOrErr.takeError()); + return "<?>"; +} + +// Used for printing symbol names in places where possible errors can be +// ignored. +static std::string getSymbolName(const ELFSymbolRef &Sym) { + Expected<StringRef> NameOrErr = Sym.getName(); + if (NameOrErr) + return maybeDemangle(*NameOrErr); + consumeError(NameOrErr.takeError()); + return "<?>"; +} + +template <class ELFT> +void DumpStyle<ELFT>::printFunctionStackSize( + const ELFObjectFile<ELFT> *Obj, uint64_t SymValue, 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; + for (const ELFSymbolRef &Symbol : Obj->symbols()) { + Expected<uint64_t> SymAddrOrErr = Symbol.getAddress(); + if (!SymAddrOrErr) { + consumeError(SymAddrOrErr.takeError()); + continue; + } + if (Symbol.getELFType() == ELF::STT_FUNC && *SymAddrOrErr == SymValue) { + // Check if the symbol is in the right section. + if (FunctionSec.containsSymbol(Symbol)) { + FuncSym = Symbol; + break; + } + } + } + + std::string FuncName = "?"; + // A valid SymbolRef has a non-null object file pointer. + if (FuncSym.BasicSymbolRef::getObject()) + FuncName = getSymbolName(FuncSym); + else + reportWarning( + createError("could not identify function symbol for stack size entry"), + Obj->getFileName()); + + // Extract the size. The expectation is that Offset is pointing to the right + // place, i.e. past the function address. + uint64_t PrevOffset = *Offset; + uint64_t StackSize = Data.getULEB128(Offset); + // getULEB128() does not advance Offset if it is not able to extract a valid + // integer. + if (*Offset == PrevOffset) + reportError( + createStringError(object_error::parse_failed, + "could not extract a valid stack size in section %s", + SectionName.data()), + Obj->getFileName()); + + printStackSizeEntry(StackSize, FuncName); +} + +template <class ELFT> +void GNUStyle<ELFT>::printStackSizeEntry(uint64_t Size, StringRef FuncName) { + OS.PadToColumn(2); + OS << format_decimal(Size, 11); + OS.PadToColumn(18); + OS << FuncName << "\n"; +} + +template <class ELFT> +void DumpStyle<ELFT>::printStackSize(const ELFObjectFile<ELFT> *Obj, + RelocationRef Reloc, + SectionRef FunctionSec, + const StringRef &StackSizeSectionName, + const RelocationResolver &Resolver, + DataExtractor Data) { + // This function ignores potentially erroneous input, unless it is directly + // related to stack size reporting. + object::symbol_iterator RelocSym = Reloc.getSymbol(); + uint64_t RelocSymValue = 0; + StringRef FileStr = Obj->getFileName(); + if (RelocSym != Obj->symbol_end()) { + // Ensure that the relocation symbol is in the function section, i.e. the + // section where the functions whose stack sizes we are reporting are + // located. + auto SectionOrErr = RelocSym->getSection(); + if (!SectionOrErr) { + reportWarning( + createError("cannot identify the section for relocation symbol '" + + getSymbolName(*RelocSym) + "'"), + FileStr); + consumeError(SectionOrErr.takeError()); + } else if (*SectionOrErr != FunctionSec) { + reportWarning(createError("relocation symbol '" + + getSymbolName(*RelocSym) + + "' is not in the expected section"), + FileStr); + // Pretend that the symbol is in the correct section and report its + // stack size anyway. + FunctionSec = **SectionOrErr; + } + + Expected<uint64_t> RelocSymValueOrErr = RelocSym->getValue(); + if (RelocSymValueOrErr) + RelocSymValue = *RelocSymValueOrErr; + else + consumeError(RelocSymValueOrErr.takeError()); + } + + uint64_t Offset = Reloc.getOffset(); + if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Elf_Addr) + 1)) + reportError( + createStringError(object_error::parse_failed, + "found invalid relocation offset into section %s " + "while trying to extract a stack size entry", + StackSizeSectionName.data()), + FileStr); + + uint64_t Addend = Data.getAddress(&Offset); + uint64_t SymValue = Resolver(Reloc, RelocSymValue, Addend); + this->printFunctionStackSize(Obj, SymValue, FunctionSec, StackSizeSectionName, + Data, &Offset); +} + +template <class ELFT> +void DumpStyle<ELFT>::printNonRelocatableStackSizes( + const ELFObjectFile<ELFT> *Obj, std::function<void()> PrintHeader) { + // This function ignores potentially erroneous input, unless it is directly + // related to stack size reporting. + const ELFFile<ELFT> *EF = Obj->getELFFile(); + StringRef FileStr = Obj->getFileName(); + for (const SectionRef &Sec : Obj->sections()) { + StringRef SectionName = getSectionName(Sec); + if (SectionName != ".stack_sizes") + continue; + PrintHeader(); + const Elf_Shdr *ElfSec = Obj->getSection(Sec.getRawDataRefImpl()); + 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 + // size. Check for an extra byte before we try to process the entry. + if (!Data.isValidOffsetForDataOfSize(Offset, sizeof(Elf_Addr) + 1)) { + reportError( + createStringError( + object_error::parse_failed, + "section %s ended while trying to extract a stack size entry", + SectionName.data()), + FileStr); + } + uint64_t SymValue = Data.getAddress(&Offset); + printFunctionStackSize(Obj, SymValue, Obj->toSectionRef(FunctionELFSec), + SectionName, Data, &Offset); + } + } +} + +template <class ELFT> +void DumpStyle<ELFT>::printRelocatableStackSizes( + const ELFObjectFile<ELFT> *Obj, std::function<void()> PrintHeader) { + const ELFFile<ELFT> *EF = Obj->getELFFile(); + + // Build a map between stack size sections and their corresponding relocation + // sections. + llvm::MapVector<SectionRef, SectionRef> StackSizeRelocMap; + const SectionRef NullSection{}; + + for (const SectionRef &Sec : Obj->sections()) { + StringRef SectionName; + if (Expected<StringRef> NameOrErr = Sec.getName()) + SectionName = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + // A stack size section that we haven't encountered yet is mapped to the + // null section until we find its corresponding relocation section. + if (SectionName == ".stack_sizes") + if (StackSizeRelocMap.count(Sec) == 0) { + StackSizeRelocMap[Sec] = NullSection; + continue; + } + + // Check relocation sections if they are relocating contents of a + // stack sizes section. + const Elf_Shdr *ElfSec = Obj->getSection(Sec.getRawDataRefImpl()); + uint32_t SectionType = ElfSec->sh_type; + if (SectionType != ELF::SHT_RELA && SectionType != ELF::SHT_REL) + continue; + + Expected<section_iterator> RelSecOrErr = Sec.getRelocatedSection(); + if (!RelSecOrErr) + reportError(createStringError(object_error::parse_failed, + "%s: failed to get a relocated section: %s", + SectionName.data(), + toString(RelSecOrErr.takeError()).c_str()), + Obj->getFileName()); + + const Elf_Shdr *ContentsSec = + Obj->getSection((*RelSecOrErr)->getRawDataRefImpl()); + Expected<StringRef> ContentsSectionNameOrErr = + EF->getSectionName(ContentsSec); + if (!ContentsSectionNameOrErr) { + consumeError(ContentsSectionNameOrErr.takeError()); + continue; + } + if (*ContentsSectionNameOrErr != ".stack_sizes") + continue; + // Insert a mapping from the stack sizes section to its relocation section. + StackSizeRelocMap[Obj->toSectionRef(ContentsSec)] = Sec; + } + + for (const auto &StackSizeMapEntry : StackSizeRelocMap) { + PrintHeader(); + const SectionRef &StackSizesSec = StackSizeMapEntry.first; + const SectionRef &RelocSec = StackSizeMapEntry.second; + + // Warn about stack size sections without a relocation section. + StringRef StackSizeSectionName = getSectionName(StackSizesSec); + if (RelocSec == NullSection) { + reportWarning(createError("section " + StackSizeSectionName + + " does not have a corresponding " + "relocation section"), + Obj->getFileName()); + continue; + } + + // 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 *StackSizesELFSec = + Obj->getSection(StackSizesSec.getRawDataRefImpl()); + const SectionRef FunctionSec = Obj->toSectionRef(unwrapOrError( + this->FileName, EF->getSection(StackSizesELFSec->sh_link))); + + bool (*IsSupportedFn)(uint64_t); + RelocationResolver Resolver; + std::tie(IsSupportedFn, Resolver) = getRelocationResolver(*Obj); + auto Contents = unwrapOrError(this->FileName, StackSizesSec.getContents()); + DataExtractor Data(Contents, Obj->isLittleEndian(), sizeof(Elf_Addr)); + for (const RelocationRef &Reloc : RelocSec.relocations()) { + if (!IsSupportedFn || !IsSupportedFn(Reloc.getType())) + reportError(createStringError( + object_error::parse_failed, + "unsupported relocation type in section %s: %s", + getSectionName(RelocSec).data(), + EF->getRelocationTypeName(Reloc.getType()).data()), + Obj->getFileName()); + this->printStackSize(Obj, Reloc, FunctionSec, StackSizeSectionName, + Resolver, Data); + } + } +} + +template <class ELFT> +void GNUStyle<ELFT>::printStackSizes(const ELFObjectFile<ELFT> *Obj) { + bool HeaderHasBeenPrinted = false; + auto PrintHeader = [&]() { + if (HeaderHasBeenPrinted) + return; + OS << "\nStack Sizes:\n"; + OS.PadToColumn(9); + OS << "Size"; + OS.PadToColumn(18); + OS << "Function\n"; + HeaderHasBeenPrinted = true; + }; + + // For non-relocatable objects, look directly for sections whose name starts + // with .stack_sizes and process the contents. + if (Obj->isRelocatableObject()) + this->printRelocatableStackSizes(Obj, PrintHeader); + else + this->printNonRelocatableStackSizes(Obj, PrintHeader); +} + template <class ELFT> void GNUStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) { size_t Bias = ELFT::Is64Bits ? 8 : 0; @@ -4402,6 +5103,45 @@ void GNUStyle<ELFT>::printMipsPLT(const MipsGOTParser<ELFT> &Parser) { } } +template <class ELFT> +void GNUStyle<ELFT>::printMipsABIFlags(const ELFObjectFile<ELFT> *ObjF) { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + const Elf_Shdr *Shdr = + findSectionByName(*Obj, ObjF->getFileName(), ".MIPS.abiflags"); + if (!Shdr) + return; + + ArrayRef<uint8_t> Sec = + unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(Shdr)); + if (Sec.size() != sizeof(Elf_Mips_ABIFlags<ELFT>)) + reportError(createError(".MIPS.abiflags section has a wrong size"), + ObjF->getFileName()); + + auto *Flags = reinterpret_cast<const Elf_Mips_ABIFlags<ELFT> *>(Sec.data()); + + OS << "MIPS ABI Flags Version: " << Flags->version << "\n\n"; + OS << "ISA: MIPS" << int(Flags->isa_level); + if (Flags->isa_rev > 1) + OS << "r" << int(Flags->isa_rev); + OS << "\n"; + OS << "GPR size: " << getMipsRegisterSize(Flags->gpr_size) << "\n"; + OS << "CPR1 size: " << getMipsRegisterSize(Flags->cpr1_size) << "\n"; + OS << "CPR2 size: " << getMipsRegisterSize(Flags->cpr2_size) << "\n"; + OS << "FP ABI: " << printEnum(Flags->fp_abi, makeArrayRef(ElfMipsFpABIType)) + << "\n"; + OS << "ISA Extension: " + << printEnum(Flags->isa_ext, makeArrayRef(ElfMipsISAExtType)) << "\n"; + if (Flags->ases == 0) + OS << "ASEs: None\n"; + else + // FIXME: Print each flag on a separate line. + OS << "ASEs: " << printFlags(Flags->ases, makeArrayRef(ElfMipsASEFlags)) + << "\n"; + OS << "FLAGS 1: " << format_hex_no_prefix(Flags->flags1, 8, false) << "\n"; + OS << "FLAGS 2: " << format_hex_no_prefix(Flags->flags2, 8, false) << "\n"; + OS << "\n"; +} + template <class ELFT> void LLVMStyle<ELFT>::printFileHeaders(const ELFO *Obj) { const Elf_Ehdr *E = Obj->getHeader(); { @@ -4455,16 +5195,17 @@ template <class ELFT> void LLVMStyle<ELFT>::printFileHeaders(const ELFO *Obj) { W.printNumber("ProgramHeaderEntrySize", E->e_phentsize); W.printNumber("ProgramHeaderCount", E->e_phnum); W.printNumber("SectionHeaderEntrySize", E->e_shentsize); - W.printString("SectionHeaderCount", getSectionHeadersNumString(Obj)); + W.printString("SectionHeaderCount", + getSectionHeadersNumString(Obj, this->FileName)); W.printString("StringTableSectionIndex", - getSectionHeaderTableIndexString(Obj)); + getSectionHeaderTableIndexString(Obj, this->FileName)); } } template <class ELFT> void LLVMStyle<ELFT>::printGroupSections(const ELFO *Obj) { DictScope Lists(W, "Groups"); - std::vector<GroupSection> V = getGroups<ELFT>(Obj); + std::vector<GroupSection> V = getGroups<ELFT>(Obj, this->FileName); DenseMap<uint64_t, const GroupSection *> Map = mapSectionsToGroups(V); for (const GroupSection &G : V) { DictScope D(W, "Group"); @@ -4499,7 +5240,7 @@ template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const ELFO *Obj) { ListScope D(W, "Relocations"); int SectionNumber = -1; - for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { + for (const Elf_Shdr &Sec : unwrapOrError(this->FileName, Obj->sections())) { ++SectionNumber; if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA && @@ -4508,7 +5249,7 @@ template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const ELFO *Obj) { Sec.sh_type != ELF::SHT_ANDROID_RELR) continue; - StringRef Name = unwrapOrError(Obj->getSectionName(&Sec)); + StringRef Name = unwrapOrError(this->FileName, Obj->getSectionName(&Sec)); W.startLine() << "Section (" << SectionNumber << ") " << Name << " {\n"; W.indent(); @@ -4522,11 +5263,12 @@ template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const ELFO *Obj) { template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) { - const Elf_Shdr *SymTab = unwrapOrError(Obj->getSection(Sec->sh_link)); + const Elf_Shdr *SymTab = + unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); switch (Sec->sh_type) { case ELF::SHT_REL: - for (const Elf_Rel &R : unwrapOrError(Obj->rels(Sec))) { + for (const Elf_Rel &R : unwrapOrError(this->FileName, Obj->rels(Sec))) { Elf_Rela Rela; Rela.r_offset = R.r_offset; Rela.r_info = R.r_info; @@ -4535,17 +5277,18 @@ void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) { } break; case ELF::SHT_RELA: - for (const Elf_Rela &R : unwrapOrError(Obj->relas(Sec))) + for (const Elf_Rela &R : unwrapOrError(this->FileName, Obj->relas(Sec))) printRelocation(Obj, R, SymTab); break; case ELF::SHT_RELR: case ELF::SHT_ANDROID_RELR: { - Elf_Relr_Range Relrs = unwrapOrError(Obj->relrs(Sec)); + Elf_Relr_Range Relrs = unwrapOrError(this->FileName, Obj->relrs(Sec)); if (opts::RawRelr) { for (const Elf_Relr &R : Relrs) W.startLine() << W.hex(R) << "\n"; } else { - std::vector<Elf_Rela> RelrRelas = unwrapOrError(Obj->decode_relrs(Relrs)); + std::vector<Elf_Rela> RelrRelas = + unwrapOrError(this->FileName, Obj->decode_relrs(Relrs)); for (const Elf_Rela &R : RelrRelas) printRelocation(Obj, R, SymTab); } @@ -4553,7 +5296,8 @@ void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) { } case ELF::SHT_ANDROID_REL: case ELF::SHT_ANDROID_RELA: - for (const Elf_Rela &R : unwrapOrError(Obj->android_relas(Sec))) + for (const Elf_Rela &R : + unwrapOrError(this->FileName, Obj->android_relas(Sec))) printRelocation(Obj, R, SymTab); break; } @@ -4565,13 +5309,16 @@ void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, Elf_Rela Rel, SmallString<32> RelocName; Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); std::string TargetName; - const Elf_Sym *Sym = unwrapOrError(Obj->getRelocationSymbol(&Rel, SymTab)); + 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(Obj->getSectionName(Sec)); + TargetName = unwrapOrError(this->FileName, Obj->getSectionName(Sec)); } else if (Sym) { - StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTab)); + StringRef StrTable = + unwrapOrError(this->FileName, Obj->getStringTableForSymtab(*SymTab)); TargetName = this->dumper()->getFullSymbolName( Sym, StrTable, SymTab->sh_type == SHT_DYNSYM /* IsDynamic */); } @@ -4596,10 +5343,11 @@ void LLVMStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { ListScope SectionsD(W, "Sections"); int SectionIndex = -1; - ArrayRef<Elf_Shdr> Sections = unwrapOrError(Obj->sections()); + ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, Obj->sections()); const ELFObjectFile<ELFT> *ElfObj = this->dumper()->getElfObject(); for (const Elf_Shdr &Sec : Sections) { - StringRef Name = getSectionName(Sec, *ElfObj, Sections); + StringRef Name = unwrapOrError( + ElfObj->getFileName(), Obj->getSectionName(&Sec, this->WarningHandler)); DictScope SectionD(W, "Section"); W.printNumber("Index", ++SectionIndex); W.printNumber("Name", Name, Sec.sh_name); @@ -4652,19 +5400,25 @@ void LLVMStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { if (opts::SectionSymbols) { ListScope D(W, "Symbols"); const Elf_Shdr *Symtab = this->dumper()->getDotSymtabSec(); - StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*Symtab)); + StringRef StrTable = + unwrapOrError(this->FileName, Obj->getStringTableForSymtab(*Symtab)); - for (const Elf_Sym &Sym : unwrapOrError(Obj->symbols(Symtab))) { + for (const Elf_Sym &Sym : + unwrapOrError(this->FileName, Obj->symbols(Symtab))) { const Elf_Shdr *SymSec = unwrapOrError( + this->FileName, Obj->getSection(&Sym, Symtab, this->dumper()->getShndxTable())); if (SymSec == &Sec) - printSymbol(Obj, &Sym, unwrapOrError(Obj->symbols(Symtab)).begin(), - StrTable, false); + printSymbol( + Obj, &Sym, + unwrapOrError(this->FileName, Obj->symbols(Symtab)).begin(), + StrTable, false, false); } } if (opts::SectionData && Sec.sh_type != ELF::SHT_NOBITS) { - ArrayRef<uint8_t> Data = unwrapOrError(Obj->getSectionContents(&Sec)); + ArrayRef<uint8_t> Data = + unwrapOrError(this->FileName, Obj->getSectionContents(&Sec)); W.printBinaryBlock( "SectionData", StringRef(reinterpret_cast<const char *>(Data.data()), Data.size())); @@ -4675,7 +5429,8 @@ void LLVMStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { template <class ELFT> void LLVMStyle<ELFT>::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First, StringRef StrTable, - bool IsDynamic) { + bool IsDynamic, + bool /*NonVisibilityBitsUsed*/) { unsigned SectionIndex = 0; StringRef SectionName; this->dumper()->getSectionNameIndex(Symbol, First, SectionName, SectionIndex); @@ -4786,7 +5541,8 @@ void LLVMStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) { } if (DynRelrRegion.Size > 0) { Elf_Relr_Range Relrs = this->dumper()->dyn_relrs(); - std::vector<Elf_Rela> RelrRelas = unwrapOrError(Obj->decode_relrs(Relrs)); + std::vector<Elf_Rela> RelrRelas = + unwrapOrError(this->FileName, Obj->decode_relrs(Relrs)); for (const Elf_Rela &Rela : RelrRelas) printDynamicRelocation(Obj, Rela); } @@ -4809,11 +5565,9 @@ template <class ELFT> void LLVMStyle<ELFT>::printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel) { SmallString<32> RelocName; Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); - std::string SymbolName; - uint32_t SymIndex = Rel.getSymbol(Obj->isMips64EL()); - const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex; - SymbolName = maybeDemangle( - unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable()))); + std::string SymbolName = + getSymbolForReloc(Obj, this->FileName, this->dumper(), Rel).Name; + if (opts::ExpandRelocs) { DictScope Group(W, "Relocation"); W.printHex("Offset", Rel.r_offset); @@ -4842,7 +5596,8 @@ template <class ELFT> void LLVMStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { ListScope L(W, "ProgramHeaders"); - for (const Elf_Phdr &Phdr : unwrapOrError(Obj->program_headers())) { + for (const Elf_Phdr &Phdr : + unwrapOrError(this->FileName, Obj->program_headers())) { DictScope P(W, "ProgramHeader"); W.printHex("Type", getElfSegmentType(Obj->getHeader()->e_machine, Phdr.p_type), @@ -4860,23 +5615,16 @@ void LLVMStyle<ELFT>::printProgramHeaders(const ELFO *Obj) { template <class ELFT> void LLVMStyle<ELFT>::printVersionSymbolSection(const ELFFile<ELFT> *Obj, const Elf_Shdr *Sec) { - DictScope SS(W, "Version symbols"); + ListScope SS(W, "VersionSymbols"); if (!Sec) return; - StringRef SecName = unwrapOrError(Obj->getSectionName(Sec)); - W.printNumber("Section Name", SecName, Sec->sh_name); - W.printHex("Address", Sec->sh_addr); - W.printHex("Offset", Sec->sh_offset); - W.printNumber("Link", Sec->sh_link); - const uint8_t *VersymBuf = reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); const ELFDumper<ELFT> *Dumper = this->dumper(); StringRef StrTable = Dumper->getDynamicStringTable(); // Same number of entries in the dynamic symbol table (DT_SYMTAB). - ListScope Syms(W, "Symbols"); for (const Elf_Sym &Sym : Dumper->dynamic_symbols()) { DictScope S(W, "Symbol"); const Elf_Versym *Versym = reinterpret_cast<const Elf_Versym *>(VersymBuf); @@ -4891,7 +5639,7 @@ void LLVMStyle<ELFT>::printVersionSymbolSection(const ELFFile<ELFT> *Obj, template <class ELFT> void LLVMStyle<ELFT>::printVersionDefinitionSection(const ELFFile<ELFT> *Obj, const Elf_Shdr *Sec) { - DictScope SD(W, "SHT_GNU_verdef"); + ListScope SD(W, "VersionDefinitions"); if (!Sec) return; @@ -4899,7 +5647,8 @@ void LLVMStyle<ELFT>::printVersionDefinitionSection(const ELFFile<ELFT> *Obj, reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size; const uint8_t *VerdefBuf = SecStartAddress; - const Elf_Shdr *StrTab = unwrapOrError(Obj->getSection(Sec->sh_link)); + const Elf_Shdr *StrTab = + unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); unsigned VerDefsNum = Sec->sh_info; while (VerDefsNum--) { @@ -4938,13 +5687,14 @@ void LLVMStyle<ELFT>::printVersionDefinitionSection(const ELFFile<ELFT> *Obj, template <class ELFT> void LLVMStyle<ELFT>::printVersionDependencySection(const ELFFile<ELFT> *Obj, const Elf_Shdr *Sec) { - DictScope SD(W, "SHT_GNU_verneed"); + ListScope SD(W, "VersionRequirements"); if (!Sec) return; const uint8_t *SecData = reinterpret_cast<const uint8_t *>(Obj->base() + Sec->sh_offset); - const Elf_Shdr *StrTab = unwrapOrError(Obj->getSection(Sec->sh_link)); + const Elf_Shdr *StrTab = + unwrapOrError(this->FileName, Obj->getSection(Sec->sh_link)); const uint8_t *VerneedBuf = SecData; unsigned VerneedNum = Sec->sh_info; @@ -4986,37 +5736,62 @@ void LLVMStyle<ELFT>::printCGProfile(const ELFFile<ELFT> *Obj) { ListScope L(W, "CGProfile"); if (!this->dumper()->getDotCGProfileSec()) return; - auto CGProfile = - unwrapOrError(Obj->template getSectionContentsAsArray<Elf_CGProfile>( - this->dumper()->getDotCGProfileSec())); + auto CGProfile = unwrapOrError( + this->FileName, Obj->template getSectionContentsAsArray<Elf_CGProfile>( + this->dumper()->getDotCGProfileSec())); for (const Elf_CGProfile &CGPE : CGProfile) { DictScope D(W, "CGProfileEntry"); - 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( + "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("Weight", CGPE.cgp_weight); } } +static Expected<std::vector<uint64_t>> toULEB128Array(ArrayRef<uint8_t> Data) { + std::vector<uint64_t> Ret; + const uint8_t *Cur = Data.begin(); + const uint8_t *End = Data.end(); + while (Cur != End) { + unsigned Size; + const char *Err; + Ret.push_back(decodeULEB128(Cur, &Size, End, &Err)); + if (Err) + return createError(Err); + Cur += Size; + } + return Ret; +} + template <class ELFT> void LLVMStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) { ListScope L(W, "Addrsig"); if (!this->dumper()->getDotAddrsigSec()) return; ArrayRef<uint8_t> Contents = unwrapOrError( + this->FileName, Obj->getSectionContents(this->dumper()->getDotAddrsigSec())); - const uint8_t *Cur = Contents.begin(); - const uint8_t *End = Contents.end(); - while (Cur != End) { - unsigned Size; - const char *Err; - uint64_t SymIndex = decodeULEB128(Cur, &Size, End, &Err); - if (Err) - reportError(Err); - W.printNumber("Sym", this->dumper()->getStaticSymbolName(SymIndex), - SymIndex); - Cur += Size; + Expected<std::vector<uint64_t>> V = toULEB128Array(Contents); + if (!V) { + reportWarning(V.takeError(), this->FileName); + return; + } + + for (uint64_t Sym : *V) { + Expected<std::string> NameOrErr = this->dumper()->getStaticSymbolName(Sym); + if (NameOrErr) { + W.printNumber("Sym", *NameOrErr, Sym); + continue; + } + reportWarning(NameOrErr.takeError(), this->FileName); + W.printNumber("Sym", "<?>", Sym); } } @@ -5051,6 +5826,17 @@ static void printGNUNoteLLVMStyle(uint32_t NoteType, ArrayRef<uint8_t> Desc, } } +static void printCoreNoteLLVMStyle(const CoreNote &Note, ScopedPrinter &W) { + W.printNumber("Page Size", Note.PageSize); + for (const CoreFileMapping &Mapping : Note.Mappings) { + ListScope D(W, "Mapping"); + W.printHex("Start", Mapping.Start); + W.printHex("End", Mapping.End); + W.printHex("Offset", Mapping.Offset); + W.printString("Filename", Mapping.Filename); + } +} + template <class ELFT> void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { ListScope L(W, "Notes"); @@ -5067,56 +5853,81 @@ void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { ArrayRef<uint8_t> Descriptor = Note.getDesc(); Elf_Word Type = Note.getType(); + // Print the note owner/type. W.printString("Owner", Name); W.printHex("Data size", Descriptor.size()); if (Name == "GNU") { W.printString("Type", getGNUNoteTypeName(Type)); - printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W); } else if (Name == "FreeBSD") { W.printString("Type", getFreeBSDNoteTypeName(Type)); } else if (Name == "AMD") { W.printString("Type", getAMDNoteTypeName(Type)); - const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); - if (!N.Type.empty()) - W.printString(N.Type, N.Value); } else if (Name == "AMDGPU") { W.printString("Type", getAMDGPUNoteTypeName(Type)); - const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); - if (!N.Type.empty()) - W.printString(N.Type, N.Value); } else { - StringRef NoteType = getGenericNoteTypeName(Type); + StringRef NoteType = Obj->getHeader()->e_type == ELF::ET_CORE + ? getCoreNoteTypeName(Type) + : getGenericNoteTypeName(Type); if (!NoteType.empty()) W.printString("Type", NoteType); else W.printString("Type", "Unknown (" + to_string(format_hex(Type, 10)) + ")"); } + + // Print the description, or fallback to printing raw bytes for unknown + // owners. + if (Name == "GNU") { + printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W); + } else if (Name == "AMD") { + const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); + if (!N.Type.empty()) + W.printString(N.Type, N.Value); + } else if (Name == "AMDGPU") { + const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); + if (!N.Type.empty()) + W.printString(N.Type, N.Value); + } else if (Name == "CORE") { + if (Type == ELF::NT_FILE) { + DataExtractor DescExtractor(Descriptor, + ELFT::TargetEndianness == support::little, + sizeof(Elf_Addr)); + Expected<CoreNote> Note = readCoreNote(DescExtractor); + if (Note) + printCoreNoteLLVMStyle(*Note, W); + else + reportWarning(Note.takeError(), this->FileName); + } + } else if (!Descriptor.empty()) { + W.printBinaryBlock("Description data", Descriptor); + } }; - if (Obj->getHeader()->e_type == ELF::ET_CORE) { - for (const auto &P : unwrapOrError(Obj->program_headers())) { - if (P.p_type != PT_NOTE) + ArrayRef<Elf_Shdr> Sections = unwrapOrError(this->FileName, 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(P.p_offset, P.p_filesz); + PrintHeader(S.sh_offset, S.sh_size); Error Err = Error::success(); - for (const auto &Note : Obj->notes(P, Err)) + for (const auto &Note : Obj->notes(S, Err)) ProcessNote(Note); if (Err) - error(std::move(Err)); + reportError(std::move(Err), this->FileName); } } else { - for (const auto &S : unwrapOrError(Obj->sections())) { - if (S.sh_type != SHT_NOTE) + for (const auto &P : + unwrapOrError(this->FileName, Obj->program_headers())) { + if (P.p_type != PT_NOTE) continue; DictScope D(W, "NoteSection"); - PrintHeader(S.sh_offset, S.sh_size); + PrintHeader(P.p_offset, P.p_filesz); Error Err = Error::success(); - for (const auto &Note : Obj->notes(S, Err)) + for (const auto &Note : Obj->notes(P, Err)) ProcessNote(Note); if (Err) - error(std::move(Err)); + reportError(std::move(Err), this->FileName); } } } @@ -5125,11 +5936,12 @@ template <class ELFT> void LLVMStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) { ListScope L(W, "LinkerOptions"); - for (const Elf_Shdr &Shdr : unwrapOrError(Obj->sections())) { + for (const Elf_Shdr &Shdr : unwrapOrError(this->FileName, Obj->sections())) { if (Shdr.sh_type != ELF::SHT_LLVM_LINKER_OPTIONS) continue; - ArrayRef<uint8_t> Contents = unwrapOrError(Obj->getSectionContents(&Shdr)); + ArrayRef<uint8_t> Contents = + unwrapOrError(this->FileName, Obj->getSectionContents(&Shdr)); for (const uint8_t *P = Contents.begin(), *E = Contents.end(); P < E; ) { StringRef Key = StringRef(reinterpret_cast<const char *>(P)); StringRef Value = @@ -5143,6 +5955,22 @@ void LLVMStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) { } template <class ELFT> +void LLVMStyle<ELFT>::printStackSizes(const ELFObjectFile<ELFT> *Obj) { + ListScope L(W, "StackSizes"); + if (Obj->isRelocatableObject()) + this->printRelocatableStackSizes(Obj, []() {}); + else + this->printNonRelocatableStackSizes(Obj, []() {}); +} + +template <class ELFT> +void LLVMStyle<ELFT>::printStackSizeEntry(uint64_t Size, StringRef FuncName) { + DictScope D(W, "Entry"); + W.printString("Function", FuncName); + W.printHex("Size", Size); +} + +template <class ELFT> void LLVMStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) { auto PrintEntry = [&](const Elf_Addr *E) { W.printHex("Address", Parser.getGotAddress(E)); @@ -5252,3 +6080,41 @@ void LLVMStyle<ELFT>::printMipsPLT(const MipsGOTParser<ELFT> &Parser) { } } } + +template <class ELFT> +void LLVMStyle<ELFT>::printMipsABIFlags(const ELFObjectFile<ELFT> *ObjF) { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); + const Elf_Shdr *Shdr = + findSectionByName(*Obj, ObjF->getFileName(), ".MIPS.abiflags"); + if (!Shdr) { + W.startLine() << "There is no .MIPS.abiflags section in the file.\n"; + return; + } + ArrayRef<uint8_t> Sec = + unwrapOrError(ObjF->getFileName(), Obj->getSectionContents(Shdr)); + if (Sec.size() != sizeof(Elf_Mips_ABIFlags<ELFT>)) { + W.startLine() << "The .MIPS.abiflags section has a wrong size.\n"; + return; + } + + auto *Flags = reinterpret_cast<const Elf_Mips_ABIFlags<ELFT> *>(Sec.data()); + + raw_ostream &OS = W.getOStream(); + DictScope GS(W, "MIPS ABI Flags"); + + W.printNumber("Version", Flags->version); + W.startLine() << "ISA: "; + if (Flags->isa_rev <= 1) + OS << format("MIPS%u", Flags->isa_level); + else + OS << format("MIPS%ur%u", Flags->isa_level, Flags->isa_rev); + OS << "\n"; + W.printEnum("ISA Extension", Flags->isa_ext, makeArrayRef(ElfMipsISAExtType)); + W.printFlags("ASEs", Flags->ases, makeArrayRef(ElfMipsASEFlags)); + W.printEnum("FP ABI", Flags->fp_abi, makeArrayRef(ElfMipsFpABIType)); + W.printNumber("GPR size", getMipsRegisterSize(Flags->gpr_size)); + W.printNumber("CPR1 size", getMipsRegisterSize(Flags->cpr1_size)); + W.printNumber("CPR2 size", getMipsRegisterSize(Flags->cpr2_size)); + W.printFlags("Flags 1", Flags->flags1, makeArrayRef(ElfMipsFlags1)); + W.printHex("Flags 2", Flags->flags2); +} diff --git a/tools/llvm-readobj/MachODumper.cpp b/tools/llvm-readobj/MachODumper.cpp index 32a3866eb2f2..20a60b3df699 100644 --- a/tools/llvm-readobj/MachODumper.cpp +++ b/tools/llvm-readobj/MachODumper.cpp @@ -214,6 +214,31 @@ static const EnumEntry<uint32_t> MachOHeaderFlags[] = { LLVM_READOBJ_ENUM_ENT(MachO, MH_APP_EXTENSION_SAFE), }; +static const EnumEntry<unsigned> MachOSectionTypes[] = { + { "Regular" , MachO::S_REGULAR }, + { "ZeroFill" , MachO::S_ZEROFILL }, + { "CStringLiterals" , MachO::S_CSTRING_LITERALS }, + { "4ByteLiterals" , MachO::S_4BYTE_LITERALS }, + { "8ByteLiterals" , MachO::S_8BYTE_LITERALS }, + { "LiteralPointers" , MachO::S_LITERAL_POINTERS }, + { "NonLazySymbolPointers" , MachO::S_NON_LAZY_SYMBOL_POINTERS }, + { "LazySymbolPointers" , MachO::S_LAZY_SYMBOL_POINTERS }, + { "SymbolStubs" , MachO::S_SYMBOL_STUBS }, + { "ModInitFuncPointers" , MachO::S_MOD_INIT_FUNC_POINTERS }, + { "ModTermFuncPointers" , MachO::S_MOD_TERM_FUNC_POINTERS }, + { "Coalesced" , MachO::S_COALESCED }, + { "GBZeroFill" , MachO::S_GB_ZEROFILL }, + { "Interposing" , MachO::S_INTERPOSING }, + { "16ByteLiterals" , MachO::S_16BYTE_LITERALS }, + { "DTraceDOF" , MachO::S_DTRACE_DOF }, + { "LazyDylibSymbolPointers" , MachO::S_LAZY_DYLIB_SYMBOL_POINTERS }, + { "ThreadLocalRegular" , MachO::S_THREAD_LOCAL_REGULAR }, + { "ThreadLocalZerofill" , MachO::S_THREAD_LOCAL_ZEROFILL }, + { "ThreadLocalVariables" , MachO::S_THREAD_LOCAL_VARIABLES }, + { "ThreadLocalVariablePointers" , MachO::S_THREAD_LOCAL_VARIABLE_POINTERS }, + { "ThreadLocalInitFunctionPointers", MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS } +}; + static const EnumEntry<unsigned> MachOSectionAttributes[] = { { "LocReloc" , 1 << 0 /*S_ATTR_LOC_RELOC */ }, { "ExtReloc" , 1 << 1 /*S_ATTR_EXT_RELOC */ }, @@ -440,10 +465,7 @@ void MachODumper::printSectionHeaders(const MachOObjectFile *Obj) { MachOSection MOSection; getSection(Obj, Section.getRawDataRefImpl(), MOSection); DataRefImpl DR = Section.getRawDataRefImpl(); - - StringRef Name; - error(Section.getName(Name)); - + StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); ArrayRef<char> RawName = Obj->getSectionRawName(DR); StringRef SegmentName = Obj->getSectionFinalSegmentName(DR); ArrayRef<char> RawSegmentName = Obj->getSectionRawFinalSegmentName(DR); @@ -459,7 +481,7 @@ void MachODumper::printSectionHeaders(const MachOObjectFile *Obj) { W.printHex("RelocationOffset", MOSection.RelocationTableOffset); W.printNumber("RelocationCount", MOSection.NumRelocationTableEntries); W.printEnum("Type", MOSection.Flags & 0xFF, - makeArrayRef(MachOSectionAttributes)); + makeArrayRef(MachOSectionTypes)); W.printFlags("Attributes", MOSection.Flags >> 8, makeArrayRef(MachOSectionAttributes)); W.printHex("Reserved1", MOSection.Reserved1); @@ -484,7 +506,8 @@ void MachODumper::printSectionHeaders(const MachOObjectFile *Obj) { } if (opts::SectionData && !Section.isBSS()) - W.printBinaryBlock("SectionData", unwrapOrError(Section.getContents())); + W.printBinaryBlock("SectionData", unwrapOrError(Obj->getFileName(), + Section.getContents())); } } @@ -493,9 +516,7 @@ void MachODumper::printRelocations() { std::error_code EC; for (const SectionRef &Section : Obj->sections()) { - StringRef Name; - error(Section.getName(Name)); - + StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); bool PrintedGroup = false; for (const RelocationRef &Reloc : Section.relocations()) { if (!PrintedGroup) { @@ -535,14 +556,13 @@ void MachODumper::printRelocation(const MachOObjectFile *Obj, if (Symbol != Obj->symbol_end()) { Expected<StringRef> TargetNameOrErr = Symbol->getName(); if (!TargetNameOrErr) - error(errorToErrorCode(TargetNameOrErr.takeError())); + reportError(TargetNameOrErr.takeError(), Obj->getFileName()); TargetName = *TargetNameOrErr; } } else if (!IsScattered) { section_iterator SecI = Obj->getRelocationSection(DR); - if (SecI != Obj->section_end()) { - error(SecI->getName(TargetName)); - } + if (SecI != Obj->section_end()) + TargetName = unwrapOrError(Obj->getFileName(), SecI->getName()); } if (TargetName.empty()) TargetName = "-"; @@ -610,10 +630,12 @@ void MachODumper::printSymbol(const SymbolRef &Symbol) { StringRef SectionName = ""; Expected<section_iterator> SecIOrErr = Symbol.getSection(); - error(errorToErrorCode(SecIOrErr.takeError())); + if (!SecIOrErr) + reportError(SecIOrErr.takeError(), Obj->getFileName()); + section_iterator SecI = *SecIOrErr; if (SecI != Obj->section_end()) - error(SecI->getName(SectionName)); + SectionName = unwrapOrError(Obj->getFileName(), SecI->getName()); DictScope D(W, "Symbol"); W.printNumber("Name", SymbolName, MOSymbol.StringIndex); @@ -643,7 +665,11 @@ void MachODumper::printStackMap() const { object::SectionRef StackMapSection; for (auto Sec : Obj->sections()) { StringRef Name; - Sec.getName(Name); + if (Expected<StringRef> NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + if (Name == "__llvm_stackmaps") { StackMapSection = Sec; break; @@ -653,7 +679,8 @@ void MachODumper::printStackMap() const { if (StackMapSection == object::SectionRef()) return; - StringRef StackMapContents = unwrapOrError(StackMapSection.getContents()); + StringRef StackMapContents = + unwrapOrError(Obj->getFileName(), StackMapSection.getContents()); ArrayRef<uint8_t> StackMapContentsArray = arrayRefFromStringRef(StackMapContents); diff --git a/tools/llvm-readobj/ObjDumper.cpp b/tools/llvm-readobj/ObjDumper.cpp index 0a9e22c8a71c..9e5ebd99ac37 100644 --- a/tools/llvm-readobj/ObjDumper.cpp +++ b/tools/llvm-readobj/ObjDumper.cpp @@ -23,6 +23,10 @@ namespace llvm { +static inline Error createError(const Twine &Msg) { + return createStringError(object::object_error::parse_failed, Msg); +} + ObjDumper::ObjDumper(ScopedPrinter &Writer) : W(Writer) {} ObjDumper::~ObjDumper() { @@ -49,8 +53,7 @@ getSectionRefsByNameOrIndex(const object::ObjectFile *Obj, SecIndex = Obj->isELF() ? 0 : 1; for (object::SectionRef SecRef : Obj->sections()) { - StringRef SecName; - error(SecRef.getName(SecName)); + StringRef SecName = unwrapOrError(Obj->getFileName(), SecRef.getName()); auto NameIt = SecNames.find(SecName); if (NameIt != SecNames.end()) NameIt->second = true; @@ -64,10 +67,15 @@ getSectionRefsByNameOrIndex(const object::ObjectFile *Obj, for (const std::pair<std::string, bool> &S : SecNames) if (!S.second) - reportWarning(formatv("could not find section '{0}'", S.first).str()); + reportWarning( + createError(formatv("could not find section '{0}'", S.first).str()), + Obj->getFileName()); + for (std::pair<unsigned, bool> S : SecIndices) if (!S.second) - reportWarning(formatv("could not find section {0}", S.first).str()); + reportWarning( + createError(formatv("could not find section {0}", S.first).str()), + Obj->getFileName()); return Ret; } @@ -77,14 +85,16 @@ void ObjDumper::printSectionsAsString(const object::ObjectFile *Obj, bool First = true; for (object::SectionRef Section : getSectionRefsByNameOrIndex(Obj, Sections)) { - StringRef SectionName; - error(Section.getName(SectionName)); + StringRef SectionName = + unwrapOrError(Obj->getFileName(), Section.getName()); + if (!First) W.startLine() << '\n'; First = false; W.startLine() << "String dump of section '" << SectionName << "':\n"; - StringRef SectionContent = unwrapOrError(Section.getContents()); + StringRef SectionContent = + unwrapOrError(Obj->getFileName(), Section.getContents()); const uint8_t *SecContent = SectionContent.bytes_begin(); const uint8_t *CurrentWord = SecContent; @@ -110,14 +120,16 @@ void ObjDumper::printSectionsAsHex(const object::ObjectFile *Obj, bool First = true; for (object::SectionRef Section : getSectionRefsByNameOrIndex(Obj, Sections)) { - StringRef SectionName; - error(Section.getName(SectionName)); + StringRef SectionName = + unwrapOrError(Obj->getFileName(), Section.getName()); + if (!First) W.startLine() << '\n'; First = false; W.startLine() << "Hex dump of section '" << SectionName << "':\n"; - StringRef SectionContent = unwrapOrError(Section.getContents()); + StringRef SectionContent = + unwrapOrError(Obj->getFileName(), Section.getContents()); const uint8_t *SecContent = SectionContent.bytes_begin(); const uint8_t *SecEnd = SecContent + SectionContent.size(); diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index aaabfa2ca2e8..2ba441342499 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -68,15 +68,8 @@ public: virtual void printAddrsig() {} virtual void printNotes() {} virtual void printELFLinkerOptions() {} - - // Only implemented for ARM ELF at this time. - virtual void printAttributes() { } - - // Only implemented for MIPS ELF at this time. - virtual void printMipsPLTGOT() { } - virtual void printMipsABIFlags() { } - virtual void printMipsReginfo() { } - virtual void printMipsOptions() { } + virtual void printStackSizes() {} + virtual void printArchSpecificInfo() { } // Only implemented for PE/COFF. virtual void printCOFFImports() { } diff --git a/tools/llvm-readobj/WasmDumper.cpp b/tools/llvm-readobj/WasmDumper.cpp index 041a9a15bdb6..dfab9f40d71b 100644 --- a/tools/llvm-readobj/WasmDumper.cpp +++ b/tools/llvm-readobj/WasmDumper.cpp @@ -51,6 +51,7 @@ static const EnumEntry<unsigned> WasmSymbolFlags[] = { ENUM_ENTRY(UNDEFINED), ENUM_ENTRY(EXPORTED), ENUM_ENTRY(EXPLICIT_NAME), + ENUM_ENTRY(NO_STRIP), #undef ENUM_ENTRY }; @@ -90,7 +91,7 @@ void WasmDumper::printRelocation(const SectionRef &Section, StringRef SymName; symbol_iterator SI = Reloc.getSymbol(); if (SI != Obj->symbol_end()) - SymName = error(SI->getName()); + SymName = unwrapOrError(Obj->getFileName(), SI->getName()); bool HasAddend = false; switch (RelocType) { @@ -133,8 +134,8 @@ void WasmDumper::printRelocations() { int SectionNumber = 0; for (const SectionRef &Section : Obj->sections()) { bool PrintedGroup = false; - StringRef Name; - error(Section.getName(Name)); + StringRef Name = unwrapOrError(Obj->getFileName(), Section.getName()); + ++SectionNumber; for (const RelocationRef &Reloc : Section.relocations()) { diff --git a/tools/llvm-readobj/Win64EHDumper.cpp b/tools/llvm-readobj/Win64EHDumper.cpp index e64b8f157180..fa268ce9d434 100644 --- a/tools/llvm-readobj/Win64EHDumper.cpp +++ b/tools/llvm-readobj/Win64EHDumper.cpp @@ -289,7 +289,9 @@ void Dumper::printRuntimeFunction(const Context &Ctx, resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset); ArrayRef<uint8_t> Contents; - error(Ctx.COFF.getSectionContents(XData, Contents)); + if (Error E = Ctx.COFF.getSectionContents(XData, Contents)) + reportError(std::move(E), Ctx.COFF.getFileName()); + if (Contents.empty()) return; @@ -304,14 +306,19 @@ void Dumper::printRuntimeFunction(const Context &Ctx, void Dumper::printData(const Context &Ctx) { for (const auto &Section : Ctx.COFF.sections()) { StringRef Name; - Section.getName(Name); + if (Expected<StringRef> NameOrErr = Section.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); if (Name != ".pdata" && !Name.startswith(".pdata$")) continue; const coff_section *PData = Ctx.COFF.getCOFFSection(Section); ArrayRef<uint8_t> Contents; - error(Ctx.COFF.getSectionContents(PData, Contents)); + + if (Error E = Ctx.COFF.getSectionContents(PData, Contents)) + reportError(std::move(E), Ctx.COFF.getFileName()); if (Contents.empty()) continue; diff --git a/tools/llvm-readobj/WindowsResourceDumper.cpp b/tools/llvm-readobj/WindowsResourceDumper.cpp index 13989f696d9d..a2fb6aac3f93 100644 --- a/tools/llvm-readobj/WindowsResourceDumper.cpp +++ b/tools/llvm-readobj/WindowsResourceDumper.cpp @@ -56,8 +56,12 @@ void Dumper::printEntry(const ResourceEntryRef &Ref) { if (Ref.checkTypeString()) { auto NarrowStr = stripUTF16(Ref.getTypeString()); SW.printString("Resource type (string)", NarrowStr); - } else - SW.printNumber("Resource type (int)", Ref.getTypeID()); + } else { + SmallString<20> IDStr; + raw_svector_ostream OS(IDStr); + printResourceTypeName(Ref.getTypeID(), OS); + SW.printString("Resource type (int)", IDStr); + } if (Ref.checkNameString()) { auto NarrowStr = stripUTF16(Ref.getNameString()); diff --git a/tools/llvm-readobj/XCOFFDumper.cpp b/tools/llvm-readobj/XCOFFDumper.cpp index 6f260f91537f..fe95b6d1b494 100644 --- a/tools/llvm-readobj/XCOFFDumper.cpp +++ b/tools/llvm-readobj/XCOFFDumper.cpp @@ -22,6 +22,12 @@ using namespace object; namespace { class XCOFFDumper : public ObjDumper { + enum { + SymbolTypeMask = 0x07, + SymbolAlignmentMask = 0xF8, + SymbolAlignmentBitOffset = 3 + }; + public: XCOFFDumper(const XCOFFObjectFile &Obj, ScopedPrinter &Writer) : ObjDumper(Writer), Obj(Obj) {} @@ -37,11 +43,21 @@ public: private: template <typename T> void printSectionHeaders(ArrayRef<T> Sections); - - const XCOFFObjectFile &Obj; + template <typename T> void printGenericSectionHeader(T &Sec) const; + template <typename T> void printOverflowSectionHeader(T &Sec) const; + void printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr); + void printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr); + void printSectAuxEntForStat(const XCOFFSectAuxEntForStat *AuxEntPtr); + void printSymbol(const SymbolRef &); // Least significant 3 bits are reserved. static constexpr unsigned SectionFlagsReservedMask = 0x7; + + // The low order 16 bits of section flags denotes the section type. + static constexpr unsigned SectionFlagsTypeMask = 0xffffu; + + void printRelocations(ArrayRef<XCOFFSectionHeader32> Sections); + const XCOFFObjectFile &Obj; }; } // anonymous namespace @@ -100,11 +116,315 @@ void XCOFFDumper::printSectionHeaders() { } void XCOFFDumper::printRelocations() { - llvm_unreachable("Unimplemented functionality for XCOFFDumper"); + if (Obj.is64Bit()) + llvm_unreachable("64-bit relocation output not implemented!"); + else + printRelocations(Obj.sections32()); +} + +static const EnumEntry<XCOFF::RelocationType> RelocationTypeNameclass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(R_POS), ECase(R_RL), ECase(R_RLA), ECase(R_NEG), + ECase(R_REL), ECase(R_TOC), ECase(R_TRL), ECase(R_TRLA), + ECase(R_GL), ECase(R_TCL), ECase(R_REF), ECase(R_BA), + ECase(R_BR), ECase(R_RBA), ECase(R_RBR), ECase(R_TLS), + ECase(R_TLS_IE), ECase(R_TLS_LD), ECase(R_TLS_LE), ECase(R_TLSM), + ECase(R_TLSML), ECase(R_TOCU), ECase(R_TOCL) +#undef ECase +}; + +void XCOFFDumper::printRelocations(ArrayRef<XCOFFSectionHeader32> Sections) { + if (!opts::ExpandRelocs) + report_fatal_error("Unexpanded relocation output not implemented."); + + ListScope LS(W, "Relocations"); + uint16_t Index = 0; + for (const auto &Sec : Sections) { + ++Index; + // Only the .text, .data, .tdata, and STYP_DWARF sections have relocation. + if (Sec.Flags != XCOFF::STYP_TEXT && Sec.Flags != XCOFF::STYP_DATA && + Sec.Flags != XCOFF::STYP_TDATA && Sec.Flags != XCOFF::STYP_DWARF) + continue; + auto Relocations = unwrapOrError(Obj.getFileName(), Obj.relocations(Sec)); + if (Relocations.empty()) + continue; + + W.startLine() << "Section (index: " << Index << ") " << Sec.getName() + << " {\n"; + for (auto Reloc : Relocations) { + StringRef SymbolName = unwrapOrError( + Obj.getFileName(), Obj.getSymbolNameByIndex(Reloc.SymbolIndex)); + + DictScope RelocScope(W, "Relocation"); + W.printHex("Virtual Address", Reloc.VirtualAddress); + W.printNumber("Symbol", SymbolName, Reloc.SymbolIndex); + W.printString("IsSigned", Reloc.isRelocationSigned() ? "Yes" : "No"); + W.printNumber("FixupBitValue", Reloc.isFixupIndicated() ? 1 : 0); + W.printNumber("Length", Reloc.getRelocatedLength()); + W.printEnum("Type", (uint8_t)Reloc.Type, + makeArrayRef(RelocationTypeNameclass)); + } + W.unindent(); + W.startLine() << "}\n"; + } +} + +static const EnumEntry<XCOFF::CFileStringType> FileStringType[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(XFT_FN), ECase(XFT_CT), ECase(XFT_CV), ECase(XFT_CD) +#undef ECase +}; + +void XCOFFDumper::printFileAuxEnt(const XCOFFFileAuxEnt *AuxEntPtr) { + if (Obj.is64Bit()) + report_fatal_error( + "Printing for File Auxiliary Entry in 64-bit is unimplemented."); + StringRef FileName = + unwrapOrError(Obj.getFileName(), Obj.getCFileName(AuxEntPtr)); + DictScope SymDs(W, "File Auxiliary Entry"); + W.printNumber("Index", + Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr))); + W.printString("Name", FileName); + W.printEnum("Type", static_cast<uint8_t>(AuxEntPtr->Type), + makeArrayRef(FileStringType)); +} + +static const EnumEntry<XCOFF::StorageMappingClass> CsectStorageMappingClass[] = + { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(XMC_PR), ECase(XMC_RO), ECase(XMC_DB), + ECase(XMC_GL), ECase(XMC_XO), ECase(XMC_SV), + ECase(XMC_SV64), ECase(XMC_SV3264), ECase(XMC_TI), + ECase(XMC_TB), ECase(XMC_RW), ECase(XMC_TC0), + ECase(XMC_TC), ECase(XMC_TD), ECase(XMC_DS), + ECase(XMC_UA), ECase(XMC_BS), ECase(XMC_UC), + ECase(XMC_TL), ECase(XMC_TE) +#undef ECase +}; + +static const EnumEntry<XCOFF::SymbolType> CsectSymbolTypeClass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(XTY_ER), ECase(XTY_SD), ECase(XTY_LD), ECase(XTY_CM) +#undef ECase +}; + +void XCOFFDumper::printCsectAuxEnt32(const XCOFFCsectAuxEnt32 *AuxEntPtr) { + assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file."); + + DictScope SymDs(W, "CSECT Auxiliary Entry"); + W.printNumber("Index", + Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr))); + if ((AuxEntPtr->SymbolAlignmentAndType & SymbolTypeMask) == XCOFF::XTY_LD) + 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, + makeArrayRef(CsectSymbolTypeClass)); + W.printEnum("StorageMappingClass", + static_cast<uint8_t>(AuxEntPtr->StorageMappingClass), + makeArrayRef(CsectStorageMappingClass)); + W.printHex("StabInfoIndex", AuxEntPtr->StabInfoIndex); + W.printHex("StabSectNum", AuxEntPtr->StabSectNum); +} + +void XCOFFDumper::printSectAuxEntForStat( + const XCOFFSectAuxEntForStat *AuxEntPtr) { + assert(!Obj.is64Bit() && "32-bit interface called on 64-bit object file."); + + DictScope SymDs(W, "Sect Auxiliary Entry For Stat"); + W.printNumber("Index", + Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(AuxEntPtr))); + W.printNumber("SectionLength", AuxEntPtr->SectionLength); + + // Unlike the corresponding fields in the section header, NumberOfRelocEnt + // and NumberOfLineNum do not handle values greater than 65535. + W.printNumber("NumberOfRelocEnt", AuxEntPtr->NumberOfRelocEnt); + W.printNumber("NumberOfLineNum", AuxEntPtr->NumberOfLineNum); +} + +static const EnumEntry<XCOFF::StorageClass> SymStorageClass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(C_NULL), ECase(C_AUTO), ECase(C_EXT), ECase(C_STAT), + ECase(C_REG), ECase(C_EXTDEF), ECase(C_LABEL), ECase(C_ULABEL), + ECase(C_MOS), ECase(C_ARG), ECase(C_STRTAG), ECase(C_MOU), + ECase(C_UNTAG), ECase(C_TPDEF), ECase(C_USTATIC), ECase(C_ENTAG), + ECase(C_MOE), ECase(C_REGPARM), ECase(C_FIELD), ECase(C_BLOCK), + ECase(C_FCN), ECase(C_EOS), ECase(C_FILE), ECase(C_LINE), + ECase(C_ALIAS), ECase(C_HIDDEN), ECase(C_HIDEXT), ECase(C_BINCL), + ECase(C_EINCL), ECase(C_INFO), ECase(C_WEAKEXT), ECase(C_DWARF), + ECase(C_GSYM), ECase(C_LSYM), ECase(C_PSYM), ECase(C_RSYM), + ECase(C_RPSYM), ECase(C_STSYM), ECase(C_TCSYM), ECase(C_BCOMM), + ECase(C_ECOML), ECase(C_ECOMM), ECase(C_DECL), ECase(C_ENTRY), + ECase(C_FUN), ECase(C_BSTAT), ECase(C_ESTAT), ECase(C_GTLS), + ECase(C_STTLS), ECase(C_EFCN) +#undef ECase +}; + +static StringRef GetSymbolValueName(XCOFF::StorageClass SC) { + switch (SC) { + case XCOFF::C_EXT: + case XCOFF::C_WEAKEXT: + case XCOFF::C_HIDEXT: + case XCOFF::C_STAT: + return "Value (RelocatableAddress)"; + case XCOFF::C_FILE: + return "Value (SymbolTableIndex)"; + case XCOFF::C_FCN: + case XCOFF::C_BLOCK: + case XCOFF::C_FUN: + case XCOFF::C_STSYM: + case XCOFF::C_BINCL: + case XCOFF::C_EINCL: + case XCOFF::C_INFO: + case XCOFF::C_BSTAT: + case XCOFF::C_LSYM: + case XCOFF::C_PSYM: + case XCOFF::C_RPSYM: + case XCOFF::C_RSYM: + case XCOFF::C_ECOML: + case XCOFF::C_DWARF: + assert(false && "This StorageClass for the symbol is not yet implemented."); + return ""; + default: + return "Value"; + } +} + +static const EnumEntry<XCOFF::CFileLangId> CFileLangIdClass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(TB_C), ECase(TB_CPLUSPLUS) +#undef ECase +}; + +static const EnumEntry<XCOFF::CFileCpuId> CFileCpuIdClass[] = { +#define ECase(X) \ + { #X, XCOFF::X } + ECase(TCPU_PPC64), ECase(TCPU_COM), ECase(TCPU_970) +#undef ECase +}; + +void XCOFFDumper::printSymbol(const SymbolRef &S) { + if (Obj.is64Bit()) + report_fatal_error("64-bit support is unimplemented."); + + DataRefImpl SymbolDRI = S.getRawDataRefImpl(); + const XCOFFSymbolEntry *SymbolEntPtr = Obj.toSymbolEntry(SymbolDRI); + + XCOFFSymbolRef XCOFFSymRef(SymbolDRI, &Obj); + uint8_t NumberOfAuxEntries = XCOFFSymRef.getNumberOfAuxEntries(); + + DictScope SymDs(W, "Symbol"); + + StringRef SymbolName = + unwrapOrError(Obj.getFileName(), Obj.getSymbolName(SymbolDRI)); + + W.printNumber("Index", + Obj.getSymbolIndex(reinterpret_cast<uintptr_t>(SymbolEntPtr))); + W.printString("Name", SymbolName); + W.printHex(GetSymbolValueName(SymbolEntPtr->StorageClass), + SymbolEntPtr->Value); + + StringRef SectionName = + unwrapOrError(Obj.getFileName(), Obj.getSymbolSectionName(SymbolEntPtr)); + + W.printString("Section", SectionName); + if (XCOFFSymRef.getStorageClass() == XCOFF::C_FILE) { + W.printEnum("Source Language ID", + SymbolEntPtr->CFileLanguageIdAndTypeId.LanguageId, + makeArrayRef(CFileLangIdClass)); + W.printEnum("CPU Version ID", + SymbolEntPtr->CFileLanguageIdAndTypeId.CpuTypeId, + makeArrayRef(CFileCpuIdClass)); + } else + W.printHex("Type", SymbolEntPtr->SymbolType); + + W.printEnum("StorageClass", static_cast<uint8_t>(SymbolEntPtr->StorageClass), + makeArrayRef(SymStorageClass)); + W.printNumber("NumberOfAuxEntries", SymbolEntPtr->NumberOfAuxEntries); + + if (NumberOfAuxEntries == 0) + return; + + switch (XCOFFSymRef.getStorageClass()) { + case XCOFF::C_FILE: + // If the symbol is C_FILE and has auxiliary entries... + for (int i = 1; i <= NumberOfAuxEntries; i++) { + const XCOFFFileAuxEnt *FileAuxEntPtr = + reinterpret_cast<const XCOFFFileAuxEnt *>(SymbolEntPtr + i); +#ifndef NDEBUG + Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(FileAuxEntPtr)); +#endif + printFileAuxEnt(FileAuxEntPtr); + } + break; + case XCOFF::C_EXT: + case XCOFF::C_WEAKEXT: + case XCOFF::C_HIDEXT: + // If the symbol is for a function, and it has more than 1 auxiliary entry, + // then one of them must be function auxiliary entry which we do not + // support yet. + if (XCOFFSymRef.isFunction() && NumberOfAuxEntries >= 2) + report_fatal_error("Function auxiliary entry printing is unimplemented."); + + // If there is more than 1 auxiliary entry, instead of printing out + // error information, print out the raw Auxiliary entry from 1st till + // the last - 1. The last one must be a CSECT Auxiliary Entry. + for (int i = 1; i < NumberOfAuxEntries; i++) { + W.startLine() << "!Unexpected raw auxiliary entry data:\n"; + W.startLine() << format_bytes( + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i), + XCOFF::SymbolTableEntrySize)); + } + + // The symbol's last auxiliary entry is a CSECT Auxiliary Entry. + printCsectAuxEnt32(XCOFFSymRef.getXCOFFCsectAuxEnt32()); + break; + case XCOFF::C_STAT: + if (NumberOfAuxEntries > 1) + report_fatal_error( + "C_STAT symbol should not have more than 1 auxiliary entry."); + + const XCOFFSectAuxEntForStat *StatAuxEntPtr; + StatAuxEntPtr = + reinterpret_cast<const XCOFFSectAuxEntForStat *>(SymbolEntPtr + 1); +#ifndef NDEBUG + Obj.checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(StatAuxEntPtr)); +#endif + printSectAuxEntForStat(StatAuxEntPtr); + break; + case XCOFF::C_DWARF: + case XCOFF::C_BLOCK: + case XCOFF::C_FCN: + report_fatal_error("Symbol table entry printing for this storage class " + "type is unimplemented."); + break; + default: + for (int i = 1; i <= NumberOfAuxEntries; i++) { + W.startLine() << "!Unexpected raw auxiliary entry data:\n"; + W.startLine() << format_bytes( + ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(SymbolEntPtr + i), + XCOFF::SymbolTableEntrySize)); + } + break; + } } void XCOFFDumper::printSymbols() { - llvm_unreachable("Unimplemented functionality for XCOFFDumper"); + ListScope Group(W, "Symbols"); + for (const SymbolRef &S : Obj.symbols()) + printSymbol(S); } void XCOFFDumper::printDynamicSymbols() { @@ -135,6 +455,39 @@ static const EnumEntry<XCOFF::SectionTypeFlags> SectionTypeFlagsNames[] = { }; template <typename T> +void XCOFFDumper::printOverflowSectionHeader(T &Sec) const { + if (Obj.is64Bit()) { + reportWarning(make_error<StringError>("An 64-bit XCOFF object file may not " + "contain an overflow section header.", + object_error::parse_failed), + Obj.getFileName()); + } + + W.printString("Name", Sec.getName()); + W.printNumber("NumberOfRelocations", Sec.PhysicalAddress); + W.printNumber("NumberOfLineNumbers", Sec.VirtualAddress); + W.printHex("Size", Sec.SectionSize); + W.printHex("RawDataOffset", Sec.FileOffsetToRawData); + W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo); + W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo); + W.printNumber("IndexOfSectionOverflowed", Sec.NumberOfRelocations); + W.printNumber("IndexOfSectionOverflowed", Sec.NumberOfLineNumbers); +} + +template <typename T> +void XCOFFDumper::printGenericSectionHeader(T &Sec) const { + W.printString("Name", Sec.getName()); + W.printHex("PhysicalAddress", Sec.PhysicalAddress); + W.printHex("VirtualAddress", Sec.VirtualAddress); + W.printHex("Size", Sec.SectionSize); + W.printHex("RawDataOffset", Sec.FileOffsetToRawData); + W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo); + W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo); + W.printNumber("NumberOfRelocations", Sec.NumberOfRelocations); + W.printNumber("NumberOfLineNumbers", Sec.NumberOfLineNumbers); +} + +template <typename T> void XCOFFDumper::printSectionHeaders(ArrayRef<T> Sections) { ListScope Group(W, "Sections"); @@ -143,27 +496,28 @@ void XCOFFDumper::printSectionHeaders(ArrayRef<T> Sections) { DictScope SecDS(W, "Section"); W.printNumber("Index", Index++); - W.printString("Name", Sec.getName()); - - W.printHex("PhysicalAddress", Sec.PhysicalAddress); - W.printHex("VirtualAddress", Sec.VirtualAddress); - W.printHex("Size", Sec.SectionSize); - W.printHex("RawDataOffset", Sec.FileOffsetToRawData); - W.printHex("RelocationPointer", Sec.FileOffsetToRelocationInfo); - W.printHex("LineNumberPointer", Sec.FileOffsetToLineNumberInfo); - - // TODO Need to add overflow handling when NumberOfX == _OVERFLOW_MARKER - // in 32-bit object files. - W.printNumber("NumberOfRelocations", Sec.NumberOfRelocations); - W.printNumber("NumberOfLineNumbers", Sec.NumberOfLineNumbers); - - // The most significant 16-bits represent the DWARF section subtype. For - // now we just dump the section type flags. - uint16_t Flags = Sec.Flags & 0xffffu; - if (Flags & SectionFlagsReservedMask) - W.printHex("Flags", "Reserved", Flags); + + uint16_t SectionType = Sec.Flags & SectionFlagsTypeMask; + switch (SectionType) { + case XCOFF::STYP_OVRFLO: + printOverflowSectionHeader(Sec); + break; + case XCOFF::STYP_LOADER: + case XCOFF::STYP_EXCEPT: + case XCOFF::STYP_TYPCHK: + // TODO The interpretation of loader, exception and type check section + // headers are different from that of generic section headers. We will + // implement them later. We interpret them as generic section headers for + // now. + default: + printGenericSectionHeader(Sec); + break; + } + // For now we just dump the section type portion of the flags. + if (SectionType & SectionFlagsReservedMask) + W.printHex("Flags", "Reserved", SectionType); else - W.printEnum("Type", Flags, makeArrayRef(SectionTypeFlagsNames)); + W.printEnum("Type", SectionType, makeArrayRef(SectionTypeFlagsNames)); } if (opts::SectionRelocations) diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index 1bd5bb74bf29..4db13897879d 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -231,26 +231,11 @@ namespace opts { "codeview-subsection-bytes", cl::desc("Dump raw contents of codeview debug sections and records")); - // --arm-attributes - cl::opt<bool> ARMAttributes("arm-attributes", - cl::desc("Display the ARM attributes section")); - - // --mips-plt-got - cl::opt<bool> - MipsPLTGOT("mips-plt-got", - cl::desc("Display the MIPS GOT and PLT GOT sections")); - - // --mips-abi-flags - cl::opt<bool> MipsABIFlags("mips-abi-flags", - cl::desc("Display the MIPS.abiflags section")); - - // --mips-reginfo - cl::opt<bool> MipsReginfo("mips-reginfo", - cl::desc("Display the MIPS .reginfo section")); - - // --mips-options - cl::opt<bool> MipsOptions("mips-options", - cl::desc("Display the MIPS .MIPS.options section")); + // --arch-specific + cl::opt<bool> ArchSpecificInfo("arch-specific", + cl::desc("Displays architecture-specific information, if there is any.")); + cl::alias ArchSpecifcInfoShort("A", cl::desc("Alias for --arch-specific"), + cl::aliasopt(ArchSpecificInfo), cl::NotHidden); // --coff-imports cl::opt<bool> @@ -324,6 +309,11 @@ namespace opts { PrintStackMap("stackmap", cl::desc("Display contents of stackmap section")); + // --stack-sizes + cl::opt<bool> + PrintStackSizes("stack-sizes", + cl::desc("Display contents of all stack sizes sections")); + // --version-info, -V cl::opt<bool> VersionInfo("version-info", @@ -368,63 +358,45 @@ namespace opts { HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); } // namespace opts +static StringRef ToolName; + namespace llvm { -LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) { +LLVM_ATTRIBUTE_NORETURN static void error(Twine Msg) { + // Flush the standard output to print the error at a + // proper place. fouts().flush(); errs() << "\n"; - WithColor::error(errs()) << Msg << "\n"; + WithColor::error(errs(), ToolName) << Msg << "\n"; exit(1); } -void reportError(StringRef Input, Error Err) { +LLVM_ATTRIBUTE_NORETURN void reportError(Error Err, StringRef Input) { + assert(Err); if (Input == "-") Input = "<stdin>"; - error(createFileError(Input, std::move(Err))); + handleAllErrors(createFileError(Input, std::move(Err)), + [&](const ErrorInfoBase &EI) { error(EI.message()); }); + llvm_unreachable("error() call should never return"); } -void reportWarning(Twine Msg) { - fouts().flush(); - errs() << "\n"; - WithColor::warning(errs()) << Msg << "\n"; -} - -void warn(Error Err) { - handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { - reportWarning(EI.message()); - }); -} - -void error(Error EC) { - if (!EC) - return; - handleAllErrors(std::move(EC), - [&](const ErrorInfoBase &EI) { reportError(EI.message()); }); -} +void reportWarning(Error Err, StringRef Input) { + assert(Err); + if (Input == "-") + Input = "<stdin>"; -void error(std::error_code EC) { - if (!EC) - return; - reportError(EC.message()); + // Flush the standard output to print the warning at a + // proper place. + fouts().flush(); + handleAllErrors( + createFileError(Input, std::move(Err)), [&](const ErrorInfoBase &EI) { + errs() << "\n"; + WithColor::warning(errs(), ToolName) << EI.message() << "\n"; + }); } } // namespace llvm -static void reportError(StringRef Input, std::error_code EC) { - reportError(Input, errorCodeToError(EC)); -} - -static bool isMipsArch(unsigned Arch) { - switch (Arch) { - case llvm::Triple::mips: - case llvm::Triple::mipsel: - case llvm::Triple::mips64: - case llvm::Triple::mips64el: - return true; - default: - return false; - } -} namespace { struct ReadObjTypeTableBuilder { ReadObjTypeTableBuilder() @@ -471,19 +443,19 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, std::unique_ptr<ObjDumper> Dumper; if (std::error_code EC = createDumper(Obj, Writer, Dumper)) - reportError(FileStr, EC); + reportError(errorCodeToError(EC), FileStr); - Writer.startLine() << "\n"; - if (opts::Output == opts::LLVM) { + if (opts::Output == opts::LLVM || opts::InputFilenames.size() > 1 || A) { + Writer.startLine() << "\n"; Writer.printString("File", FileStr); + } + if (opts::Output == opts::LLVM) { Writer.printString("Format", Obj->getFileFormatName()); Writer.printString("Arch", Triple::getArchTypeName( (llvm::Triple::ArchType)Obj->getArch())); Writer.printString("AddressSize", formatv("{0}bit", 8 * Obj->getBytesInAddress())); Dumper->printLoadName(); - } else if (opts::Output == opts::GNU && A) { - Writer.printString("File", FileStr); } if (opts::FileHeaders) @@ -519,19 +491,8 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, if (Obj->isELF()) { if (opts::ELFLinkerOptions) Dumper->printELFLinkerOptions(); - if (Obj->getArch() == llvm::Triple::arm) - if (opts::ARMAttributes) - Dumper->printAttributes(); - if (isMipsArch(Obj->getArch())) { - if (opts::MipsPLTGOT) - Dumper->printMipsPLTGOT(); - if (opts::MipsABIFlags) - Dumper->printMipsABIFlags(); - if (opts::MipsReginfo) - Dumper->printMipsReginfo(); - if (opts::MipsOptions) - Dumper->printMipsOptions(); - } + if (opts::ArchSpecificInfo) + Dumper->printArchSpecificInfo(); if (opts::SectionGroups) Dumper->printGroupSections(); if (opts::HashHistogram) @@ -583,6 +544,8 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer, } if (opts::PrintStackMap) Dumper->printStackMap(); + if (opts::PrintStackSizes) + Dumper->printStackSizes(); } /// Dumps each object file in \a Arc; @@ -591,9 +554,8 @@ static void dumpArchive(const Archive *Arc, ScopedPrinter &Writer) { for (auto &Child : Arc->children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); if (!ChildOrErr) { - if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) { - reportError(Arc->getFileName(), std::move(E)); - } + if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + reportError(std::move(E), Arc->getFileName()); continue; } if (ObjectFile *Obj = dyn_cast<ObjectFile>(&*ChildOrErr.get())) @@ -601,10 +563,11 @@ static void dumpArchive(const Archive *Arc, ScopedPrinter &Writer) { else if (COFFImportFile *Imp = dyn_cast<COFFImportFile>(&*ChildOrErr.get())) dumpCOFFImportFile(Imp, Writer); else - reportError(Arc->getFileName(), readobj_error::unrecognized_file_format); + reportError(errorCodeToError(readobj_error::unrecognized_file_format), + Arc->getFileName()); } if (Err) - reportError(Arc->getFileName(), std::move(Err)); + reportError(std::move(Err), Arc->getFileName()); } /// Dumps each object file in \a MachO Universal Binary; @@ -614,9 +577,8 @@ static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary, Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile(); if (ObjOrErr) dumpObject(&*ObjOrErr.get(), Writer); - else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { - reportError(UBinary->getFileName(), ObjOrErr.takeError()); - } + else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) + reportError(ObjOrErr.takeError(), UBinary->getFileName()); else if (Expected<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive()) dumpArchive(&*AOrErr.get(), Writer); } @@ -627,7 +589,7 @@ static void dumpWindowsResourceFile(WindowsResource *WinRes, ScopedPrinter &Printer) { WindowsRes::Dumper Dumper(WinRes, Printer); if (auto Err = Dumper.printData()) - reportError(WinRes->getFileName(), std::move(Err)); + reportError(std::move(Err), WinRes->getFileName()); } @@ -636,7 +598,7 @@ static void dumpInput(StringRef File, ScopedPrinter &Writer) { // Attempt to open the binary. Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File); if (!BinaryOrErr) - reportError(File, BinaryOrErr.takeError()); + reportError(BinaryOrErr.takeError(), File); Binary &Binary = *BinaryOrErr.get().getBinary(); if (Archive *Arc = dyn_cast<Archive>(&Binary)) @@ -651,7 +613,8 @@ static void dumpInput(StringRef File, ScopedPrinter &Writer) { else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(&Binary)) dumpWindowsResourceFile(WinRes, Writer); else - reportError(File, readobj_error::unrecognized_file_format); + reportError(errorCodeToError(readobj_error::unrecognized_file_format), + File); CVTypes.Binaries.push_back(std::move(*BinaryOrErr)); } @@ -702,6 +665,7 @@ static void registerReadelfAliases() { int main(int argc, const char *argv[]) { InitLLVM X(argc, argv); + ToolName = argv[0]; // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); @@ -727,6 +691,10 @@ int main(int argc, const char *argv[]) { opts::UnwindInfo = true; opts::SectionGroups = true; opts::HashHistogram = true; + if (opts::Output == opts::LLVM) { + opts::Addrsig = true; + opts::PrintStackSizes = true; + } } if (opts::Headers) { diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 0e02da4cb847..d9813f5dea62 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/tools/llvm-readobj/llvm-readobj.h @@ -21,30 +21,13 @@ namespace llvm { } // Various helper functions. - LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg); - void reportError(StringRef Input, Error Err); - void reportWarning(Twine Msg); - void warn(llvm::Error Err); - void error(std::error_code EC); - void error(llvm::Error EC); - template <typename T> T error(llvm::Expected<T> &&E) { - error(E.takeError()); - return std::move(*E); - } + LLVM_ATTRIBUTE_NORETURN void reportError(Error Err, StringRef Input); + void reportWarning(Error Err, StringRef Input); - template <class T> T unwrapOrError(ErrorOr<T> EO) { - if (EO) - return *EO; - reportError(EO.getError().message()); - } - template <class T> T unwrapOrError(Expected<T> EO) { + template <class T> T unwrapOrError(StringRef Input, Expected<T> EO) { if (EO) return *EO; - std::string Buf; - raw_string_ostream OS(Buf); - logAllUnhandledErrors(EO.takeError(), OS); - OS.flush(); - reportError(Buf); + reportError(EO.takeError(), Input); } } // namespace llvm diff --git a/tools/llvm-reduce/CMakeLists.txt b/tools/llvm-reduce/CMakeLists.txt new file mode 100644 index 000000000000..48de0ffa78a1 --- /dev/null +++ b/tools/llvm-reduce/CMakeLists.txt @@ -0,0 +1,26 @@ +set(LLVM_LINK_COMPONENTS + AllTargetsAsmParsers + AllTargetsCodeGens + AllTargetsDescs + AllTargetsInfos + Core + IRReader + Support + Target + TransformUtils + ) + +add_llvm_tool(llvm-reduce + llvm-reduce.cpp + TestRunner.cpp + deltas/Delta.cpp + deltas/ReduceFunctions.cpp + deltas/ReduceGlobalVars.cpp + deltas/ReduceMetadata.cpp + deltas/ReduceArguments.cpp + deltas/ReduceBasicBlocks.cpp + deltas/ReduceInstructions.cpp + + DEPENDS + intrinsics_gen + ) diff --git a/tools/llvm-reduce/DeltaManager.h b/tools/llvm-reduce/DeltaManager.h new file mode 100644 index 000000000000..2309c3adf4e6 --- /dev/null +++ b/tools/llvm-reduce/DeltaManager.h @@ -0,0 +1,36 @@ +//===- DeltaManager.h - Runs Delta Passes to reduce Input -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file calls each specialized Delta pass in order to reduce the input IR +// file. +// +//===----------------------------------------------------------------------===// + +#include "TestRunner.h" +#include "deltas/Delta.h" +#include "deltas/ReduceArguments.h" +#include "deltas/ReduceBasicBlocks.h" +#include "deltas/ReduceFunctions.h" +#include "deltas/ReduceGlobalVars.h" +#include "deltas/ReduceMetadata.h" +#include "deltas/ReduceInstructions.h" + +namespace llvm { + +// TODO: Add CLI option to run only specified Passes (for unit tests) +inline void runDeltaPasses(TestRunner &Tester) { + reduceFunctionsDeltaPass(Tester); + reduceBasicBlocksDeltaPass(Tester); + reduceGlobalsDeltaPass(Tester); + reduceMetadataDeltaPass(Tester); + reduceArgumentsDeltaPass(Tester); + reduceInstructionsDeltaPass(Tester); + // TODO: Implement the remaining Delta Passes +} + +} // namespace llvm diff --git a/tools/llvm-reduce/LLVMBuild.txt b/tools/llvm-reduce/LLVMBuild.txt new file mode 100644 index 000000000000..7928f0503283 --- /dev/null +++ b/tools/llvm-reduce/LLVMBuild.txt @@ -0,0 +1,24 @@ +;===- ./tools/llvm-reduce/LLVMBuild.txt ------------------------*- Conf -*--===; +; +; Part of the LLVM Project, under the Apache 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 is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-reduce +parent = Tools +required_libraries = + BitReader + IRReader + all-targets diff --git a/tools/llvm-reduce/TestRunner.cpp b/tools/llvm-reduce/TestRunner.cpp new file mode 100644 index 000000000000..d0e195d5697c --- /dev/null +++ b/tools/llvm-reduce/TestRunner.cpp @@ -0,0 +1,42 @@ +//===-- TestRunner.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 "TestRunner.h" + +using namespace llvm; + +TestRunner::TestRunner(StringRef TestName, const std::vector<std::string> &TestArgs) + : TestName(TestName), TestArgs(TestArgs) { +} + +/// Runs the interestingness test, passes file to be tested as first argument +/// and other specified test arguments after that. +int TestRunner::run(StringRef Filename) { + std::vector<StringRef> ProgramArgs; + ProgramArgs.push_back(TestName); + + for (const auto &Arg : TestArgs) + ProgramArgs.push_back(Arg); + + ProgramArgs.push_back(Filename); + + std::string ErrMsg; + int Result = sys::ExecuteAndWait( + TestName, ProgramArgs, /*Env=*/None, /*Redirects=*/None, + /*SecondsToWait=*/0, /*MemoryLimit=*/0, &ErrMsg); + + if (Result < 0) { + Error E = make_error<StringError>("Error running interesting-ness test: " + + ErrMsg, + inconvertibleErrorCode()); + errs() << toString(std::move(E)); + exit(1); + } + + return !Result; +} diff --git a/tools/llvm-reduce/TestRunner.h b/tools/llvm-reduce/TestRunner.h new file mode 100644 index 000000000000..2270d6bd90b2 --- /dev/null +++ b/tools/llvm-reduce/TestRunner.h @@ -0,0 +1,46 @@ +//===-- tools/llvm-reduce/TestRunner.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_LLVMREDUCE_TESTRUNNER_H +#define LLVM_TOOLS_LLVMREDUCE_TESTRUNNER_H + +#include "llvm/ADT/SmallString.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include <vector> + +namespace llvm { + +// This class contains all the info necessary for running the provided +// interesting-ness test, as well as the most reduced module and its +// respective filename. +class TestRunner { +public: + TestRunner(StringRef TestName, const std::vector<std::string> &TestArgs); + + /// Runs the interesting-ness test for the specified file + /// @returns 0 if test was successful, 1 if otherwise + int run(StringRef Filename); + + /// Returns the most reduced version of the original testcase + Module *getProgram() const { return Program.get(); } + + void setProgram(std::unique_ptr<Module> P) { Program = std::move(P); } + +private: + StringRef TestName; + const std::vector<std::string> &TestArgs; + std::unique_ptr<Module> Program; +}; + +} // namespace llvm + +#endif diff --git a/tools/llvm-reduce/deltas/Delta.cpp b/tools/llvm-reduce/deltas/Delta.cpp new file mode 100644 index 000000000000..0642241ddebd --- /dev/null +++ b/tools/llvm-reduce/deltas/Delta.cpp @@ -0,0 +1,162 @@ +//===- Delta.cpp - Delta Debugging Algorithm Implementation ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the implementation for the Delta Debugging Algorithm: +// it splits a given set of Targets (i.e. Functions, Instructions, BBs, etc.) +// into chunks and tries to reduce the number chunks that are interesting. +// +//===----------------------------------------------------------------------===// + +#include "Delta.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include <fstream> +#include <set> + +using namespace llvm; + +bool IsReduced(Module &M, TestRunner &Test, SmallString<128> &CurrentFilepath) { + // Write Module to tmp file + int FD; + std::error_code EC = + sys::fs::createTemporaryFile("llvm-reduce", "ll", FD, CurrentFilepath); + if (EC) { + errs() << "Error making unique filename: " << EC.message() << "!\n"; + exit(1); + } + + ToolOutputFile Out(CurrentFilepath, FD); + M.print(Out.os(), /*AnnotationWriter=*/nullptr); + Out.os().close(); + if (Out.os().has_error()) { + errs() << "Error emitting bitcode to file '" << CurrentFilepath << "'!\n"; + exit(1); + } + + // Current Chunks aren't interesting + return Test.run(CurrentFilepath); +} + +/// Counts the amount of lines for a given file +static int getLines(StringRef Filepath) { + int Lines = 0; + std::string CurrLine; + std::ifstream FileStream(Filepath); + + while (std::getline(FileStream, CurrLine)) + ++Lines; + + return Lines; +} + +/// Splits Chunks in half and prints them. +/// If unable to split (when chunk size is 1) returns false. +static bool increaseGranularity(std::vector<Chunk> &Chunks) { + errs() << "Increasing granularity..."; + std::vector<Chunk> NewChunks; + bool SplitOne = false; + + for (auto &C : Chunks) { + if (C.end - C.begin == 0) + NewChunks.push_back(C); + else { + int Half = (C.begin + C.end) / 2; + NewChunks.push_back({C.begin, Half}); + NewChunks.push_back({Half + 1, C.end}); + SplitOne = true; + } + } + if (SplitOne) { + Chunks = NewChunks; + errs() << "Success! New Chunks:\n"; + for (auto C : Chunks) { + errs() << '\t'; + C.print(); + errs() << '\n'; + } + } + return SplitOne; +} + +/// Runs the Delta Debugging algorithm, splits the code into chunks and +/// reduces the amount of chunks that are considered interesting by the +/// given test. +void llvm::runDeltaPass( + TestRunner &Test, int Targets, + std::function<void(const std::vector<Chunk> &, Module *)> + ExtractChunksFromModule) { + assert(Targets >= 0); + if (!Targets) { + errs() << "\nNothing to reduce\n"; + return; + } + + if (Module *Program = Test.getProgram()) { + SmallString<128> CurrentFilepath; + if (!IsReduced(*Program, Test, CurrentFilepath)) { + errs() << "\nInput isn't interesting! Verify interesting-ness test\n"; + exit(1); + } + } + + std::vector<Chunk> Chunks = {{1, Targets}}; + std::set<Chunk> UninterestingChunks; + std::unique_ptr<Module> ReducedProgram; + + if (!increaseGranularity(Chunks)) { + errs() << "\nAlready at minimum size. Cannot reduce anymore.\n"; + return; + } + + do { + UninterestingChunks = {}; + for (int I = Chunks.size() - 1; I >= 0; --I) { + std::vector<Chunk> CurrentChunks; + + for (auto C : Chunks) + if (!UninterestingChunks.count(C) && C != Chunks[I]) + CurrentChunks.push_back(C); + + if (CurrentChunks.empty()) + continue; + + // Clone module before hacking it up.. + std::unique_ptr<Module> Clone = CloneModule(*Test.getProgram()); + // Generate Module with only Targets inside Current Chunks + ExtractChunksFromModule(CurrentChunks, Clone.get()); + + errs() << "Ignoring: "; + Chunks[I].print(); + for (auto C : UninterestingChunks) + C.print(); + + + + SmallString<128> CurrentFilepath; + if (!IsReduced(*Clone, Test, CurrentFilepath)) { + errs() << "\n"; + continue; + } + + UninterestingChunks.insert(Chunks[I]); + ReducedProgram = std::move(Clone); + errs() << " **** SUCCESS | lines: " << getLines(CurrentFilepath) << "\n"; + } + // Delete uninteresting chunks + erase_if(Chunks, [&UninterestingChunks](const Chunk &C) { + return UninterestingChunks.count(C); + }); + + } while (!UninterestingChunks.empty() || increaseGranularity(Chunks)); + + // If we reduced the testcase replace it + if (ReducedProgram) + Test.setProgram(std::move(ReducedProgram)); + errs() << "Couldn't increase anymore.\n"; +} diff --git a/tools/llvm-reduce/deltas/Delta.h b/tools/llvm-reduce/deltas/Delta.h new file mode 100644 index 000000000000..dbb18e4bd07f --- /dev/null +++ b/tools/llvm-reduce/deltas/Delta.h @@ -0,0 +1,76 @@ +//===- Delta.h - Delta Debugging Algorithm Implementation -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the implementation for the Delta Debugging Algorithm: +// it splits a given set of Targets (i.e. Functions, Instructions, BBs, etc.) +// into chunks and tries to reduce the number chunks that are interesting. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMREDUCE_LLVMREDUCE_DELTA_H +#define LLVM_TOOLS_LLVMREDUCE_LLVMREDUCE_DELTA_H + +#include "TestRunner.h" +#include <vector> +#include <utility> +#include <functional> + +namespace llvm { + +struct Chunk { + int begin; + int end; + + /// Helper function to verify if a given Target-index is inside the Chunk + bool contains(int Index) const { return Index >= begin && Index <= end; } + + void print() const { + errs() << "[" << begin; + if (end - begin != 0) + errs() << "," << end; + errs() << "]"; + } + + /// Operator when populating CurrentChunks in Generic Delta Pass + friend bool operator!=(const Chunk &C1, const Chunk &C2) { + return C1.begin != C2.begin || C1.end != C2.end; + } + + /// Operator used for sets + friend bool operator<(const Chunk &C1, const Chunk &C2) { + return std::tie(C1.begin, C1.end) < std::tie(C2.begin, C2.end); + } +}; + +/// This function implements the Delta Debugging algorithm, it receives a +/// number of Targets (e.g. Functions, Instructions, Basic Blocks, etc.) and +/// splits them in half; these chunks of targets are then tested while ignoring +/// one chunk, if a chunk is proven to be uninteresting (i.e. fails the test) +/// it is removed from consideration. The algorithm will attempt to split the +/// Chunks in half and start the process again until it can't split chunks +/// anymore. +/// +/// This function is intended to be called by each specialized delta pass (e.g. +/// RemoveFunctions) and receives three key parameters: +/// * Test: The main TestRunner instance which is used to run the provided +/// interesting-ness test, as well as to store and access the reduced Program. +/// * Targets: The amount of Targets that are going to be reduced by the +/// algorithm, for example, the RemoveGlobalVars pass would send the amount of +/// initialized GVs. +/// * ExtractChunksFromModule: A function used to tailor the main program so it +/// only contains Targets that are inside Chunks of the given iteration. +/// Note: This function is implemented by each specialized Delta pass +/// +/// Other implementations of the Delta Debugging algorithm can also be found in +/// the CReduce, Delta, and Lithium projects. +void runDeltaPass(TestRunner &Test, int Targets, + std::function<void(const std::vector<Chunk> &, Module *)> + ExtractChunksFromModule); +} // namespace llvm + +#endif diff --git a/tools/llvm-reduce/deltas/ReduceArguments.cpp b/tools/llvm-reduce/deltas/ReduceArguments.cpp new file mode 100644 index 000000000000..f5f14b83f42c --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceArguments.cpp @@ -0,0 +1,125 @@ +//===- ReduceArguments.cpp - Specialized Delta Pass -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass in order +// to reduce uninteresting Arguments from defined functions. +// +//===----------------------------------------------------------------------===// + +#include "ReduceArguments.h" +#include "Delta.h" +#include "llvm/ADT/SmallVector.h" +#include <set> +#include <vector> + +using namespace llvm; + +/// Goes over OldF calls and replaces them with a call to NewF +static void replaceFunctionCalls(Function &OldF, Function &NewF, + const std::set<int> &ArgIndexesToKeep) { + const auto &Users = OldF.users(); + for (auto I = Users.begin(), E = Users.end(); I != E; ) + if (auto *CI = dyn_cast<CallInst>(*I++)) { + SmallVector<Value *, 8> Args; + for (auto ArgI = CI->arg_begin(), E = CI->arg_end(); ArgI != E; ++ArgI) + if (ArgIndexesToKeep.count(ArgI - CI->arg_begin())) + Args.push_back(*ArgI); + + CallInst *NewCI = CallInst::Create(&NewF, Args); + NewCI->setCallingConv(NewF.getCallingConv()); + if (!CI->use_empty()) + CI->replaceAllUsesWith(NewCI); + ReplaceInstWithInst(CI, NewCI); + } +} + +/// Removes out-of-chunk arguments from functions, and modifies their calls +/// accordingly. It also removes allocations of out-of-chunk arguments. +static void extractArgumentsFromModule(std::vector<Chunk> ChunksToKeep, + Module *Program) { + int I = 0, ArgCount = 0; + std::set<Argument *> ArgsToKeep; + std::vector<Function *> Funcs; + // Get inside-chunk arguments, as well as their parent function + for (auto &F : *Program) + if (!F.isDeclaration()) { + Funcs.push_back(&F); + for (auto &A : F.args()) + if (I < (int)ChunksToKeep.size()) { + if (ChunksToKeep[I].contains(++ArgCount)) + ArgsToKeep.insert(&A); + if (ChunksToKeep[I].end == ArgCount) + ++I; + } + } + + for (auto *F : Funcs) { + ValueToValueMapTy VMap; + std::vector<Instruction *> InstToDelete; + for (auto &A : F->args()) + if (!ArgsToKeep.count(&A)) { + // By adding undesired arguments to the VMap, CloneFunction will remove + // them from the resulting Function + VMap[&A] = UndefValue::get(A.getType()); + for (auto *U : A.users()) + if (auto *I = dyn_cast<Instruction>(*&U)) + InstToDelete.push_back(I); + } + // Delete any instruction that uses the argument + for (auto *I : InstToDelete) { + I->replaceAllUsesWith(UndefValue::get(I->getType())); + I->eraseFromParent(); + } + + // No arguments to reduce + if (VMap.empty()) + continue; + + std::set<int> ArgIndexesToKeep; + int ArgI = 0; + for (auto &Arg : F->args()) + if (ArgsToKeep.count(&Arg)) + ArgIndexesToKeep.insert(++ArgI); + + auto *ClonedFunc = CloneFunction(F, VMap); + // In order to preserve function order, we move Clone after old Function + ClonedFunc->removeFromParent(); + Program->getFunctionList().insertAfter(F->getIterator(), ClonedFunc); + + replaceFunctionCalls(*F, *ClonedFunc, ArgIndexesToKeep); + // Rename Cloned Function to Old's name + std::string FName = F->getName(); + F->eraseFromParent(); + ClonedFunc->setName(FName); + } +} + +/// Counts the amount of arguments in non-declaration functions and prints their +/// respective name, index, and parent function name +static int countArguments(Module *Program) { + // TODO: Silence index with --quiet flag + outs() << "----------------------------\n"; + outs() << "Param Index Reference:\n"; + int ArgsCount = 0; + for (auto &F : *Program) + if (!F.isDeclaration() && F.arg_size()) { + outs() << " " << F.getName() << "\n"; + for (auto &A : F.args()) + outs() << "\t" << ++ArgsCount << ": " << A.getName() << "\n"; + + outs() << "----------------------------\n"; + } + + return ArgsCount; +} + +void llvm::reduceArgumentsDeltaPass(TestRunner &Test) { + outs() << "*** Reducing Arguments...\n"; + int ArgCount = countArguments(Test.getProgram()); + runDeltaPass(Test, ArgCount, extractArgumentsFromModule); +} diff --git a/tools/llvm-reduce/deltas/ReduceArguments.h b/tools/llvm-reduce/deltas/ReduceArguments.h new file mode 100644 index 000000000000..d9682b44f74d --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceArguments.h @@ -0,0 +1,21 @@ +//===- ReduceArguments.h - Specialized Delta Pass -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass in order +// to reduce uninteresting Arguments from defined functions. +// +//===----------------------------------------------------------------------===// + +#include "Delta.h" +#include "llvm/IR/Argument.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" + +namespace llvm { +void reduceArgumentsDeltaPass(TestRunner &Test); +} // namespace llvm diff --git a/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp b/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp new file mode 100644 index 000000000000..03c3962d2fd9 --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceBasicBlocks.cpp @@ -0,0 +1,146 @@ +//===- ReduceArguments.cpp - Specialized Delta Pass -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass in order +// to reduce uninteresting Arguments from defined functions. +// +//===----------------------------------------------------------------------===// + +#include "ReduceBasicBlocks.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include <vector> + +using namespace llvm; + +/// Replaces BB Terminator with one that only contains Chunk BBs +static void replaceBranchTerminator(BasicBlock &BB, + std::set<BasicBlock *> BBsToKeep) { + auto Term = BB.getTerminator(); + std::vector<BasicBlock *> ChunkSucessors; + for (auto Succ : successors(&BB)) + if (BBsToKeep.count(Succ)) + ChunkSucessors.push_back(Succ); + + // BB only references Chunk BBs + if (ChunkSucessors.size() == Term->getNumSuccessors()) + return; + + bool IsBranch = isa<BranchInst>(Term); + Value *Address = nullptr; + if (auto IndBI = dyn_cast<IndirectBrInst>(Term)) + Address = IndBI->getAddress(); + + Term->eraseFromParent(); + + if (ChunkSucessors.empty()) { + ReturnInst::Create(BB.getContext(), nullptr, &BB); + return; + } + + if (IsBranch) + BranchInst::Create(ChunkSucessors[0], &BB); + + if (Address) { + auto NewIndBI = + IndirectBrInst::Create(Address, ChunkSucessors.size(), &BB); + for (auto Dest : ChunkSucessors) + NewIndBI->addDestination(Dest); + } +} + +/// Removes uninteresting BBs from switch, if the default case ends up being +/// uninteresting, the switch is replaced with a void return (since it has to be +/// replace with something) +static void removeUninterestingBBsFromSwitch(SwitchInst &SwInst, + std::set<BasicBlock *> BBsToKeep) { + if (!BBsToKeep.count(SwInst.getDefaultDest())) { + ReturnInst::Create(SwInst.getContext(), nullptr, SwInst.getParent()); + SwInst.eraseFromParent(); + } else + for (int I = 0, E = SwInst.getNumCases(); I != E; ++I) { + auto Case = SwInst.case_begin() + I; + if (!BBsToKeep.count(Case->getCaseSuccessor())) { + SwInst.removeCase(Case); + --I; + --E; + } + } +} + +/// Removes out-of-chunk arguments from functions, and modifies their calls +/// accordingly. It also removes allocations of out-of-chunk arguments. +static void extractBasicBlocksFromModule(std::vector<Chunk> ChunksToKeep, + Module *Program) { + int I = 0, BBCount = 0; + std::set<BasicBlock *> BBsToKeep; + + for (auto &F : *Program) + for (auto &BB : F) + if (I < (int)ChunksToKeep.size()) { + if (ChunksToKeep[I].contains(++BBCount)) + BBsToKeep.insert(&BB); + if (ChunksToKeep[I].end == BBCount) + ++I; + } + + std::vector<BasicBlock *> BBsToDelete; + for (auto &F : *Program) + for (auto &BB : F) { + if (!BBsToKeep.count(&BB)) { + BBsToDelete.push_back(&BB); + // Remove out-of-chunk BB from successor phi nodes + for (auto *Succ : successors(&BB)) + Succ->removePredecessor(&BB); + } + } + + // Replace terminators that reference out-of-chunk BBs + for (auto &F : *Program) + for (auto &BB : F) { + if (auto *SwInst = dyn_cast<SwitchInst>(BB.getTerminator())) + removeUninterestingBBsFromSwitch(*SwInst, BBsToKeep); + else + replaceBranchTerminator(BB, BBsToKeep); + } + + // Replace out-of-chunk switch uses + for (auto &BB : BBsToDelete) { + // Instructions might be referenced in other BBs + for (auto &I : *BB) + I.replaceAllUsesWith(UndefValue::get(I.getType())); + BB->eraseFromParent(); + } +} + +/// Counts the amount of basic blocks and prints their name & respective index +static int countBasicBlocks(Module *Program) { + // TODO: Silence index with --quiet flag + outs() << "----------------------------\n"; + int BBCount = 0; + for (auto &F : *Program) + for (auto &BB : F) { + if (BB.hasName()) + outs() << "\t" << ++BBCount << ": " << BB.getName() << "\n"; + else + outs() << "\t" << ++BBCount << ": Unnamed\n"; + } + + return BBCount; +} + +void llvm::reduceBasicBlocksDeltaPass(TestRunner &Test) { + outs() << "*** Reducing Basic Blocks...\n"; + int BBCount = countBasicBlocks(Test.getProgram()); + runDeltaPass(Test, BBCount, extractBasicBlocksFromModule); +} diff --git a/tools/llvm-reduce/deltas/ReduceBasicBlocks.h b/tools/llvm-reduce/deltas/ReduceBasicBlocks.h new file mode 100644 index 000000000000..cf76a0abbcd7 --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceBasicBlocks.h @@ -0,0 +1,20 @@ +//===- ReduceArguments.h - Specialized Delta Pass -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass in order +// to reduce uninteresting Arguments from defined functions. +// +//===----------------------------------------------------------------------===// + +#include "Delta.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" + +namespace llvm { +void reduceBasicBlocksDeltaPass(TestRunner &Test); +} // namespace llvm diff --git a/tools/llvm-reduce/deltas/ReduceFunctions.cpp b/tools/llvm-reduce/deltas/ReduceFunctions.cpp new file mode 100644 index 000000000000..3382f35a945a --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceFunctions.cpp @@ -0,0 +1,77 @@ +//===- ReduceFunctions.cpp - Specialized Delta Pass -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass in order +// to reduce functions (and any instruction that calls it) in the provided +// Module. +// +//===----------------------------------------------------------------------===// + +#include "ReduceFunctions.h" +#include "Delta.h" +#include "llvm/ADT/SetVector.h" +#include <set> + +using namespace llvm; + +/// Removes all the Defined Functions (as well as their calls) +/// that aren't inside any of the desired Chunks. +static void extractFunctionsFromModule(const std::vector<Chunk> &ChunksToKeep, + Module *Program) { + // Get functions inside desired chunks + std::set<Function *> FuncsToKeep; + int I = 0, FunctionCount = 0; + for (auto &F : *Program) + if (I < (int)ChunksToKeep.size()) { + if (ChunksToKeep[I].contains(++FunctionCount)) + FuncsToKeep.insert(&F); + if (FunctionCount == ChunksToKeep[I].end) + ++I; + } + + // Delete out-of-chunk functions, and replace their calls with undef + std::vector<Function *> FuncsToRemove; + SetVector<CallInst *> CallsToRemove; + for (auto &F : *Program) + if (!FuncsToKeep.count(&F)) { + for (auto U : F.users()) + if (auto *Call = dyn_cast<CallInst>(U)) { + Call->replaceAllUsesWith(UndefValue::get(Call->getType())); + CallsToRemove.insert(Call); + } + F.replaceAllUsesWith(UndefValue::get(F.getType())); + FuncsToRemove.push_back(&F); + } + + for (auto *C : CallsToRemove) + C->eraseFromParent(); + + for (auto *F : FuncsToRemove) + F->eraseFromParent(); +} + +/// Counts the amount of non-declaration functions and prints their +/// respective name & index +static int countFunctions(Module *Program) { + // TODO: Silence index with --quiet flag + errs() << "----------------------------\n"; + errs() << "Function Index Reference:\n"; + int FunctionCount = 0; + for (auto &F : *Program) + errs() << "\t" << ++FunctionCount << ": " << F.getName() << "\n"; + + errs() << "----------------------------\n"; + return FunctionCount; +} + +void llvm::reduceFunctionsDeltaPass(TestRunner &Test) { + errs() << "*** Reducing Functions...\n"; + int Functions = countFunctions(Test.getProgram()); + runDeltaPass(Test, Functions, extractFunctionsFromModule); + errs() << "----------------------------\n"; +} diff --git a/tools/llvm-reduce/deltas/ReduceFunctions.h b/tools/llvm-reduce/deltas/ReduceFunctions.h new file mode 100644 index 000000000000..7c2cd3f33e9f --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceFunctions.h @@ -0,0 +1,20 @@ +//===- ReduceFunctions.h - Specialized Delta Pass -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass in order +// to reduce functions (and any instruction that calls it) in the provided +// Module. +// +//===----------------------------------------------------------------------===// + +#include "Delta.h" +#include "llvm/Transforms/Utils/Cloning.h" + +namespace llvm { +void reduceFunctionsDeltaPass(TestRunner &Test); +} // namespace llvm diff --git a/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp b/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp new file mode 100644 index 000000000000..5732208ee0a9 --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp @@ -0,0 +1,74 @@ +//===- ReduceGlobalVars.cpp - Specialized Delta Pass ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass in order +// to reduce initialized Global Variables in the provided Module. +// +//===----------------------------------------------------------------------===// + +#include "ReduceGlobalVars.h" +#include <set> + +using namespace llvm; + +/// Removes all the Initialized GVs that aren't inside the desired Chunks. +static void extractGVsFromModule(std::vector<Chunk> ChunksToKeep, + Module *Program) { + // Get GVs inside desired chunks + std::set<GlobalVariable *> GVsToKeep; + int I = 0, GVCount = 0; + for (auto &GV : Program->globals()) + if (GV.hasInitializer() && I < (int)ChunksToKeep.size()) { + if (ChunksToKeep[I].contains(++GVCount)) + GVsToKeep.insert(&GV); + if (GVCount == ChunksToKeep[I].end) + ++I; + } + + // Delete out-of-chunk GVs and their uses + std::vector<GlobalVariable *> ToRemove; + std::vector<Instruction *> InstToRemove; + for (auto &GV : Program->globals()) + if (GV.hasInitializer() && !GVsToKeep.count(&GV)) { + for (auto U : GV.users()) + if (auto *Inst = dyn_cast<Instruction>(U)) + InstToRemove.push_back(Inst); + + GV.replaceAllUsesWith(UndefValue::get(GV.getType())); + ToRemove.push_back(&GV); + } + + // Delete Instruction uses of unwanted GVs + for (auto *Inst : InstToRemove) { + Inst->replaceAllUsesWith(UndefValue::get(Inst->getType())); + Inst->eraseFromParent(); + } + + for (auto *GV : ToRemove) + GV->eraseFromParent(); +} + +/// Counts the amount of initialized GVs and displays their +/// respective name & index +static int countGVs(Module *Program) { + // TODO: Silence index with --quiet flag + outs() << "----------------------------\n"; + outs() << "GlobalVariable Index Reference:\n"; + int GVCount = 0; + for (auto &GV : Program->globals()) + if (GV.hasInitializer()) + outs() << "\t" << ++GVCount << ": " << GV.getName() << "\n"; + outs() << "----------------------------\n"; + return GVCount; +} + +void llvm::reduceGlobalsDeltaPass(TestRunner &Test) { + outs() << "*** Reducing GVs...\n"; + int GVCount = countGVs(Test.getProgram()); + runDeltaPass(Test, GVCount, extractGVsFromModule); +} diff --git a/tools/llvm-reduce/deltas/ReduceGlobalVars.h b/tools/llvm-reduce/deltas/ReduceGlobalVars.h new file mode 100644 index 000000000000..d4a870aded58 --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceGlobalVars.h @@ -0,0 +1,20 @@ +//===- ReduceGlobalVars.h - Specialized Delta Pass ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass in order +// to reduce initialized Global Variables in the provided Module. +// +//===----------------------------------------------------------------------===// + +#include "Delta.h" +#include "llvm/IR/Value.h" +#include "llvm/Transforms/Utils/Cloning.h" + +namespace llvm { +void reduceGlobalsDeltaPass(TestRunner &Test); +} // namespace llvm diff --git a/tools/llvm-reduce/deltas/ReduceInstructions.cpp b/tools/llvm-reduce/deltas/ReduceInstructions.cpp new file mode 100644 index 000000000000..b3497ad2dc02 --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceInstructions.cpp @@ -0,0 +1,65 @@ +//===- ReduceArguments.cpp - Specialized Delta Pass -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass in order +// to reduce uninteresting Arguments from defined functions. +// +//===----------------------------------------------------------------------===// + +#include "ReduceInstructions.h" + +using namespace llvm; + +/// Removes out-of-chunk arguments from functions, and modifies their calls +/// accordingly. It also removes allocations of out-of-chunk arguments. +static void extractInstrFromModule(std::vector<Chunk> ChunksToKeep, + Module *Program) { + int I = 0, InstCount = 0; + std::set<Instruction *> InstToKeep; + + for (auto &F : *Program) + for (auto &BB : F) + for (auto &Inst : BB) + if (I < (int)ChunksToKeep.size()) { + if (ChunksToKeep[I].contains(++InstCount)) + InstToKeep.insert(&Inst); + if (ChunksToKeep[I].end == InstCount) + ++I; + } + + std::vector<Instruction *> InstToDelete; + for (auto &F : *Program) + for (auto &BB : F) + for (auto &Inst : BB) + if (!InstToKeep.count(&Inst)) { + Inst.replaceAllUsesWith(UndefValue::get(Inst.getType())); + InstToDelete.push_back(&Inst); + } + + for (auto &I : InstToDelete) + I->eraseFromParent(); +} + +/// Counts the amount of basic blocks and prints their name & respective index +static unsigned countInstructions(Module *Program) { + // TODO: Silence index with --quiet flag + outs() << "----------------------------\n"; + int InstCount = 0; + for (auto &F : *Program) + for (auto &BB : F) + InstCount += BB.getInstList().size(); + outs() << "Number of instructions: " << InstCount << "\n"; + + return InstCount; +} + +void llvm::reduceInstructionsDeltaPass(TestRunner &Test) { + outs() << "*** Reducing Insructions...\n"; + unsigned InstCount = countInstructions(Test.getProgram()); + runDeltaPass(Test, InstCount, extractInstrFromModule); +} diff --git a/tools/llvm-reduce/deltas/ReduceInstructions.h b/tools/llvm-reduce/deltas/ReduceInstructions.h new file mode 100644 index 000000000000..a9266acd051a --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceInstructions.h @@ -0,0 +1,20 @@ +//===- ReduceArguments.h - Specialized Delta Pass -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a function which calls the Generic Delta pass in order +// to reduce uninteresting Arguments from defined functions. +// +//===----------------------------------------------------------------------===// + +#include "Delta.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" + +namespace llvm { +void reduceInstructionsDeltaPass(TestRunner &Test); +} // namespace llvm diff --git a/tools/llvm-reduce/deltas/ReduceMetadata.cpp b/tools/llvm-reduce/deltas/ReduceMetadata.cpp new file mode 100644 index 000000000000..4ea223546efa --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceMetadata.cpp @@ -0,0 +1,138 @@ +//===- ReduceMetadata.cpp - Specialized Delta Pass ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements two functions used by the Generic Delta Debugging +// Algorithm, which are used to reduce Metadata nodes. +// +//===----------------------------------------------------------------------===// + +#include "ReduceMetadata.h" +#include "Delta.h" +#include "llvm/ADT/SmallVector.h" +#include <set> +#include <vector> + +using namespace llvm; + +/// Adds all Unnamed Metadata Nodes that are inside desired Chunks to set +template <class T> +static void getChunkMetadataNodes(T &MDUser, int &I, + const std::vector<Chunk> &ChunksToKeep, + std::set<MDNode *> &SeenNodes, + std::set<MDNode *> &NodesToKeep) { + SmallVector<std::pair<unsigned, MDNode *>, 4> MDs; + MDUser.getAllMetadata(MDs); + for (auto &MD : MDs) { + SeenNodes.insert(MD.second); + if (I < (int)ChunksToKeep.size()) { + if (ChunksToKeep[I].contains(SeenNodes.size())) + NodesToKeep.insert(MD.second); + if (ChunksToKeep[I].end == (int)SeenNodes.size()) + ++I; + } + } +} + +/// Erases out-of-chunk unnamed metadata nodes from its user +template <class T> +static void eraseMetadataIfOutsideChunk(T &MDUser, + const std::set<MDNode *> &NodesToKeep) { + SmallVector<std::pair<unsigned, MDNode *>, 4> MDs; + MDUser.getAllMetadata(MDs); + for (int I = 0, E = MDs.size(); I != E; ++I) + if (!NodesToKeep.count(MDs[I].second)) + MDUser.setMetadata(I, NULL); +} + +/// Removes all the Named and Unnamed Metadata Nodes, as well as any debug +/// functions that aren't inside the desired Chunks. +static void extractMetadataFromModule(const std::vector<Chunk> &ChunksToKeep, + Module *Program) { + std::set<MDNode *> SeenNodes; + std::set<MDNode *> NodesToKeep; + int I = 0; + + // Add chunk MDNodes used by GVs, Functions, and Instructions to set + for (auto &GV : Program->globals()) + getChunkMetadataNodes(GV, I, ChunksToKeep, SeenNodes, NodesToKeep); + + for (auto &F : *Program) { + getChunkMetadataNodes(F, I, ChunksToKeep, SeenNodes, NodesToKeep); + for (auto &BB : F) + for (auto &Inst : BB) + getChunkMetadataNodes(Inst, I, ChunksToKeep, SeenNodes, NodesToKeep); + } + + // Once more, go over metadata nodes, but deleting the ones outside chunks + for (auto &GV : Program->globals()) + eraseMetadataIfOutsideChunk(GV, NodesToKeep); + + for (auto &F : *Program) { + eraseMetadataIfOutsideChunk(F, NodesToKeep); + for (auto &BB : F) + for (auto &Inst : BB) + eraseMetadataIfOutsideChunk(Inst, NodesToKeep); + } + + + // Get out-of-chunk Named metadata nodes + unsigned MetadataCount = SeenNodes.size(); + std::vector<NamedMDNode *> NamedNodesToDelete; + for (auto &MD : Program->named_metadata()) { + if (I < (int)ChunksToKeep.size()) { + if (!ChunksToKeep[I].contains(++MetadataCount)) + NamedNodesToDelete.push_back(&MD); + if (ChunksToKeep[I].end == (int)SeenNodes.size()) + ++I; + } else + NamedNodesToDelete.push_back(&MD); + } + + for (auto *NN : NamedNodesToDelete) { + for (int I = 0, E = NN->getNumOperands(); I != E; ++I) + NN->setOperand(I, NULL); + NN->eraseFromParent(); + } +} + +// Gets unnamed metadata nodes used by a given instruction/GV/function and adds +// them to the set of seen nodes +template <class T> +static void addMetadataToSet(T &MDUser, std::set<MDNode *> &UnnamedNodes) { + SmallVector<std::pair<unsigned, MDNode *>, 4> MDs; + MDUser.getAllMetadata(MDs); + for (auto &MD : MDs) + UnnamedNodes.insert(MD.second); +} + +/// Returns the amount of Named and Unnamed Metadata Nodes +static int countMetadataTargets(Module *Program) { + std::set<MDNode *> UnnamedNodes; + int NamedMetadataNodes = Program->named_metadata_size(); + + // Get metadata nodes used by globals + for (auto &GV : Program->globals()) + addMetadataToSet(GV, UnnamedNodes); + + // Do the same for nodes used by functions & instructions + for (auto &F : *Program) { + addMetadataToSet(F, UnnamedNodes); + for (auto &BB : F) + for (auto &I : BB) + addMetadataToSet(I, UnnamedNodes); + } + + return UnnamedNodes.size() + NamedMetadataNodes; +} + +void llvm::reduceMetadataDeltaPass(TestRunner &Test) { + outs() << "*** Reducing Metadata...\n"; + int MDCount = countMetadataTargets(Test.getProgram()); + runDeltaPass(Test, MDCount, extractMetadataFromModule); + outs() << "----------------------------\n"; +} diff --git a/tools/llvm-reduce/deltas/ReduceMetadata.h b/tools/llvm-reduce/deltas/ReduceMetadata.h new file mode 100644 index 000000000000..275b44c2aa7d --- /dev/null +++ b/tools/llvm-reduce/deltas/ReduceMetadata.h @@ -0,0 +1,18 @@ +//===- ReduceMetadata.h - Specialized Delta Pass ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements two functions used by the Generic Delta Debugging +// Algorithm, which are used to reduce Metadata nodes. +// +//===----------------------------------------------------------------------===// + +#include "TestRunner.h" + +namespace llvm { +void reduceMetadataDeltaPass(TestRunner &Test); +} // namespace llvm diff --git a/tools/llvm-reduce/llvm-reduce.cpp b/tools/llvm-reduce/llvm-reduce.cpp new file mode 100644 index 000000000000..83dcf980a786 --- /dev/null +++ b/tools/llvm-reduce/llvm-reduce.cpp @@ -0,0 +1,114 @@ +//===- llvm-reduce.cpp - The LLVM Delta Reduction 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 tries to reduce an IR test case for a given interesting-ness +// test. It runs multiple delta debugging passes in order to minimize the input +// file. It's worth noting that this is a part of the bugpoint redesign +// proposal, and thus a *temporary* tool that will eventually be integrated +// into the bugpoint tool itself. +// +//===----------------------------------------------------------------------===// + +#include "DeltaManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Verifier.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include <system_error> +#include <vector> + +using namespace llvm; + +static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); +static cl::opt<bool> Version("v", cl::desc("Alias for -version"), cl::Hidden); + +static cl::opt<std::string> InputFilename(cl::Positional, cl::Required, + cl::desc("<input llvm ll/bc file>")); + +static cl::opt<std::string> + TestFilename("test", cl::Required, + cl::desc("Name of the interesting-ness test to be run")); + +static cl::list<std::string> + TestArguments("test-arg", cl::ZeroOrMore, + cl::desc("Arguments passed onto the interesting-ness test")); + +static cl::opt<std::string> + OutputFilename("output", + cl::desc("Specify the output file. default: reduced.ll")); +static cl::alias OutputFileAlias("o", cl::desc("Alias for -output"), + cl::aliasopt(OutputFilename)); + +static cl::opt<bool> + ReplaceInput("in-place", + cl::desc("WARNING: This option will replace your input file" + "with the reduced version!")); + +// Parses IR into a Module and verifies it +static std::unique_ptr<Module> parseInputFile(StringRef Filename, + LLVMContext &Ctxt) { + SMDiagnostic Err; + std::unique_ptr<Module> Result = parseIRFile(Filename, Err, Ctxt); + if (!Result) { + Err.print("llvm-reduce", errs()); + return Result; + } + + if (verifyModule(*Result, &errs())) { + errs() << "Error: " << Filename << " - input module is broken!\n"; + return std::unique_ptr<Module>(); + } + + return Result; +} + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv, "LLVM automatic testcase reducer.\n"); + + LLVMContext Context; + std::unique_ptr<Module> OriginalProgram = + parseInputFile(InputFilename, Context); + + // Initialize test environment + TestRunner Tester(TestFilename, TestArguments); + Tester.setProgram(std::move(OriginalProgram)); + + // Try to reduce code + runDeltaPasses(Tester); + + if (!Tester.getProgram()) { + errs() << "\nCouldnt reduce input :/\n"; + } else { + // Print reduced file to STDOUT + if (OutputFilename == "-") + Tester.getProgram()->print(outs(), nullptr); + else { + if (ReplaceInput) // In-place + OutputFilename = InputFilename.c_str(); + else if (OutputFilename.empty()) + OutputFilename = "reduced.ll"; + + std::error_code EC; + raw_fd_ostream Out(OutputFilename, EC); + if (EC) { + errs() << "Error opening output file: " << EC.message() << "!\n"; + exit(1); + } + Tester.getProgram()->print(Out, /*AnnotationWriter=*/nullptr); + errs() << "\nDone reducing! Reduced testcase: " << OutputFilename << "\n"; + } + } + + return 0; +} diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp index a7cc1deb8cf6..3a36e7709483 100644 --- a/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -27,12 +27,13 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MSVCErrorWorkarounds.h" #include "llvm/Support/Memory.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/MSVCErrorWorkarounds.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" #include <future> @@ -138,8 +139,21 @@ PrintAllocationRequests("print-alloc-requests", "manager by RuntimeDyld"), cl::Hidden); +static cl::opt<bool> ShowTimes("show-times", + cl::desc("Show times for llvm-rtdyld phases"), + cl::init(false)); + ExitOnError ExitOnErr; +struct RTDyldTimers { + TimerGroup RTDyldTG{"llvm-rtdyld timers", "timers for llvm-rtdyld phases"}; + Timer LoadObjectsTimer{"load", "time to load/add object files", RTDyldTG}; + Timer LinkTimer{"link", "time to link object files", RTDyldTG}; + Timer RunTimer{"run", "time to execute jitlink'd code", RTDyldTG}; +}; + +std::unique_ptr<RTDyldTimers> Timers; + /* *** */ using SectionIDMap = StringMap<unsigned>; @@ -441,8 +455,6 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { continue; } object::section_iterator Sec = *SecOrErr; - StringRef SecName; - Sec->getName(SecName); Address.SectionIndex = Sec->getIndex(); uint64_t SectionLoadAddress = LoadedObjInfo->getSectionLoadAddress(*Sec); @@ -491,35 +503,41 @@ static int executeInput() { // If we don't have any input files, read from stdin. if (!InputFileList.size()) InputFileList.push_back("-"); - for (auto &File : InputFileList) { - // Load the input memory buffer. - ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer = - MemoryBuffer::getFileOrSTDIN(File); - if (std::error_code EC = InputBuffer.getError()) - ErrorAndExit("unable to read input: '" + EC.message() + "'"); - Expected<std::unique_ptr<ObjectFile>> MaybeObj( - ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef())); - - if (!MaybeObj) { - std::string Buf; - raw_string_ostream OS(Buf); - logAllUnhandledErrors(MaybeObj.takeError(), OS); - OS.flush(); - ErrorAndExit("unable to create object file: '" + Buf + "'"); - } + { + TimeRegion TR(Timers ? &Timers->LoadObjectsTimer : nullptr); + for (auto &File : InputFileList) { + // Load the input memory buffer. + ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer = + MemoryBuffer::getFileOrSTDIN(File); + if (std::error_code EC = InputBuffer.getError()) + ErrorAndExit("unable to read input: '" + EC.message() + "'"); + Expected<std::unique_ptr<ObjectFile>> MaybeObj( + ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef())); + + if (!MaybeObj) { + std::string Buf; + raw_string_ostream OS(Buf); + logAllUnhandledErrors(MaybeObj.takeError(), OS); + OS.flush(); + ErrorAndExit("unable to create object file: '" + Buf + "'"); + } - ObjectFile &Obj = **MaybeObj; + ObjectFile &Obj = **MaybeObj; - // Load the object file - Dyld.loadObject(Obj); - if (Dyld.hasError()) { - ErrorAndExit(Dyld.getErrorString()); + // Load the object file + Dyld.loadObject(Obj); + if (Dyld.hasError()) { + ErrorAndExit(Dyld.getErrorString()); + } } } - // Resove all the relocations we can. - // FIXME: Error out if there are unresolved relocations. - Dyld.resolveRelocations(); + { + TimeRegion TR(Timers ? &Timers->LinkTimer : nullptr); + // Resove all the relocations we can. + // FIXME: Error out if there are unresolved relocations. + Dyld.resolveRelocations(); + } // Get the address of the entry point (_main by default). void *MainAddress = Dyld.getSymbolLocalAddress(EntryPoint); @@ -551,7 +569,13 @@ static int executeInput() { for (auto &Arg : InputArgv) Argv.push_back(Arg.data()); Argv.push_back(nullptr); - return Main(Argv.size() - 1, Argv.data()); + int Result = 0; + { + TimeRegion TR(Timers ? &Timers->RunTimer : nullptr); + Result = Main(Argv.size() - 1, Argv.data()); + } + + return Result; } static int checkAllExpressions(RuntimeDyldChecker &Checker) { @@ -891,7 +915,7 @@ static int linkAndVerify() { ObjectFile &Obj = **MaybeObj; if (!Checker) - Checker = llvm::make_unique<RuntimeDyldChecker>( + Checker = std::make_unique<RuntimeDyldChecker>( IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, GetStubInfo, Obj.isLittleEndian() ? support::little : support::big, Disassembler.get(), InstPrinter.get(), dbgs()); @@ -937,16 +961,28 @@ int main(int argc, char **argv) { ExitOnErr.setBanner(std::string(argv[0]) + ": "); + Timers = ShowTimes ? std::make_unique<RTDyldTimers>() : nullptr; + + int Result; switch (Action) { case AC_Execute: - return executeInput(); + Result = executeInput(); + break; case AC_PrintDebugLineInfo: - return printLineInfoForInput(/* LoadObjects */ true,/* UseDebugObj */ true); + Result = + printLineInfoForInput(/* LoadObjects */ true, /* UseDebugObj */ true); + break; case AC_PrintLineInfo: - return printLineInfoForInput(/* LoadObjects */ true,/* UseDebugObj */false); + Result = + printLineInfoForInput(/* LoadObjects */ true, /* UseDebugObj */ false); + break; case AC_PrintObjectLineInfo: - return printLineInfoForInput(/* LoadObjects */false,/* UseDebugObj */false); + Result = + printLineInfoForInput(/* LoadObjects */ false, /* UseDebugObj */ false); + break; case AC_Verify: - return linkAndVerify(); + Result = linkAndVerify(); + break; } + return Result; } diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp index a455bf13fe7b..5f36a785332b 100644 --- a/tools/llvm-stress/llvm-stress.cpp +++ b/tools/llvm-stress/llvm-stress.cpp @@ -735,7 +735,7 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm codegen stress-tester\n"); llvm_shutdown_obj Y; - auto M = llvm::make_unique<Module>("/tmp/autogen.bc", Context); + auto M = std::make_unique<Module>("/tmp/autogen.bc", Context); Function *F = GenEmptyFunction(M.get()); // Pick an initial seed value @@ -752,7 +752,7 @@ int main(int argc, char **argv) { OutputFilename = "-"; std::error_code EC; - Out.reset(new ToolOutputFile(OutputFilename, EC, sys::fs::F_None)); + Out.reset(new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None)); if (EC) { errs() << EC.message() << '\n'; return 1; diff --git a/tools/llvm-symbolizer/llvm-symbolizer.cpp b/tools/llvm-symbolizer/llvm-symbolizer.cpp index ea94cf9b69a1..54ce87d47979 100644 --- a/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -55,6 +55,10 @@ static cl::opt<bool> cl::desc("Interpret addresses as relative addresses"), cl::ReallyHidden); +static cl::opt<bool> ClUntagAddresses( + "untag-addresses", cl::init(true), + cl::desc("Remove memory tags from addresses before symbolization")); + static cl::opt<bool> ClPrintInlining("inlining", cl::init(true), cl::desc("Print all inlined frames for a given address")); @@ -274,6 +278,7 @@ int main(int argc, char **argv) { ClDemangle.setInitialValue(false); ClPrintFunctions.setInitialValue(FunctionNameKind::None); ClPrintInlining.setInitialValue(false); + ClUntagAddresses.setInitialValue(false); ClOutputStyle.setInitialValue(DIPrinter::OutputStyle::GNU); } @@ -290,6 +295,7 @@ int main(int argc, char **argv) { Opts.UseSymbolTable = ClUseSymbolTable; Opts.Demangle = ClDemangle; Opts.RelativeAddresses = ClUseRelativeAddress; + Opts.UntagAddresses = ClUntagAddresses; Opts.DefaultArch = ClDefaultArch; Opts.FallbackDebugPath = ClFallbackDebugPath; Opts.DWPName = ClDwpName; diff --git a/tools/llvm-xray/func-id-helper.cpp b/tools/llvm-xray/func-id-helper.cpp index dc821a420c67..afc912a6398e 100644 --- a/tools/llvm-xray/func-id-helper.cpp +++ b/tools/llvm-xray/func-id-helper.cpp @@ -36,7 +36,7 @@ std::string FuncIdConversionHelper::SymbolOrNumber(int32_t FuncId) const { ModuleAddress.SectionIndex = object::SectionedAddress::UndefSection; if (auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, ModuleAddress)) { auto &DI = *ResOrErr; - if (DI.FunctionName == "<invalid>") + if (DI.FunctionName == DILineInfo::BadString) F << "@(" << std::hex << It->second << ")"; else F << DI.FunctionName; diff --git a/tools/llvm-xray/xray-account.cpp b/tools/llvm-xray/xray-account.cpp index 2b49a311d7e3..e37cd212377a 100644 --- a/tools/llvm-xray/xray-account.cpp +++ b/tools/llvm-xray/xray-account.cpp @@ -421,7 +421,7 @@ static CommandRegistration Unused(&Account, []() -> Error { } std::error_code EC; - raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::F_Text); + raw_fd_ostream OS(AccountOutput, EC, sys::fs::OpenFlags::OF_Text); if (EC) return make_error<StringError>( Twine("Cannot open file '") + AccountOutput + "' for writing.", EC); diff --git a/tools/llvm-xray/xray-converter.cpp b/tools/llvm-xray/xray-converter.cpp index dfc757e0f276..7258245b95cc 100644 --- a/tools/llvm-xray/xray-converter.cpp +++ b/tools/llvm-xray/xray-converter.cpp @@ -387,8 +387,8 @@ static CommandRegistration Unused(&Convert, []() -> Error { std::error_code EC; raw_fd_ostream OS(ConvertOutput, EC, ConvertOutputFormat == ConvertFormats::BINARY - ? sys::fs::OpenFlags::F_None - : sys::fs::OpenFlags::F_Text); + ? sys::fs::OpenFlags::OF_None + : sys::fs::OpenFlags::OF_Text); if (EC) return make_error<StringError>( Twine("Cannot open file '") + ConvertOutput + "' for writing.", EC); diff --git a/tools/llvm-xray/xray-extract.cpp b/tools/llvm-xray/xray-extract.cpp index 7c7d26b5a389..7800b88d9eeb 100644 --- a/tools/llvm-xray/xray-extract.cpp +++ b/tools/llvm-xray/xray-extract.cpp @@ -80,7 +80,7 @@ static CommandRegistration Unused(&Extract, []() -> Error { InstrumentationMapOrError.takeError()); std::error_code EC; - raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::F_Text); + raw_fd_ostream OS(ExtractOutput, EC, sys::fs::OpenFlags::OF_Text); if (EC) return make_error<StringError>( Twine("Cannot open file '") + ExtractOutput + "' for writing.", EC); diff --git a/tools/llvm-xray/xray-fdr-dump.cpp b/tools/llvm-xray/xray-fdr-dump.cpp index 81a93cac57c4..295f7a78765f 100644 --- a/tools/llvm-xray/xray-fdr-dump.cpp +++ b/tools/llvm-xray/xray-fdr-dump.cpp @@ -51,7 +51,7 @@ static CommandRegistration Unused(&Dump, []() -> Error { sys::fs::closeFile(*FDOrErr); DataExtractor DE(StringRef(MappedFile.data(), MappedFile.size()), true, 8); - uint32_t OffsetPtr = 0; + uint64_t OffsetPtr = 0; auto FileHeaderOrError = readBinaryFormatHeader(DE, OffsetPtr); if (!FileHeaderOrError) diff --git a/tools/llvm-xray/xray-graph-diff.cpp b/tools/llvm-xray/xray-graph-diff.cpp index a514be97f40b..116aa6869ec1 100644 --- a/tools/llvm-xray/xray-graph-diff.cpp +++ b/tools/llvm-xray/xray-graph-diff.cpp @@ -470,7 +470,7 @@ static CommandRegistration Unused(&GraphDiff, []() -> Error { auto &GDR = *GDROrErr; std::error_code EC; - raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::F_Text); + raw_fd_ostream OS(GraphDiffOutput, EC, sys::fs::OpenFlags::OF_Text); if (EC) return make_error<StringError>( Twine("Cannot open file '") + GraphDiffOutput + "' for writing.", EC); diff --git a/tools/llvm-xray/xray-graph.cpp b/tools/llvm-xray/xray-graph.cpp index c09357fcb502..0be511219c1a 100644 --- a/tools/llvm-xray/xray-graph.cpp +++ b/tools/llvm-xray/xray-graph.cpp @@ -506,7 +506,7 @@ static CommandRegistration Unused(&GraphC, []() -> Error { auto &GR = *GROrError; std::error_code EC; - raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::F_Text); + raw_fd_ostream OS(GraphOutput, EC, sys::fs::OpenFlags::OF_Text); if (EC) return make_error<StringError>( Twine("Cannot open file '") + GraphOutput + "' for writing.", EC); diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index ccf8b073b82b..15495a511d06 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -523,7 +523,6 @@ int main(int argc, char **argv) { initializeDwarfEHPreparePass(Registry); initializeSafeStackLegacyPassPass(Registry); initializeSjLjEHPreparePass(Registry); - initializeStackProtectorPass(Registry); initializePreISelIntrinsicLoweringLegacyPassPass(Registry); initializeGlobalMergePass(Registry); initializeIndirectBrExpandPassPass(Registry); @@ -612,7 +611,9 @@ int main(int argc, char **argv) { OutputFilename = "-"; std::error_code EC; - Out.reset(new ToolOutputFile(OutputFilename, EC, sys::fs::F_None)); + sys::fs::OpenFlags Flags = OutputAssembly ? sys::fs::OF_Text + : sys::fs::OF_None; + Out.reset(new ToolOutputFile(OutputFilename, EC, Flags)); if (EC) { errs() << EC.message() << '\n'; return 1; @@ -620,7 +621,7 @@ int main(int argc, char **argv) { if (!ThinLinkBitcodeFile.empty()) { ThinLinkOut.reset( - new ToolOutputFile(ThinLinkBitcodeFile, EC, sys::fs::F_None)); + new ToolOutputFile(ThinLinkBitcodeFile, EC, sys::fs::OF_None)); if (EC) { errs() << EC.message() << '\n'; return 1; @@ -720,8 +721,8 @@ int main(int argc, char **argv) { OutputFilename = "-"; std::error_code EC; - Out = llvm::make_unique<ToolOutputFile>(OutputFilename, EC, - sys::fs::F_None); + Out = std::make_unique<ToolOutputFile>(OutputFilename, EC, + sys::fs::OF_None); if (EC) { errs() << EC.message() << '\n'; return 1; @@ -867,7 +868,7 @@ int main(int argc, char **argv) { assert(Out); OS = &Out->os(); if (RunTwice) { - BOS = make_unique<raw_svector_ostream>(Buffer); + BOS = std::make_unique<raw_svector_ostream>(Buffer); OS = BOS.get(); } if (OutputAssembly) { diff --git a/tools/vfabi-demangle-fuzzer/CMakeLists.txt b/tools/vfabi-demangle-fuzzer/CMakeLists.txt new file mode 100644 index 000000000000..908364690f5e --- /dev/null +++ b/tools/vfabi-demangle-fuzzer/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_LINK_COMPONENTS + Analysis + Support +) +add_llvm_fuzzer(vfabi-demangler-fuzzer + vfabi-demangler-fuzzer.cpp +) diff --git a/tools/vfabi-demangle-fuzzer/vfabi-demangler-fuzzer.cpp b/tools/vfabi-demangle-fuzzer/vfabi-demangler-fuzzer.cpp new file mode 100644 index 000000000000..13657effbbeb --- /dev/null +++ b/tools/vfabi-demangle-fuzzer/vfabi-demangler-fuzzer.cpp @@ -0,0 +1,26 @@ +//===-- vfabi-demangler-fuzzer.cpp - Fuzzer VFABI using lib/Fuzzer ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Build tool to fuzz the demangler for the vector function ABI names. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/VectorUtils.h" + +using namespace llvm; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + const StringRef MangledName((const char *)Data, Size); + const auto Info = VFABI::tryDemangleForVFABI(MangledName); + + // Do not optimize away the return value. Inspired by + // https://github.com/google/benchmark/blob/master/include/benchmark/benchmark.h#L307-L345 + asm volatile("" : : "r,m"(Info) : "memory"); + + return 0; +} |