diff options
author | Stephen Hines <srhines@google.com> | 2014-12-04 02:59:27 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-12-04 02:59:28 +0000 |
commit | 876d6995443e99d13696f3941c3a789a4daa7c7a (patch) | |
tree | 8fb36d3910e3ee4c4e1b7422f4f017108efc52f5 /tools | |
parent | d2327b22152ced7bc46dc629fc908959e8a52d03 (diff) | |
parent | 37ed9c199ca639565f6ce88105f9e39e898d82d0 (diff) | |
download | external_llvm-876d6995443e99d13696f3941c3a789a4daa7c7a.zip external_llvm-876d6995443e99d13696f3941c3a789a4daa7c7a.tar.gz external_llvm-876d6995443e99d13696f3941c3a789a4daa7c7a.tar.bz2 |
Merge "Update aosp/master LLVM for rebase to r222494."
Diffstat (limited to 'tools')
156 files changed, 10837 insertions, 2123 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 846ad1e..fd761ec 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -8,6 +8,12 @@ else(WITH_POLLY) list(APPEND LLVM_IMPLICIT_PROJECT_IGNORE "${LLVM_MAIN_SRC_DIR}/tools/polly") endif(WITH_POLLY) +if( LLVM_BUILD_LLVM_DYLIB ) + add_llvm_tool_subdirectory(llvm-shlib) +else() + ignore_llvm_tool_subdirectory(llvm-shlib) +endif() + add_llvm_tool_subdirectory(opt) add_llvm_tool_subdirectory(llvm-as) add_llvm_tool_subdirectory(llvm-dis) @@ -30,6 +36,7 @@ add_llvm_tool_subdirectory(llvm-objdump) add_llvm_tool_subdirectory(llvm-readobj) add_llvm_tool_subdirectory(llvm-rtdyld) add_llvm_tool_subdirectory(llvm-dwarfdump) +add_llvm_tool_subdirectory(llvm-vtabledump) if( LLVM_USE_INTEL_JITEVENTS ) add_llvm_tool_subdirectory(llvm-jitlistener) else() @@ -42,6 +49,8 @@ add_llvm_tool_subdirectory(llvm-bcanalyzer) add_llvm_tool_subdirectory(llvm-stress) add_llvm_tool_subdirectory(llvm-mcmarkup) +add_llvm_tool_subdirectory(verify-uselistorder) + add_llvm_tool_subdirectory(llvm-symbolizer) add_llvm_tool_subdirectory(llvm-c-test) @@ -49,6 +58,8 @@ add_llvm_tool_subdirectory(llvm-c-test) add_llvm_tool_subdirectory(obj2yaml) add_llvm_tool_subdirectory(yaml2obj) +add_llvm_tool_subdirectory(llvm-go) + if(NOT CYGWIN AND LLVM_ENABLE_PIC) add_llvm_tool_subdirectory(lto) add_llvm_tool_subdirectory(llvm-lto) diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt index 1b537a3..13a08b2 100644 --- a/tools/LLVMBuild.txt +++ b/tools/LLVMBuild.txt @@ -16,7 +16,7 @@ ;===------------------------------------------------------------------------===; [common] -subdirectories = bugpoint llc lli llvm-ar llvm-as llvm-bcanalyzer llvm-cov llvm-diff llvm-dis llvm-dwarfdump llvm-extract llvm-jitlistener llvm-link llvm-lto llvm-mc llvm-nm llvm-objdump llvm-profdata llvm-rtdyld llvm-size macho-dump opt llvm-mcmarkup +subdirectories = bugpoint llc lli llvm-ar llvm-as llvm-bcanalyzer llvm-cov llvm-diff llvm-dis llvm-dwarfdump llvm-extract llvm-jitlistener llvm-link llvm-lto llvm-mc llvm-nm llvm-objdump llvm-profdata llvm-rtdyld llvm-size macho-dump opt llvm-mcmarkup verify-uselistorder [component_0] type = Group diff --git a/tools/Makefile b/tools/Makefile index 97ad99a..4b8923a 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -21,7 +21,8 @@ endif # Build LLD and LLDB if present. Note LLDB must be built last as it depends on # the wider LLVM infrastructure (including Clang). -OPTIONAL_DIRS := lld lldb +OPTIONAL_PARALLEL_DIRS += lld +OPTIONAL_DIRS := lldb # NOTE: The tools are organized into five groups of four consisting of one # large and three small executables. This is done to minimize memory load @@ -31,7 +32,8 @@ PARALLEL_DIRS := opt llvm-as llvm-dis llc llvm-ar llvm-nm llvm-link \ lli llvm-extract llvm-mc bugpoint llvm-bcanalyzer llvm-diff \ macho-dump llvm-objdump llvm-readobj llvm-rtdyld \ llvm-dwarfdump llvm-cov llvm-size llvm-stress llvm-mcmarkup \ - llvm-profdata llvm-symbolizer obj2yaml yaml2obj llvm-c-test + llvm-profdata llvm-symbolizer obj2yaml yaml2obj llvm-c-test \ + llvm-vtabledump verify-uselistorder # If Intel JIT Events support is configured, build an extra tool to test it. ifeq ($(USE_INTEL_JITEVENTS), 1) @@ -72,4 +74,8 @@ ifneq ($(ENABLE_SHARED),1) endif endif +ifneq (,$(filter go,$(BINDINGS_TO_BUILD))) + PARALLEL_DIRS += llvm-go +endif + include $(LEVEL)/Makefile.common diff --git a/tools/bugpoint-passes/CMakeLists.txt b/tools/bugpoint-passes/CMakeLists.txt index b7ee626..de68bb5 100644 --- a/tools/bugpoint-passes/CMakeLists.txt +++ b/tools/bugpoint-passes/CMakeLists.txt @@ -10,6 +10,10 @@ if( NOT LLVM_REQUIRES_RTTI ) endif() endif() +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Core) +endif() + add_llvm_loadable_module( BugpointPasses TestPasses.cpp ) diff --git a/tools/bugpoint/Android.mk b/tools/bugpoint/Android.mk index 78f3eff..512a91f 100644 --- a/tools/bugpoint/Android.mk +++ b/tools/bugpoint/Android.mk @@ -35,6 +35,7 @@ bugpoint_STATIC_LIBRARIES := \ libLLVMTarget \ libLLVMCore \ libLLVMMC \ + libLLVMProfileData \ libLLVMTransformUtils \ libLLVMVectorize \ libLLVMSupport \ diff --git a/tools/bugpoint/BugDriver.cpp b/tools/bugpoint/BugDriver.cpp index cecccbe..b8be17e 100644 --- a/tools/bugpoint/BugDriver.cpp +++ b/tools/bugpoint/BugDriver.cpp @@ -82,14 +82,10 @@ BugDriver::~BugDriver() { delete gcc; } - -/// ParseInputFile - Given a bitcode or assembly input filename, parse and -/// return it, or return null if not possible. -/// -Module *llvm::ParseInputFile(const std::string &Filename, - LLVMContext& Ctxt) { +std::unique_ptr<Module> llvm::parseInputFile(StringRef Filename, + LLVMContext &Ctxt) { SMDiagnostic Err; - Module *Result = ParseIRFile(Filename, Err, Ctxt); + std::unique_ptr<Module> Result = parseIRFile(Filename, Err, Ctxt); if (!Result) Err.print("bugpoint", errs()); @@ -120,23 +116,18 @@ bool BugDriver::addSources(const std::vector<std::string> &Filenames) { assert(!Filenames.empty() && "Must specify at least on input filename!"); // Load the first input file. - Program = ParseInputFile(Filenames[0], Context); + Program = parseInputFile(Filenames[0], Context).release(); if (!Program) return true; outs() << "Read input file : '" << Filenames[0] << "'\n"; for (unsigned i = 1, e = Filenames.size(); i != e; ++i) { - std::unique_ptr<Module> M(ParseInputFile(Filenames[i], Context)); + std::unique_ptr<Module> M = parseInputFile(Filenames[i], Context); if (!M.get()) return true; outs() << "Linking in input file: '" << Filenames[i] << "'\n"; - std::string ErrorMessage; - if (Linker::LinkModules(Program, M.get(), Linker::DestroySource, - &ErrorMessage)) { - errs() << ToolName << ": error linking in '" << Filenames[i] << "': " - << ErrorMessage << '\n'; + if (Linker::LinkModules(Program, M.get())) return true; - } } outs() << "*** All input ok\n"; diff --git a/tools/bugpoint/BugDriver.h b/tools/bugpoint/BugDriver.h index 3169d29..5797812 100644 --- a/tools/bugpoint/BugDriver.h +++ b/tools/bugpoint/BugDriver.h @@ -13,11 +13,12 @@ // //===----------------------------------------------------------------------===// -#ifndef BUGDRIVER_H -#define BUGDRIVER_H +#ifndef LLVM_TOOLS_BUGPOINT_BUGDRIVER_H +#define LLVM_TOOLS_BUGPOINT_BUGDRIVER_H #include "llvm/IR/ValueMap.h" #include "llvm/Transforms/Utils/ValueMapper.h" +#include <memory> #include <string> #include <vector> @@ -210,41 +211,46 @@ public: void EmitProgressBitcode(const Module *M, const std::string &ID, bool NoFlyer = false) const; - /// deleteInstructionFromProgram - This method clones the current Program and - /// deletes the specified instruction from the cloned module. It then runs a - /// series of cleanup passes (ADCE and SimplifyCFG) to eliminate any code - /// which depends on the value. The modified module is then returned. + /// This method clones the current Program and deletes the specified + /// instruction from the cloned module. It then runs a series of cleanup + /// passes (ADCE and SimplifyCFG) to eliminate any code which depends on the + /// value. The modified module is then returned. /// - Module *deleteInstructionFromProgram(const Instruction *I, unsigned Simp); + std::unique_ptr<Module> deleteInstructionFromProgram(const Instruction *I, + unsigned Simp); - /// performFinalCleanups - This method clones the current Program and performs - /// a series of cleanups intended to get rid of extra cruft on the module. If - /// the MayModifySemantics argument is true, then the cleanups is allowed to + /// This method clones the current Program and performs a series of cleanups + /// intended to get rid of extra cruft on the module. If the + /// MayModifySemantics argument is true, then the cleanups is allowed to /// modify how the code behaves. /// - Module *performFinalCleanups(Module *M, bool MayModifySemantics = false); - - /// ExtractLoop - Given a module, extract up to one loop from it into a new - /// function. This returns null if there are no extractable loops in the - /// program or if the loop extractor crashes. - Module *ExtractLoop(Module *M); - - /// ExtractMappedBlocksFromModule - Extract all but the specified basic blocks - /// into their own functions. The only detail is that M is actually a module - /// cloned from the one the BBs are in, so some mapping needs to be performed. - /// If this operation fails for some reason (ie the implementation is buggy), - /// this function should return null, otherwise it returns a new Module. - Module *ExtractMappedBlocksFromModule(const std::vector<BasicBlock*> &BBs, - Module *M); - - /// runPassesOn - Carefully run the specified set of pass on the specified - /// module, returning the transformed module on success, or a null pointer on - /// failure. If AutoDebugCrashes is set to true, then bugpoint will - /// automatically attempt to track down a crashing pass if one exists, and - /// this method will never return null. - Module *runPassesOn(Module *M, const std::vector<std::string> &Passes, - bool AutoDebugCrashes = false, unsigned NumExtraArgs = 0, - const char * const *ExtraArgs = nullptr); + std::unique_ptr<Module> performFinalCleanups(Module *M, + bool MayModifySemantics = false); + + /// Given a module, extract up to one loop from it into a new function. This + /// returns null if there are no extractable loops in the program or if the + /// loop extractor crashes. + std::unique_ptr<Module> extractLoop(Module *M); + + /// Extract all but the specified basic blocks into their own functions. The + /// only detail is that M is actually a module cloned from the one the BBs are + /// in, so some mapping needs to be performed. If this operation fails for + /// some reason (ie the implementation is buggy), this function should return + /// null, otherwise it returns a new Module. + std::unique_ptr<Module> + extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs, + Module *M); + + /// Carefully run the specified set of pass on the specified/ module, + /// returning the transformed module on success, or a null pointer on failure. + /// If AutoDebugCrashes is set to true, then bugpoint will automatically + /// attempt to track down a crashing pass if one exists, and this method will + /// never return null. + std::unique_ptr<Module> runPassesOn(Module *M, + const std::vector<std::string> &Passes, + bool AutoDebugCrashes = false, + unsigned NumExtraArgs = 0, + const char *const *ExtraArgs = nullptr); /// runPasses - Run the specified passes on Program, outputting a bitcode /// file and writting the filename into OutputFile if successful. If the @@ -296,12 +302,11 @@ private: bool initializeExecutionEnvironment(); }; -/// ParseInputFile - Given a bitcode or assembly input filename, parse and -/// return it, or return null if not possible. +/// Given a bitcode or assembly input filename, parse and return it, or return +/// null if not possible. /// -Module *ParseInputFile(const std::string &InputFilename, - LLVMContext& ctxt); - +std::unique_ptr<Module> parseInputFile(StringRef InputFilename, + LLVMContext &ctxt); /// getPassesString - Turn a list of passes into a string which indicates the /// command line options that must be passed to add the passes. diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp index 8bd61b3..bac948a 100644 --- a/tools/bugpoint/CrashDebugger.cpp +++ b/tools/bugpoint/CrashDebugger.cpp @@ -72,7 +72,7 @@ ReducePassList::doTest(std::vector<std::string> &Prefix, OrigProgram = BD.Program; - BD.Program = ParseInputFile(PrefixOutput, BD.getContext()); + BD.Program = parseInputFile(PrefixOutput, BD.getContext()).release(); if (BD.Program == nullptr) { errs() << BD.getToolName() << ": Error reading bitcode file '" << PrefixOutput << "'!\n"; @@ -312,22 +312,21 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) { // have to take. std::vector<std::pair<std::string, std::string> > BlockInfo; - for (SmallPtrSet<BasicBlock*, 8>::iterator I = Blocks.begin(), - E = Blocks.end(); I != E; ++I) - BlockInfo.push_back(std::make_pair((*I)->getParent()->getName(), - (*I)->getName())); + for (BasicBlock *BB : Blocks) + BlockInfo.push_back(std::make_pair(BB->getParent()->getName(), + BB->getName())); // Now run the CFG simplify pass on the function... std::vector<std::string> Passes; Passes.push_back("simplifycfg"); Passes.push_back("verify"); - Module *New = BD.runPassesOn(M, Passes); + std::unique_ptr<Module> New = BD.runPassesOn(M, Passes); delete M; if (!New) { errs() << "simplifycfg failed!\n"; exit(1); } - M = New; + M = New.release(); // Try running on the hacked up program... if (TestFn(BD, M)) { @@ -420,9 +419,8 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*> // Make sure to use instruction pointers that point into the now-current // module, and that they don't include any deleted blocks. Insts.clear(); - for (SmallPtrSet<Instruction*, 64>::const_iterator I = Instructions.begin(), - E = Instructions.end(); I != E; ++I) - Insts.push_back(*I); + for (Instruction *Inst : Instructions) + Insts.push_back(Inst); return true; } delete M; // It didn't crash, try something else. @@ -578,20 +576,17 @@ static bool DebugACrash(BugDriver &BD, continue; outs() << "Checking instruction: " << *I; - Module *M = BD.deleteInstructionFromProgram(I, Simplification); + std::unique_ptr<Module> M = + BD.deleteInstructionFromProgram(I, Simplification); // Find out if the pass still crashes on this pass... - if (TestFn(BD, M)) { + if (TestFn(BD, M.get())) { // Yup, it does, we delete the old module, and continue trying // to reduce the testcase... - BD.setNewProgram(M); + BD.setNewProgram(M.release()); InstructionsToSkipBeforeDeleting = CurInstructionNum; goto TryAgain; // I wish I had a multi-level break here! } - - // This pass didn't crash without this instruction, try the next - // one. - delete M; } } @@ -607,7 +602,7 @@ ExitLoops: if (!BugpointIsInterrupted) { outs() << "\n*** Attempting to perform final cleanups: "; Module *M = CloneModule(BD.getProgram()); - M = BD.performFinalCleanups(M, true); + M = BD.performFinalCleanups(M, true).release(); // Find out if the pass still crashes on the cleaned up program... if (TestFn(BD, M)) { diff --git a/tools/bugpoint/ExtractFunction.cpp b/tools/bugpoint/ExtractFunction.cpp index 4fb6856..34fe53c 100644 --- a/tools/bugpoint/ExtractFunction.cpp +++ b/tools/bugpoint/ExtractFunction.cpp @@ -82,13 +82,9 @@ namespace { } } // end anonymous namespace -/// deleteInstructionFromProgram - This method clones the current Program and -/// deletes the specified instruction from the cloned module. It then runs a -/// series of cleanup passes (ADCE and SimplifyCFG) to eliminate any code which -/// depends on the value. The modified module is then returned. -/// -Module *BugDriver::deleteInstructionFromProgram(const Instruction *I, - unsigned Simplification) { +std::unique_ptr<Module> +BugDriver::deleteInstructionFromProgram(const Instruction *I, + unsigned Simplification) { // FIXME, use vmap? Module *Clone = CloneModule(Program); @@ -123,7 +119,7 @@ Module *BugDriver::deleteInstructionFromProgram(const Instruction *I, Passes.push_back("simplifycfg"); // Delete dead control flow Passes.push_back("verify"); - Module *New = runPassesOn(Clone, Passes); + std::unique_ptr<Module> New = runPassesOn(Clone, Passes); delete Clone; if (!New) { errs() << "Instruction removal failed. Sorry. :( Please report a bug!\n"; @@ -132,11 +128,8 @@ Module *BugDriver::deleteInstructionFromProgram(const Instruction *I, return New; } -/// performFinalCleanups - This method clones the current Program and performs -/// a series of cleanups intended to get rid of extra cruft on the module -/// before handing it to the user. -/// -Module *BugDriver::performFinalCleanups(Module *M, bool MayModifySemantics) { +std::unique_ptr<Module> +BugDriver::performFinalCleanups(Module *M, bool MayModifySemantics) { // Make all functions external, so GlobalDCE doesn't delete them... for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) I->setLinkage(GlobalValue::ExternalLinkage); @@ -149,24 +142,20 @@ Module *BugDriver::performFinalCleanups(Module *M, bool MayModifySemantics) { else CleanupPasses.push_back("deadargelim"); - Module *New = runPassesOn(M, CleanupPasses); + std::unique_ptr<Module> New = runPassesOn(M, CleanupPasses); if (!New) { errs() << "Final cleanups failed. Sorry. :( Please report a bug!\n"; - return M; + return nullptr; } delete M; return New; } - -/// ExtractLoop - Given a module, extract up to one loop from it into a new -/// function. This returns null if there are no extractable loops in the -/// program or if the loop extractor crashes. -Module *BugDriver::ExtractLoop(Module *M) { +std::unique_ptr<Module> BugDriver::extractLoop(Module *M) { std::vector<std::string> LoopExtractPasses; LoopExtractPasses.push_back("loop-extract-single"); - Module *NewM = runPassesOn(M, LoopExtractPasses); + std::unique_ptr<Module> NewM = runPassesOn(M, LoopExtractPasses); if (!NewM) { outs() << "*** Loop extraction failed: "; EmitProgressBitcode(M, "loopextraction", true); @@ -179,7 +168,6 @@ Module *BugDriver::ExtractLoop(Module *M) { // to avoid taking forever. static unsigned NumExtracted = 32; if (M->size() == NewM->size() || --NumExtracted == 0) { - delete NewM; return nullptr; } else { assert(M->size() < NewM->size() && "Loop extract removed functions?"); @@ -356,14 +344,9 @@ llvm::SplitFunctionsOutOfModule(Module *M, // Basic Block Extraction Code //===----------------------------------------------------------------------===// -/// ExtractMappedBlocksFromModule - Extract all but the specified basic blocks -/// into their own functions. The only detail is that M is actually a module -/// cloned from the one the BBs are in, so some mapping needs to be performed. -/// If this operation fails for some reason (ie the implementation is buggy), -/// this function should return null, otherwise it returns a new Module. -Module *BugDriver::ExtractMappedBlocksFromModule(const - std::vector<BasicBlock*> &BBs, - Module *M) { +std::unique_ptr<Module> +BugDriver::extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs, + Module *M) { SmallString<128> Filename; int FD; std::error_code EC = sys::fs::createUniqueFile( @@ -401,7 +384,7 @@ Module *BugDriver::ExtractMappedBlocksFromModule(const std::vector<std::string> PI; PI.push_back("extract-blocks"); - Module *Ret = runPassesOn(M, PI, false, 1, &ExtraArg); + std::unique_ptr<Module> Ret = runPassesOn(M, PI, false, 1, &ExtraArg); sys::fs::remove(Filename.c_str()); diff --git a/tools/bugpoint/ListReducer.h b/tools/bugpoint/ListReducer.h index 8083e2d..a0bb570 100644 --- a/tools/bugpoint/ListReducer.h +++ b/tools/bugpoint/ListReducer.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef BUGPOINT_LIST_REDUCER_H -#define BUGPOINT_LIST_REDUCER_H +#ifndef LLVM_TOOLS_BUGPOINT_LISTREDUCER_H +#define LLVM_TOOLS_BUGPOINT_LISTREDUCER_H #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" diff --git a/tools/bugpoint/Miscompilation.cpp b/tools/bugpoint/Miscompilation.cpp index 3f1f84e..8cb4583 100644 --- a/tools/bugpoint/Miscompilation.cpp +++ b/tools/bugpoint/Miscompilation.cpp @@ -128,8 +128,8 @@ ReduceMiscompilingPasses::doTest(std::vector<std::string> &Prefix, // Ok, so now we know that the prefix passes work, try running the suffix // passes on the result of the prefix passes. // - std::unique_ptr<Module> PrefixOutput( - ParseInputFile(BitcodeResult, BD.getContext())); + std::unique_ptr<Module> PrefixOutput = + parseInputFile(BitcodeResult, BD.getContext()); if (!PrefixOutput) { errs() << BD.getToolName() << ": Error reading bitcode file '" << BitcodeResult << "'!\n"; @@ -218,16 +218,12 @@ static Module *TestMergedProgram(const BugDriver &BD, Module *M1, Module *M2, bool DeleteInputs, std::string &Error, bool &Broken) { // Link the two portions of the program back to together. - std::string ErrorMsg; if (!DeleteInputs) { M1 = CloneModule(M1); M2 = CloneModule(M2); } - if (Linker::LinkModules(M1, M2, Linker::DestroySource, &ErrorMsg)) { - errs() << BD.getToolName() << ": Error linking modules together:" - << ErrorMsg << '\n'; + if (Linker::LinkModules(M1, M2)) exit(1); - } delete M2; // We are done with this module. // Execute the program. @@ -316,7 +312,7 @@ static bool ExtractLoops(BugDriver &BD, Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, MiscompiledFunctions, VMap); - Module *ToOptimizeLoopExtracted = BD.ExtractLoop(ToOptimize); + Module *ToOptimizeLoopExtracted = BD.extractLoop(ToOptimize).release(); if (!ToOptimizeLoopExtracted) { // If the loop extractor crashed or if there were no extractible loops, // then this chapter of our odyssey is over with. @@ -334,8 +330,8 @@ static bool ExtractLoops(BugDriver &BD, // extraction. AbstractInterpreter *AI = BD.switchToSafeInterpreter(); bool Failure; - Module *New = TestMergedProgram(BD, ToOptimizeLoopExtracted, ToNotOptimize, - false, Error, Failure); + Module *New = TestMergedProgram(BD, ToOptimizeLoopExtracted, + ToNotOptimize, false, Error, Failure); if (!New) return false; @@ -364,7 +360,6 @@ static bool ExtractLoops(BugDriver &BD, << OutputPrefix << "-loop-extract-fail-*.bc files.\n"; delete ToOptimize; delete ToNotOptimize; - delete ToOptimizeLoopExtracted; return MadeChange; } delete ToOptimize; @@ -397,13 +392,8 @@ static bool ExtractLoops(BugDriver &BD, F->getFunctionType())); } - std::string ErrorMsg; - if (Linker::LinkModules(ToNotOptimize, ToOptimizeLoopExtracted, - Linker::DestroySource, &ErrorMsg)){ - errs() << BD.getToolName() << ": Error linking modules together:" - << ErrorMsg << '\n'; + if (Linker::LinkModules(ToNotOptimize, ToOptimizeLoopExtracted)) exit(1); - } MiscompiledFunctions.clear(); for (unsigned i = 0, e = MisCompFunctions.size(); i != e; ++i) { @@ -431,13 +421,9 @@ static bool ExtractLoops(BugDriver &BD, // extraction both didn't break the program, and didn't mask the problem. // Replace the current program with the loop extracted version, and try to // extract another loop. - std::string ErrorMsg; - if (Linker::LinkModules(ToNotOptimize, ToOptimizeLoopExtracted, - Linker::DestroySource, &ErrorMsg)){ - errs() << BD.getToolName() << ": Error linking modules together:" - << ErrorMsg << '\n'; + if (Linker::LinkModules(ToNotOptimize, ToOptimizeLoopExtracted)) exit(1); - } + delete ToOptimizeLoopExtracted; // All of the Function*'s in the MiscompiledFunctions list are in the old @@ -533,11 +519,12 @@ bool ReduceMiscompiledBlocks::TestFuncs(const std::vector<BasicBlock*> &BBs, // Try the extraction. If it doesn't work, then the block extractor crashed // or something, in which case bugpoint can't chase down this possibility. - if (Module *New = BD.ExtractMappedBlocksFromModule(BBsOnClone, ToOptimize)) { + if (std::unique_ptr<Module> New = + BD.extractMappedBlocksFromModule(BBsOnClone, ToOptimize)) { delete ToOptimize; // Run the predicate, // note that the predicate will delete both input modules. - bool Ret = TestFn(BD, New, ToNotOptimize, Error); + bool Ret = TestFn(BD, New.get(), ToNotOptimize, Error); delete BD.swapProgramIn(Orig); return Ret; } @@ -591,7 +578,8 @@ static bool ExtractBlocks(BugDriver &BD, Module *ToExtract = SplitFunctionsOutOfModule(ProgClone, MiscompiledFunctions, VMap); - Module *Extracted = BD.ExtractMappedBlocksFromModule(Blocks, ToExtract); + std::unique_ptr<Module> Extracted = + BD.extractMappedBlocksFromModule(Blocks, ToExtract); if (!Extracted) { // Weird, extraction should have worked. errs() << "Nondeterministic problem extracting blocks??\n"; @@ -611,14 +599,8 @@ static bool ExtractBlocks(BugDriver &BD, MisCompFunctions.push_back(std::make_pair(I->getName(), I->getFunctionType())); - std::string ErrorMsg; - if (Linker::LinkModules(ProgClone, Extracted, Linker::DestroySource, - &ErrorMsg)) { - errs() << BD.getToolName() << ": Error linking modules together:" - << ErrorMsg << '\n'; + if (Linker::LinkModules(ProgClone, Extracted.get())) exit(1); - } - delete Extracted; // Set the new program and delete the old one. BD.setNewProgram(ProgClone); @@ -730,14 +712,15 @@ static bool TestOptimizer(BugDriver &BD, Module *Test, Module *Safe, // Run the optimization passes on ToOptimize, producing a transformed version // of the functions being tested. outs() << " Optimizing functions being tested: "; - Module *Optimized = BD.runPassesOn(Test, BD.getPassesToRun(), - /*AutoDebugCrashes*/true); + std::unique_ptr<Module> Optimized = BD.runPassesOn(Test, BD.getPassesToRun(), + /*AutoDebugCrashes*/ true); outs() << "done.\n"; delete Test; outs() << " Checking to see if the merged program executes correctly: "; bool Broken; - Module *New = TestMergedProgram(BD, Optimized, Safe, true, Error, Broken); + Module *New = + TestMergedProgram(BD, Optimized.get(), Safe, true, Error, Broken); if (New) { outs() << (Broken ? " nope.\n" : " yup.\n"); // Delete the original and set the new program. @@ -796,7 +779,7 @@ void BugDriver::debugMiscompilation(std::string *Error) { static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test, Module *Safe) { // Clean up the modules, removing extra cruft that we don't need anymore... - Test = BD.performFinalCleanups(Test); + Test = BD.performFinalCleanups(Test).release(); // If we are executing the JIT, we have several nasty issues to take care of. if (!BD.isExecutingJIT()) return; diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp index d452fd9..f197cc5 100644 --- a/tools/bugpoint/OptimizerDriver.cpp +++ b/tools/bugpoint/OptimizerDriver.cpp @@ -66,15 +66,15 @@ static bool writeProgramToFileAux(tool_output_file &Out, const Module *M) { bool BugDriver::writeProgramToFile(const std::string &Filename, int FD, const Module *M) const { - tool_output_file Out(Filename.c_str(), FD); + tool_output_file Out(Filename, FD); return writeProgramToFileAux(Out, M); } bool BugDriver::writeProgramToFile(const std::string &Filename, const Module *M) const { - std::string ErrInfo; - tool_output_file Out(Filename.c_str(), ErrInfo, sys::fs::F_None); - if (ErrInfo.empty()) + std::error_code EC; + tool_output_file Out(Filename, EC, sys::fs::F_None); + if (!EC) return writeProgramToFileAux(Out, M); return true; } @@ -149,7 +149,7 @@ bool BugDriver::runPasses(Module *Program, return 1; } - tool_output_file InFile(InputFilename.c_str(), InputFD); + tool_output_file InFile(InputFilename, InputFD); WriteBitcodeToFile(Program, InFile.os()); InFile.os().close(); @@ -159,12 +159,31 @@ bool BugDriver::runPasses(Module *Program, return 1; } - std::string tool = OptCmd.empty()? sys::FindProgramByName("opt") : OptCmd; + std::string tool = OptCmd; + if (OptCmd.empty()) { + if (ErrorOr<std::string> Path = sys::findProgramByName("opt")) + tool = *Path; + else + errs() << Path.getError().message() << "\n"; + } if (tool.empty()) { errs() << "Cannot find `opt' in PATH!\n"; return 1; } + std::string Prog; + if (UseValgrind) { + if (ErrorOr<std::string> Path = sys::findProgramByName("valgrind")) + Prog = *Path; + else + errs() << Path.getError().message() << "\n"; + } else + Prog = tool; + if (Prog.empty()) { + errs() << "Cannot find `valgrind' in PATH!\n"; + return 1; + } + // Ok, everything that could go wrong before running opt is done. InFile.keep(); @@ -204,12 +223,6 @@ bool BugDriver::runPasses(Module *Program, errs() << "\n"; ); - std::string Prog; - if (UseValgrind) - Prog = sys::FindProgramByName("valgrind"); - else - Prog = tool; - // Redirect stdout and stderr to nowhere if SilencePasses is given StringRef Nowhere; const StringRef *Redirects[3] = {nullptr, &Nowhere, &Nowhere}; @@ -247,13 +260,10 @@ bool BugDriver::runPasses(Module *Program, } -/// runPassesOn - Carefully run the specified set of pass on the specified -/// module, returning the transformed module on success, or a null pointer on -/// failure. -Module *BugDriver::runPassesOn(Module *M, - const std::vector<std::string> &Passes, - bool AutoDebugCrashes, unsigned NumExtraArgs, - const char * const *ExtraArgs) { +std::unique_ptr<Module> +BugDriver::runPassesOn(Module *M, const std::vector<std::string> &Passes, + bool AutoDebugCrashes, unsigned NumExtraArgs, + const char *const *ExtraArgs) { std::string BitcodeResult; if (runPasses(M, Passes, BitcodeResult, false/*delete*/, true/*quiet*/, NumExtraArgs, ExtraArgs)) { @@ -267,7 +277,7 @@ Module *BugDriver::runPassesOn(Module *M, return nullptr; } - Module *Ret = ParseInputFile(BitcodeResult, Context); + std::unique_ptr<Module> Ret = parseInputFile(BitcodeResult, Context); if (!Ret) { errs() << getToolName() << ": Error reading bitcode file '" << BitcodeResult << "'!\n"; diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp index 4a2401b..51091e2 100644 --- a/tools/bugpoint/ToolRunner.cpp +++ b/tools/bugpoint/ToolRunner.cpp @@ -141,13 +141,13 @@ static std::string ProcessFailure(StringRef ProgPath, const char** Args, // Rerun the compiler, capturing any error messages to print them. SmallString<128> ErrorFilename; - int ErrorFD; std::error_code EC = sys::fs::createTemporaryFile( - "bugpoint.program_error_messages", "", ErrorFD, ErrorFilename); + "bugpoint.program_error_messages", "", ErrorFilename); if (EC) { errs() << "Error making unique filename: " << EC.message() << "\n"; exit(1); } + RunProgramWithTimeout(ProgPath, Args, "", ErrorFilename.str(), ErrorFilename.str(), Timeout, MemoryLimit); // FIXME: check return code ? @@ -427,13 +427,14 @@ static void lexCommand(std::string &Message, const std::string &CommandLine, pos = CommandLine.find_first_of(delimiters, lastPos); } - CmdPath = sys::FindProgramByName(Command); - if (CmdPath.empty()) { + auto Path = sys::findProgramByName(Command); + if (!Path) { Message = std::string("Cannot find '") + Command + - "' in PATH!\n"; + "' in PATH: " + Path.getError().message() + "\n"; return; } + CmdPath = *Path; Message = "Found command in: " + CmdPath + "\n"; } @@ -907,16 +908,24 @@ int GCC::MakeSharedObject(const std::string &InputFile, FileType fileType, GCC *GCC::create(std::string &Message, const std::string &GCCBinary, const std::vector<std::string> *Args) { - std::string GCCPath = sys::FindProgramByName(GCCBinary); - if (GCCPath.empty()) { - Message = "Cannot find `"+ GCCBinary +"' in PATH!\n"; + auto GCCPath = sys::findProgramByName(GCCBinary); + if (!GCCPath) { + Message = "Cannot find `" + GCCBinary + "' in PATH: " + + GCCPath.getError().message() + "\n"; return nullptr; } std::string RemoteClientPath; - if (!RemoteClient.empty()) - RemoteClientPath = sys::FindProgramByName(RemoteClient); + if (!RemoteClient.empty()) { + auto Path = sys::findProgramByName(RemoteClient); + if (!Path) { + Message = "Cannot find `" + RemoteClient + "' in PATH: " + + Path.getError().message() + "\n"; + return nullptr; + } + RemoteClientPath = *Path; + } - Message = "Found gcc: " + GCCPath + "\n"; - return new GCC(GCCPath, RemoteClientPath, Args); + Message = "Found gcc: " + *GCCPath + "\n"; + return new GCC(*GCCPath, RemoteClientPath, Args); } diff --git a/tools/bugpoint/ToolRunner.h b/tools/bugpoint/ToolRunner.h index 6e7b95c..454724a 100644 --- a/tools/bugpoint/ToolRunner.h +++ b/tools/bugpoint/ToolRunner.h @@ -14,8 +14,8 @@ // //===----------------------------------------------------------------------===// -#ifndef BUGPOINT_TOOLRUNNER_H -#define BUGPOINT_TOOLRUNNER_H +#ifndef LLVM_TOOLS_BUGPOINT_TOOLRUNNER_H +#define LLVM_TOOLS_BUGPOINT_TOOLRUNNER_H #include "llvm/ADT/Triple.h" #include "llvm/Support/CommandLine.h" diff --git a/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp index c7dae0f..d0bade5 100644 --- a/tools/bugpoint/bugpoint.cpp +++ b/tools/bugpoint/bugpoint.cpp @@ -63,10 +63,6 @@ static cl::list<const PassInfo*, bool, PassNameParser> PassList(cl::desc("Passes available:"), cl::ZeroOrMore); static cl::opt<bool> -StandardCompileOpts("std-compile-opts", - cl::desc("Include the standard compile time optimizations")); - -static cl::opt<bool> StandardLinkOpts("std-link-opts", cl::desc("Include the standard link time optimizations")); @@ -170,17 +166,11 @@ int main(int argc, char **argv) { if (D.addSources(InputFilenames)) return 1; AddToDriver PM(D); - if (StandardCompileOpts) { - PassManagerBuilder Builder; - Builder.OptLevel = 3; - Builder.Inliner = createFunctionInliningPass(); - Builder.populateModulePassManager(PM); - } if (StandardLinkOpts) { PassManagerBuilder Builder; - Builder.populateLTOPassManager(PM, /*Internalize=*/true, - /*RunInliner=*/true); + Builder.Inliner = createFunctionInliningPass(); + Builder.populateLTOPassManager(PM); } if (OptLevelO1 || OptLevelO2 || OptLevelO3) { diff --git a/tools/gold/CMakeLists.txt b/tools/gold/CMakeLists.txt index 3864e15..3033010 100644 --- a/tools/gold/CMakeLists.txt +++ b/tools/gold/CMakeLists.txt @@ -16,7 +16,9 @@ else() set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} - LTO + Linker + BitWriter + IPO ) add_llvm_loadable_module(LLVMgold diff --git a/tools/gold/Makefile b/tools/gold/Makefile index 593d8ea..aa006b0 100644 --- a/tools/gold/Makefile +++ b/tools/gold/Makefile @@ -20,7 +20,7 @@ EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/gold.exports # early so we can set up LINK_COMPONENTS before including Makefile.rules include $(LEVEL)/Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) LTO +LINK_COMPONENTS := $(TARGETS_TO_BUILD) Linker BitWriter IPO # Because off_t is used in the public API, the largefile parts are required for # ABI compatibility. diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index b908510..cfda6d2 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -13,33 +13,35 @@ //===----------------------------------------------------------------------===// #include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H -#include "llvm-c/lto.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringSet.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/CommandFlags.h" -#include "llvm/LTO/LTOCodeGenerator.h" -#include "llvm/LTO/LTOModule.h" -#include "llvm/Support/Errno.h" -#include "llvm/Support/FileSystem.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Linker/Linker.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/Object/IRObjectFile.h" +#include "llvm/PassManager.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Program.h" +#include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" -#include "llvm/Support/ToolOutputFile.h" -#include <cerrno> -#include <cstdlib> -#include <cstring> +#include "llvm/Target/TargetLibraryInfo.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/GlobalStatus.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +#include "llvm/Transforms/Utils/ValueMapper.h" #include <list> #include <plugin-api.h> #include <system_error> #include <vector> -// Support Windows/MinGW crazyness. -#ifdef _WIN32 -# include <io.h> -# define lseek _lseek -# define read _read -#endif - #ifndef LDPO_PIE // FIXME: remove this declaration when we stop maintaining Ubuntu Quantal and // Precise and Debian Wheezy (binutils 2.23 is required) @@ -61,25 +63,24 @@ static ld_plugin_status discard_message(int level, const char *format, ...) { abort(); } -static ld_plugin_add_symbols add_symbols = NULL; -static ld_plugin_get_symbols get_symbols = NULL; -static ld_plugin_add_input_file add_input_file = NULL; -static ld_plugin_set_extra_library_path set_extra_library_path = NULL; -static ld_plugin_get_view get_view = NULL; +static ld_plugin_get_input_file get_input_file = nullptr; +static ld_plugin_release_input_file release_input_file = nullptr; +static ld_plugin_add_symbols add_symbols = nullptr; +static ld_plugin_get_symbols get_symbols = nullptr; +static ld_plugin_add_input_file add_input_file = nullptr; +static ld_plugin_set_extra_library_path set_extra_library_path = nullptr; +static ld_plugin_get_view get_view = nullptr; static ld_plugin_message message = discard_message; -static lto_codegen_model output_type = LTO_CODEGEN_PIC_MODEL_STATIC; +static Reloc::Model RelocationModel = Reloc::Default; static std::string output_name = ""; static std::list<claimed_file> Modules; static std::vector<std::string> Cleanup; -static LTOCodeGenerator *CodeGen = nullptr; -static StringSet<> CannotBeHidden; static llvm::TargetOptions TargetOpts; namespace options { - enum generate_bc { BC_NO, BC_ALSO, BC_ONLY }; + enum generate_bc { BC_NO, BC_ONLY, BC_SAVE_TEMPS }; static bool generate_api_file = false; static generate_bc generate_bc_file = BC_NO; - static std::string bc_path; static std::string obj_path; static std::string extra_library_path; static std::string triple; @@ -89,11 +90,11 @@ namespace options { // as plugin exclusive to pass to the code generator. // For example, "generate-api-file" and "as"options are for the plugin // use only and will not be passed. - static std::vector<std::string> extra; + static std::vector<const char *> extra; static void process_plugin_option(const char* opt_) { - if (opt_ == NULL) + if (opt_ == nullptr) return; llvm::StringRef opt = opt_; @@ -109,20 +110,16 @@ namespace options { obj_path = opt.substr(strlen("obj-path=")); } else if (opt == "emit-llvm") { generate_bc_file = BC_ONLY; - } else if (opt == "also-emit-llvm") { - generate_bc_file = BC_ALSO; - } else if (opt.startswith("also-emit-llvm=")) { - llvm::StringRef path = opt.substr(strlen("also-emit-llvm=")); - generate_bc_file = BC_ALSO; - if (!bc_path.empty()) { - (*message)(LDPL_WARNING, "Path to the output IL file specified twice. " - "Discarding %s", opt_); - } else { - bc_path = path; - } + } else if (opt == "save-temps") { + generate_bc_file = BC_SAVE_TEMPS; } else { // Save this option to pass to the code generator. - extra.push_back(opt); + // ParseCommandLineOptions() expects argv[0] to be program name. Lazily + // add that. + if (extra.empty()) + extra.push_back("LLVMgold"); + + extra.push_back(opt_); } } } @@ -159,14 +156,13 @@ ld_plugin_status onload(ld_plugin_tv *tv) { case LDPO_REL: // .o case LDPO_DYN: // .so case LDPO_PIE: // position independent executable - output_type = LTO_CODEGEN_PIC_MODEL_DYNAMIC; + RelocationModel = Reloc::PIC_; break; case LDPO_EXEC: // .exe - output_type = LTO_CODEGEN_PIC_MODEL_STATIC; + RelocationModel = Reloc::Static; break; default: - (*message)(LDPL_ERROR, "Unknown output file type %d", - tv->tv_u.tv_val); + message(LDPL_ERROR, "Unknown output file type %d", tv->tv_u.tv_val); return LDPS_ERR; } break; @@ -177,7 +173,7 @@ ld_plugin_status onload(ld_plugin_tv *tv) { ld_plugin_register_claim_file callback; callback = tv->tv_u.tv_register_claim_file; - if ((*callback)(claim_file_hook) != LDPS_OK) + if (callback(claim_file_hook) != LDPS_OK) return LDPS_ERR; registeredClaimFile = true; @@ -186,7 +182,7 @@ ld_plugin_status onload(ld_plugin_tv *tv) { ld_plugin_register_all_symbols_read callback; callback = tv->tv_u.tv_register_all_symbols_read; - if ((*callback)(all_symbols_read_hook) != LDPS_OK) + if (callback(all_symbols_read_hook) != LDPS_OK) return LDPS_ERR; RegisteredAllSymbolsRead = true; @@ -195,9 +191,15 @@ ld_plugin_status onload(ld_plugin_tv *tv) { ld_plugin_register_cleanup callback; callback = tv->tv_u.tv_register_cleanup; - if ((*callback)(cleanup_hook) != LDPS_OK) + if (callback(cleanup_hook) != LDPS_OK) return LDPS_ERR; } break; + case LDPT_GET_INPUT_FILE: + get_input_file = tv->tv_u.tv_get_input_file; + break; + case LDPT_RELEASE_INPUT_FILE: + release_input_file = tv->tv_u.tv_release_input_file; + break; case LDPT_ADD_SYMBOLS: add_symbols = tv->tv_u.tv_add_symbols; break; @@ -222,56 +224,50 @@ ld_plugin_status onload(ld_plugin_tv *tv) { } if (!registeredClaimFile) { - (*message)(LDPL_ERROR, "register_claim_file not passed to LLVMgold."); + message(LDPL_ERROR, "register_claim_file not passed to LLVMgold."); return LDPS_ERR; } if (!add_symbols) { - (*message)(LDPL_ERROR, "add_symbols not passed to LLVMgold."); + message(LDPL_ERROR, "add_symbols not passed to LLVMgold."); return LDPS_ERR; } if (!RegisteredAllSymbolsRead) return LDPS_OK; - CodeGen = new LTOCodeGenerator(); - - // Pass through extra options to the code generator. - if (!options::extra.empty()) { - for (std::vector<std::string>::iterator it = options::extra.begin(); - it != options::extra.end(); ++it) { - CodeGen->setCodeGenDebugOptions((*it).c_str()); - } + if (!get_input_file) { + message(LDPL_ERROR, "get_input_file not passed to LLVMgold."); + return LDPS_ERR; } - - CodeGen->parseCodeGenDebugOptions(); - if (MAttrs.size()) { - std::string Attrs; - for (unsigned I = 0; I < MAttrs.size(); ++I) { - if (I > 0) - Attrs.append(","); - Attrs.append(MAttrs[I]); - } - CodeGen->setAttr(Attrs.c_str()); + if (!release_input_file) { + message(LDPL_ERROR, "relesase_input_file not passed to LLVMgold."); + return LDPS_ERR; } - TargetOpts = InitTargetOptionsFromCodeGenFlags(); - CodeGen->setTargetOptions(TargetOpts); - return LDPS_OK; } +static const GlobalObject *getBaseObject(const GlobalValue &GV) { + if (auto *GA = dyn_cast<GlobalAlias>(&GV)) + return GA->getBaseObject(); + return cast<GlobalObject>(&GV); +} + /// Called by gold to see whether this file is one that our plugin can handle. /// We'll try to open it and register all the symbols with add_symbol if /// possible. static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, int *claimed) { - const void *view; - std::unique_ptr<MemoryBuffer> buffer; + LLVMContext Context; + MemoryBufferRef BufferRef; + std::unique_ptr<MemoryBuffer> Buffer; if (get_view) { + const void *view; if (get_view(file->handle, &view) != LDPS_OK) { - (*message)(LDPL_ERROR, "Failed to get a view of %s", file->name); + message(LDPL_ERROR, "Failed to get a view of %s", file->name); return LDPS_ERR; } + BufferRef = MemoryBufferRef(StringRef((const char *)view, file->filesize), ""); } else { int64_t offset = 0; // Gold has found what might be IR part-way inside of a file, such as @@ -283,225 +279,567 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, MemoryBuffer::getOpenFileSlice(file->fd, file->name, file->filesize, offset); if (std::error_code EC = BufferOrErr.getError()) { - (*message)(LDPL_ERROR, EC.message().c_str()); + message(LDPL_ERROR, EC.message().c_str()); return LDPS_ERR; } - buffer = std::move(BufferOrErr.get()); - view = buffer->getBufferStart(); + Buffer = std::move(BufferOrErr.get()); + BufferRef = Buffer->getMemBufferRef(); } - if (!LTOModule::isBitcodeFile(view, file->filesize)) + ErrorOr<std::unique_ptr<object::IRObjectFile>> ObjOrErr = + object::IRObjectFile::createIRObjectFile(BufferRef, Context); + std::error_code EC = ObjOrErr.getError(); + if (EC == BitcodeError::InvalidBitcodeSignature || + EC == object::object_error::invalid_file_type || + EC == object::object_error::bitcode_section_not_found) return LDPS_OK; - std::string Error; - LTOModule *M = - LTOModule::createFromBuffer(view, file->filesize, TargetOpts, Error); - if (!M) { - (*message)(LDPL_ERROR, - "LLVM gold plugin has failed to create LTO module: %s", - Error.c_str()); - return LDPS_OK; + *claimed = 1; + + if (EC) { + message(LDPL_ERROR, "LLVM gold plugin has failed to create LTO module: %s", + EC.message().c_str()); + return LDPS_ERR; } + std::unique_ptr<object::IRObjectFile> Obj = std::move(*ObjOrErr); - *claimed = 1; Modules.resize(Modules.size() + 1); claimed_file &cf = Modules.back(); - if (!options::triple.empty()) - M->setTargetTriple(options::triple.c_str()); - cf.handle = file->handle; - unsigned sym_count = M->getSymbolCount(); - cf.syms.reserve(sym_count); - for (unsigned i = 0; i != sym_count; ++i) { - lto_symbol_attributes attrs = M->getSymbolAttributes(i); - if ((attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL) + for (auto &Sym : Obj->symbols()) { + uint32_t Symflags = Sym.getFlags(); + if (!(Symflags & object::BasicSymbolRef::SF_Global)) + continue; + + if (Symflags & object::BasicSymbolRef::SF_FormatSpecific) continue; cf.syms.push_back(ld_plugin_symbol()); ld_plugin_symbol &sym = cf.syms.back(); - sym.name = strdup(M->getSymbolName(i)); - sym.version = NULL; - - int scope = attrs & LTO_SYMBOL_SCOPE_MASK; - bool CanBeHidden = scope == LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN; - if (!CanBeHidden) - CannotBeHidden.insert(sym.name); - switch (scope) { - case LTO_SYMBOL_SCOPE_HIDDEN: + sym.version = nullptr; + + SmallString<64> Name; + { + raw_svector_ostream OS(Name); + Sym.printName(OS); + } + sym.name = strdup(Name.c_str()); + + const GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl()); + + sym.visibility = LDPV_DEFAULT; + if (GV) { + switch (GV->getVisibility()) { + case GlobalValue::DefaultVisibility: + sym.visibility = LDPV_DEFAULT; + break; + case GlobalValue::HiddenVisibility: sym.visibility = LDPV_HIDDEN; break; - case LTO_SYMBOL_SCOPE_PROTECTED: + case GlobalValue::ProtectedVisibility: sym.visibility = LDPV_PROTECTED; break; - case 0: // extern - case LTO_SYMBOL_SCOPE_DEFAULT: - case LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN: - sym.visibility = LDPV_DEFAULT; - break; - default: - (*message)(LDPL_ERROR, "Unknown scope attribute: %d", scope); - return LDPS_ERR; + } } - int definition = attrs & LTO_SYMBOL_DEFINITION_MASK; - sym.comdat_key = NULL; - switch (definition) { - case LTO_SYMBOL_DEFINITION_REGULAR: - sym.def = LDPK_DEF; - break; - case LTO_SYMBOL_DEFINITION_UNDEFINED: - sym.def = LDPK_UNDEF; - break; - case LTO_SYMBOL_DEFINITION_TENTATIVE: - sym.def = LDPK_COMMON; - break; - case LTO_SYMBOL_DEFINITION_WEAK: - sym.comdat_key = sym.name; - sym.def = LDPK_WEAKDEF; - break; - case LTO_SYMBOL_DEFINITION_WEAKUNDEF: + if (Symflags & object::BasicSymbolRef::SF_Undefined) { + sym.def = LDPK_UNDEF; + if (GV && GV->hasExternalWeakLinkage()) sym.def = LDPK_WEAKUNDEF; - break; - default: - (*message)(LDPL_ERROR, "Unknown definition attribute: %d", definition); - return LDPS_ERR; + } else { + sym.def = LDPK_DEF; + if (GV) { + assert(!GV->hasExternalWeakLinkage() && + !GV->hasAvailableExternallyLinkage() && "Not a declaration!"); + if (GV->hasCommonLinkage()) + sym.def = LDPK_COMMON; + else if (GV->isWeakForLinker()) + sym.def = LDPK_WEAKDEF; + } } sym.size = 0; + sym.comdat_key = nullptr; + if (GV) { + const GlobalObject *Base = getBaseObject(*GV); + if (!Base) + message(LDPL_FATAL, "Unable to determine comdat of alias!"); + const Comdat *C = Base->getComdat(); + if (C) + sym.comdat_key = strdup(C->getName().str().c_str()); + else if (Base->hasWeakLinkage() || Base->hasLinkOnceLinkage()) + sym.comdat_key = strdup(sym.name); + } sym.resolution = LDPR_UNKNOWN; } - cf.syms.reserve(cf.syms.size()); - if (!cf.syms.empty()) { - if ((*add_symbols)(cf.handle, cf.syms.size(), &cf.syms[0]) != LDPS_OK) { - (*message)(LDPL_ERROR, "Unable to add symbols!"); + if (add_symbols(cf.handle, cf.syms.size(), &cf.syms[0]) != LDPS_OK) { + message(LDPL_ERROR, "Unable to add symbols!"); return LDPS_ERR; } } - if (CodeGen) { - std::string Error; - if (!CodeGen->addModule(M, Error)) { - (*message)(LDPL_ERROR, "Error linking module: %s", Error.c_str()); - return LDPS_ERR; - } + return LDPS_OK; +} + +static void keepGlobalValue(GlobalValue &GV, + std::vector<GlobalAlias *> &KeptAliases) { + assert(!GV.hasLocalLinkage()); + + if (auto *GA = dyn_cast<GlobalAlias>(&GV)) + KeptAliases.push_back(GA); + + switch (GV.getLinkage()) { + default: + break; + case GlobalValue::LinkOnceAnyLinkage: + GV.setLinkage(GlobalValue::WeakAnyLinkage); + break; + case GlobalValue::LinkOnceODRLinkage: + GV.setLinkage(GlobalValue::WeakODRLinkage); + break; } - delete M; + assert(!GV.isDiscardableIfUnused()); +} - return LDPS_OK; +static void internalize(GlobalValue &GV) { + if (GV.isDeclarationForLinker()) + return; // We get here if there is a matching asm definition. + if (!GV.hasLocalLinkage()) + GV.setLinkage(GlobalValue::InternalLinkage); } -static bool mustPreserve(const claimed_file &F, int i) { - if (F.syms[i].resolution == LDPR_PREVAILING_DEF) - return true; - if (F.syms[i].resolution == LDPR_PREVAILING_DEF_IRONLY_EXP) - return CannotBeHidden.count(F.syms[i].name); - return false; +static void drop(GlobalValue &GV) { + if (auto *F = dyn_cast<Function>(&GV)) { + F->deleteBody(); + F->setComdat(nullptr); // Should deleteBody do this? + return; + } + + if (auto *Var = dyn_cast<GlobalVariable>(&GV)) { + Var->setInitializer(nullptr); + Var->setLinkage( + GlobalValue::ExternalLinkage); // Should setInitializer do this? + Var->setComdat(nullptr); // and this? + return; + } + + auto &Alias = cast<GlobalAlias>(GV); + Module &M = *Alias.getParent(); + PointerType &Ty = *cast<PointerType>(Alias.getType()); + GlobalValue::LinkageTypes L = Alias.getLinkage(); + auto *Var = + new GlobalVariable(M, Ty.getElementType(), /*isConstant*/ false, L, + /*Initializer*/ nullptr); + Var->takeName(&Alias); + Alias.replaceAllUsesWith(Var); + Alias.eraseFromParent(); } -/// all_symbols_read_hook - gold informs us that all symbols have been read. -/// At this point, we use get_symbols to see if any of our definitions have -/// been overridden by a native object file. Then, perform optimization and -/// codegen. -static ld_plugin_status all_symbols_read_hook(void) { - // FIXME: raw_fd_ostream should be able to represent an unopened file. - std::unique_ptr<raw_fd_ostream> api_file; +static const char *getResolutionName(ld_plugin_symbol_resolution R) { + switch (R) { + case LDPR_UNKNOWN: + return "UNKNOWN"; + case LDPR_UNDEF: + return "UNDEF"; + case LDPR_PREVAILING_DEF: + return "PREVAILING_DEF"; + case LDPR_PREVAILING_DEF_IRONLY: + return "PREVAILING_DEF_IRONLY"; + case LDPR_PREEMPTED_REG: + return "PREEMPTED_REG"; + case LDPR_PREEMPTED_IR: + return "PREEMPTED_IR"; + case LDPR_RESOLVED_IR: + return "RESOLVED_IR"; + case LDPR_RESOLVED_EXEC: + return "RESOLVED_EXEC"; + case LDPR_RESOLVED_DYN: + return "RESOLVED_DYN"; + case LDPR_PREVAILING_DEF_IRONLY_EXP: + return "PREVAILING_DEF_IRONLY_EXP"; + } + llvm_unreachable("Unknown resolution"); +} - assert(CodeGen); +static GlobalObject *makeInternalReplacement(GlobalObject *GO) { + Module *M = GO->getParent(); + GlobalObject *Ret; + if (auto *F = dyn_cast<Function>(GO)) { + if (F->materialize()) + message(LDPL_FATAL, "LLVM gold plugin has failed to read a function"); + + auto *NewF = Function::Create(F->getFunctionType(), F->getLinkage(), + F->getName(), M); + + ValueToValueMapTy VM; + Function::arg_iterator NewI = NewF->arg_begin(); + for (auto &Arg : F->args()) { + NewI->setName(Arg.getName()); + VM[&Arg] = NewI; + ++NewI; + } - if (options::generate_api_file) { - std::string Error; - api_file.reset(new raw_fd_ostream("apifile.txt", Error, sys::fs::F_None)); - if (!Error.empty()) - (*message)(LDPL_FATAL, "Unable to open apifile.txt for writing: %s", - Error.c_str()); + NewF->getBasicBlockList().splice(NewF->end(), F->getBasicBlockList()); + for (auto &BB : *NewF) { + for (auto &Inst : BB) + RemapInstruction(&Inst, VM, RF_IgnoreMissingEntries); + } + + Ret = NewF; + F->deleteBody(); + } else { + auto *Var = cast<GlobalVariable>(GO); + Ret = new GlobalVariable( + *M, Var->getType()->getElementType(), Var->isConstant(), + Var->getLinkage(), Var->getInitializer(), Var->getName(), + nullptr, Var->getThreadLocalMode(), Var->getType()->getAddressSpace(), + Var->isExternallyInitialized()); + Var->setInitializer(nullptr); } + Ret->copyAttributesFrom(GO); + Ret->setLinkage(GlobalValue::InternalLinkage); + Ret->setComdat(GO->getComdat()); + + return Ret; +} + +namespace { +class LocalValueMaterializer : public ValueMaterializer { + DenseSet<GlobalValue *> &Dropped; + +public: + LocalValueMaterializer(DenseSet<GlobalValue *> &Dropped) : Dropped(Dropped) {} + Value *materializeValueFor(Value *V) override; +}; +} + +Value *LocalValueMaterializer::materializeValueFor(Value *V) { + auto *GV = dyn_cast<GlobalValue>(V); + if (!GV) + return nullptr; + if (!Dropped.count(GV)) + return nullptr; + assert(!isa<GlobalAlias>(GV) && "Found alias point to weak alias."); + return makeInternalReplacement(cast<GlobalObject>(GV)); +} + +static Constant *mapConstantToLocalCopy(Constant *C, ValueToValueMapTy &VM, + LocalValueMaterializer *Materializer) { + return MapValue(C, VM, RF_IgnoreMissingEntries, nullptr, Materializer); +} + +static std::unique_ptr<Module> +getModuleForFile(LLVMContext &Context, claimed_file &F, raw_fd_ostream *ApiFile, + StringSet<> &Internalize, StringSet<> &Maybe) { + ld_plugin_input_file File; + if (get_input_file(F.handle, &File) != LDPS_OK) + message(LDPL_FATAL, "Failed to get file information"); + + if (get_symbols(F.handle, F.syms.size(), &F.syms[0]) != LDPS_OK) + message(LDPL_FATAL, "Failed to get symbol information"); - for (std::list<claimed_file>::iterator I = Modules.begin(), - E = Modules.end(); I != E; ++I) { - if (I->syms.empty()) + const void *View; + if (get_view(F.handle, &View) != LDPS_OK) + message(LDPL_FATAL, "Failed to get a view of file"); + + llvm::ErrorOr<MemoryBufferRef> MBOrErr = + object::IRObjectFile::findBitcodeInMemBuffer( + MemoryBufferRef(StringRef((const char *)View, File.filesize), "")); + if (std::error_code EC = MBOrErr.getError()) + message(LDPL_FATAL, "Could not read bitcode from file : %s", + EC.message().c_str()); + + std::unique_ptr<MemoryBuffer> Buffer = + MemoryBuffer::getMemBuffer(MBOrErr->getBuffer(), "", false); + + if (release_input_file(F.handle) != LDPS_OK) + message(LDPL_FATAL, "Failed to release file information"); + + ErrorOr<Module *> MOrErr = getLazyBitcodeModule(std::move(Buffer), Context); + + if (std::error_code EC = MOrErr.getError()) + message(LDPL_FATAL, "Could not read bitcode from file : %s", + EC.message().c_str()); + + std::unique_ptr<Module> M(MOrErr.get()); + + SmallPtrSet<GlobalValue *, 8> Used; + collectUsedGlobalVariables(*M, Used, /*CompilerUsed*/ false); + + DenseSet<GlobalValue *> Drop; + std::vector<GlobalAlias *> KeptAliases; + for (ld_plugin_symbol &Sym : F.syms) { + ld_plugin_symbol_resolution Resolution = + (ld_plugin_symbol_resolution)Sym.resolution; + + if (options::generate_api_file) + *ApiFile << Sym.name << ' ' << getResolutionName(Resolution) << '\n'; + + GlobalValue *GV = M->getNamedValue(Sym.name); + if (!GV) + continue; // Asm symbol. + + if (Resolution != LDPR_PREVAILING_DEF_IRONLY && GV->hasCommonLinkage()) { + // Common linkage is special. There is no single symbol that wins the + // resolution. Instead we have to collect the maximum alignment and size. + // The IR linker does that for us if we just pass it every common GV. + // We still have to keep track of LDPR_PREVAILING_DEF_IRONLY so we + // internalize once the IR linker has done its job. continue; - (*get_symbols)(I->handle, I->syms.size(), &I->syms[0]); - for (unsigned i = 0, e = I->syms.size(); i != e; i++) { - if (mustPreserve(*I, i)) { - CodeGen->addMustPreserveSymbol(I->syms[i].name); + } - if (options::generate_api_file) - (*api_file) << I->syms[i].name << "\n"; + switch (Resolution) { + case LDPR_UNKNOWN: + llvm_unreachable("Unexpected resolution"); + + case LDPR_RESOLVED_IR: + case LDPR_RESOLVED_EXEC: + case LDPR_RESOLVED_DYN: + case LDPR_UNDEF: + assert(GV->isDeclarationForLinker()); + break; + + case LDPR_PREVAILING_DEF_IRONLY: { + keepGlobalValue(*GV, KeptAliases); + if (!Used.count(GV)) { + // Since we use the regular lib/Linker, we cannot just internalize GV + // now or it will not be copied to the merged module. Instead we force + // it to be copied and then internalize it. + Internalize.insert(Sym.name); } + break; + } + + case LDPR_PREVAILING_DEF: + keepGlobalValue(*GV, KeptAliases); + break; + + case LDPR_PREEMPTED_IR: + // Gold might have selected a linkonce_odr and preempted a weak_odr. + // In that case we have to make sure we don't end up internalizing it. + if (!GV->isDiscardableIfUnused()) + Maybe.erase(Sym.name); + + // fall-through + case LDPR_PREEMPTED_REG: + Drop.insert(GV); + break; + + case LDPR_PREVAILING_DEF_IRONLY_EXP: { + // We can only check for address uses after we merge the modules. The + // reason is that this GV might have a copy in another module + // and in that module the address might be significant, but that + // copy will be LDPR_PREEMPTED_IR. + if (GV->hasLinkOnceODRLinkage()) + Maybe.insert(Sym.name); + keepGlobalValue(*GV, KeptAliases); + break; + } } + + free(Sym.name); + free(Sym.comdat_key); + Sym.name = nullptr; + Sym.comdat_key = nullptr; } - CodeGen->setCodePICModel(output_type); - CodeGen->setDebugInfo(LTO_DEBUG_MODEL_DWARF); - if (!options::mcpu.empty()) - CodeGen->setCpu(options::mcpu.c_str()); + ValueToValueMapTy VM; + LocalValueMaterializer Materializer(Drop); + for (GlobalAlias *GA : KeptAliases) { + // Gold told us to keep GA. It is possible that a GV usied in the aliasee + // expression is being dropped. If that is the case, that GV must be copied. + Constant *Aliasee = GA->getAliasee(); + Constant *Replacement = mapConstantToLocalCopy(Aliasee, VM, &Materializer); + if (Aliasee != Replacement) + GA->setAliasee(Replacement); + } - if (options::generate_bc_file != options::BC_NO) { - std::string path; - if (options::generate_bc_file == options::BC_ONLY) - path = output_name; - else if (!options::bc_path.empty()) - path = options::bc_path; - else - path = output_name + ".bc"; - std::string Error; - if (!CodeGen->writeMergedModules(path.c_str(), Error)) - (*message)(LDPL_FATAL, "Failed to write the output file."); - if (options::generate_bc_file == options::BC_ONLY) { - delete CodeGen; - exit(0); - } + for (auto *GV : Drop) + drop(*GV); + + return M; +} + +static void runLTOPasses(Module &M, TargetMachine &TM) { + PassManager passes; + PassManagerBuilder PMB; + PMB.LibraryInfo = new TargetLibraryInfo(Triple(TM.getTargetTriple())); + PMB.Inliner = createFunctionInliningPass(); + PMB.VerifyInput = true; + PMB.VerifyOutput = true; + PMB.LoopVectorize = true; + PMB.SLPVectorize = true; + PMB.populateLTOPassManager(passes, &TM); + passes.run(M); +} + +static void saveBCFile(StringRef Path, Module &M) { + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None); + if (EC) + message(LDPL_FATAL, "Failed to write the output file."); + WriteBitcodeToFile(&M, OS); +} + +static void codegen(Module &M) { + const std::string &TripleStr = M.getTargetTriple(); + Triple TheTriple(TripleStr); + + std::string ErrMsg; + const Target *TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrMsg); + if (!TheTarget) + message(LDPL_FATAL, "Target not found: %s", ErrMsg.c_str()); + + if (unsigned NumOpts = options::extra.size()) + cl::ParseCommandLineOptions(NumOpts, &options::extra[0]); + + SubtargetFeatures Features; + Features.getDefaultSubtargetFeatures(TheTriple); + for (const std::string &A : MAttrs) + Features.AddFeature(A); + + TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + std::unique_ptr<TargetMachine> TM(TheTarget->createTargetMachine( + TripleStr, options::mcpu, Features.getString(), Options, RelocationModel, + CodeModel::Default, CodeGenOpt::Aggressive)); + + runLTOPasses(M, *TM); + + if (options::generate_bc_file == options::BC_SAVE_TEMPS) + saveBCFile(output_name + ".opt.bc", M); + + PassManager CodeGenPasses; + CodeGenPasses.add(new DataLayoutPass()); + + SmallString<128> Filename; + int FD; + if (options::obj_path.empty()) { + std::error_code EC = + sys::fs::createTemporaryFile("lto-llvm", "o", FD, Filename); + if (EC) + message(LDPL_FATAL, "Could not create temporary file: %s", + EC.message().c_str()); + } else { + Filename = options::obj_path; + std::error_code EC = + sys::fs::openFileForWrite(Filename.c_str(), FD, sys::fs::F_None); + if (EC) + message(LDPL_FATAL, "Could not open file: %s", EC.message().c_str()); } - std::string ObjPath; { - const char *Temp; - std::string Error; - if (!CodeGen->compile_to_file(&Temp, /*DisableOpt*/ false, /*DisableInline*/ - false, /*DisableGVNLoadPRE*/ false, Error)) - (*message)(LDPL_ERROR, "Could not produce a combined object file\n"); - ObjPath = Temp; + raw_fd_ostream OS(FD, true); + formatted_raw_ostream FOS(OS); + + if (TM->addPassesToEmitFile(CodeGenPasses, FOS, + TargetMachine::CGFT_ObjectFile)) + message(LDPL_FATAL, "Failed to setup codegen"); + CodeGenPasses.run(M); } - delete CodeGen; - for (std::list<claimed_file>::iterator I = Modules.begin(), - E = Modules.end(); I != E; ++I) { - for (unsigned i = 0; i != I->syms.size(); ++i) { - ld_plugin_symbol &sym = I->syms[i]; - free(sym.name); + if (add_input_file(Filename.c_str()) != LDPS_OK) + message(LDPL_FATAL, + "Unable to add .o file to the link. File left behind in: %s", + Filename.c_str()); + + if (options::obj_path.empty()) + Cleanup.push_back(Filename.c_str()); +} + +/// gold informs us that all symbols have been read. At this point, we use +/// get_symbols to see if any of our definitions have been overridden by a +/// native object file. Then, perform optimization and codegen. +static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) { + if (Modules.empty()) + return LDPS_OK; + + LLVMContext Context; + std::unique_ptr<Module> Combined(new Module("ld-temp.o", Context)); + Linker L(Combined.get()); + + std::string DefaultTriple = sys::getDefaultTargetTriple(); + + StringSet<> Internalize; + StringSet<> Maybe; + for (claimed_file &F : Modules) { + std::unique_ptr<Module> M = + getModuleForFile(Context, F, ApiFile, Internalize, Maybe); + if (!options::triple.empty()) + M->setTargetTriple(options::triple.c_str()); + else if (M->getTargetTriple().empty()) { + M->setTargetTriple(DefaultTriple); } + + if (L.linkInModule(M.get())) + message(LDPL_FATAL, "Failed to link module"); } - if ((*add_input_file)(ObjPath.c_str()) != LDPS_OK) { - (*message)(LDPL_ERROR, "Unable to add .o file to the link."); - (*message)(LDPL_ERROR, "File left behind in: %s", ObjPath.c_str()); - return LDPS_ERR; + for (const auto &Name : Internalize) { + GlobalValue *GV = Combined->getNamedValue(Name.first()); + if (GV) + internalize(*GV); } - if (!options::extra_library_path.empty() && - set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK) { - (*message)(LDPL_ERROR, "Unable to set the extra library path."); - return LDPS_ERR; + for (const auto &Name : Maybe) { + GlobalValue *GV = Combined->getNamedValue(Name.first()); + if (!GV) + continue; + GV->setLinkage(GlobalValue::LinkOnceODRLinkage); + if (canBeOmittedFromSymbolTable(GV)) + internalize(*GV); } - if (options::obj_path.empty()) - Cleanup.push_back(ObjPath); + if (options::generate_bc_file != options::BC_NO) { + std::string path; + if (options::generate_bc_file == options::BC_ONLY) + path = output_name; + else + path = output_name + ".bc"; + saveBCFile(path, *L.getModule()); + if (options::generate_bc_file == options::BC_ONLY) + return LDPS_OK; + } + + codegen(*L.getModule()); + + if (!options::extra_library_path.empty() && + set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK) + message(LDPL_FATAL, "Unable to set the extra library path."); return LDPS_OK; } +static ld_plugin_status all_symbols_read_hook(void) { + ld_plugin_status Ret; + if (!options::generate_api_file) { + Ret = allSymbolsReadHook(nullptr); + } else { + std::error_code EC; + raw_fd_ostream ApiFile("apifile.txt", EC, sys::fs::F_None); + if (EC) + message(LDPL_FATAL, "Unable to open apifile.txt for writing: %s", + EC.message().c_str()); + Ret = allSymbolsReadHook(&ApiFile); + } + + if (options::generate_bc_file == options::BC_ONLY) + exit(0); + + return Ret; +} + static ld_plugin_status cleanup_hook(void) { - for (int i = 0, e = Cleanup.size(); i != e; ++i) { - std::error_code EC = sys::fs::remove(Cleanup[i]); + for (std::string &Name : Cleanup) { + std::error_code EC = sys::fs::remove(Name); if (EC) - (*message)(LDPL_ERROR, "Failed to delete '%s': %s", Cleanup[i].c_str(), - EC.message().c_str()); + message(LDPL_ERROR, "Failed to delete '%s': %s", Name.c_str(), + EC.message().c_str()); } return LDPS_OK; diff --git a/tools/llc/Android.mk b/tools/llc/Android.mk index a25cf5c..a98cd8b 100644 --- a/tools/llc/Android.mk +++ b/tools/llc/Android.mk @@ -48,6 +48,7 @@ llvm_llc_STATIC_LIBRARIES := \ libLLVMipo \ libLLVMipa \ libLLVMLinker \ + libLLVMMCDisassembler \ libLLVMMC \ libLLVMMCParser \ libLLVMScalarOpts \ diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp index 09ff461..fe4d9ac 100644 --- a/tools/llc/llc.cpp +++ b/tools/llc/llc.cpp @@ -41,6 +41,7 @@ #include "llvm/Support/ToolOutputFile.h" #include "llvm/Target/TargetLibraryInfo.h" #include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetSubtargetInfo.h" #include <memory> using namespace llvm; @@ -94,23 +95,6 @@ static cl::opt<bool> AsmVerbose("asm-verbose", static int compileModule(char **, LLVMContext &); -// GetFileNameRoot - Helper function to get the basename of a filename. -static inline std::string -GetFileNameRoot(const std::string &InputFilename) { - std::string IFN = InputFilename; - std::string outputFilename; - int Len = IFN.length(); - if ((Len > 2) && - IFN[Len-3] == '.' && - ((IFN[Len-2] == 'b' && IFN[Len-1] == 'c') || - (IFN[Len-2] == 'l' && IFN[Len-1] == 'l'))) { - outputFilename = std::string(IFN.begin(), IFN.end()-3); // s/.bc/.s/ - } else { - outputFilename = IFN; - } - return outputFilename; -} - static tool_output_file *GetOutputStream(const char *TargetName, Triple::OSType OS, const char *ProgName) { @@ -119,7 +103,12 @@ static tool_output_file *GetOutputStream(const char *TargetName, if (InputFilename == "-") OutputFilename = "-"; else { - OutputFilename = GetFileNameRoot(InputFilename); + // If InputFilename ends in .bc or .ll, remove it. + StringRef IFN = InputFilename; + if (IFN.endswith(".bc") || IFN.endswith(".ll")) + OutputFilename = IFN.drop_back(3); + else + OutputFilename = IFN; switch (FileType) { case TargetMachine::CGFT_AssemblyFile: @@ -158,14 +147,13 @@ static tool_output_file *GetOutputStream(const char *TargetName, } // Open the file. - std::string error; + std::error_code EC; sys::fs::OpenFlags OpenFlags = sys::fs::F_None; if (!Binary) OpenFlags |= sys::fs::F_Text; - tool_output_file *FDOut = new tool_output_file(OutputFilename.c_str(), error, - OpenFlags); - if (!error.empty()) { - errs() << error << '\n'; + tool_output_file *FDOut = new tool_output_file(OutputFilename, EC, OpenFlags); + if (EC) { + errs() << EC.message() << '\n'; delete FDOut; return nullptr; } @@ -231,7 +219,7 @@ static int compileModule(char **argv, LLVMContext &Context) { // If user just wants to list available options, skip module loading if (!SkipModule) { - M.reset(ParseIRFile(InputFilename, Err, Context)); + M = parseIRFile(InputFilename, Err, Context); mod = M.get(); if (mod == nullptr) { Err.print(argv[0], errs()); @@ -317,9 +305,9 @@ static int compileModule(char **argv, LLVMContext &Context) { PM.add(TLI); // Add the target data from the target machine, if it exists, or the module. - if (const DataLayout *DL = Target.getDataLayout()) + if (const DataLayout *DL = Target.getSubtargetImpl()->getDataLayout()) mod->setDataLayout(DL); - PM.add(new DataLayoutPass(mod)); + PM.add(new DataLayoutPass()); if (RelaxAll.getNumOccurrences() > 0 && FileType != TargetMachine::CGFT_ObjectFile) diff --git a/tools/lli/Android.mk b/tools/lli/Android.mk index f550f93..1b09102 100644 --- a/tools/lli/Android.mk +++ b/tools/lli/Android.mk @@ -44,7 +44,6 @@ lli_STATIC_LIBRARIES := \ libLLVMX86Disassembler \ libLLVMAsmPrinter \ libLLVMSelectionDAG \ - libLLVMJIT \ libLLVMCodeGen \ libLLVMInstrumentation \ libLLVMExecutionEngine \ diff --git a/tools/lli/CMakeLists.txt b/tools/lli/CMakeLists.txt index 731b61a..3610d76 100644 --- a/tools/lli/CMakeLists.txt +++ b/tools/lli/CMakeLists.txt @@ -7,8 +7,9 @@ set(LLVM_LINK_COMPONENTS IRReader Instrumentation Interpreter - JIT + MC MCJIT + Object SelectionDAG Support native diff --git a/tools/lli/LLVMBuild.txt b/tools/lli/LLVMBuild.txt index aab2a20..4c14c47 100644 --- a/tools/lli/LLVMBuild.txt +++ b/tools/lli/LLVMBuild.txt @@ -22,4 +22,4 @@ subdirectories = ChildTarget type = Tool name = lli parent = Tools -required_libraries = AsmParser BitReader IRReader Instrumentation Interpreter JIT MCJIT NativeCodeGen SelectionDAG Native +required_libraries = AsmParser BitReader IRReader Instrumentation Interpreter MCJIT NativeCodeGen SelectionDAG Native diff --git a/tools/lli/Makefile b/tools/lli/Makefile index eca5d83..94d6f06 100644 --- a/tools/lli/Makefile +++ b/tools/lli/Makefile @@ -14,7 +14,7 @@ PARALLEL_DIRS := ChildTarget include $(LEVEL)/Makefile.config -LINK_COMPONENTS := mcjit jit instrumentation interpreter nativecodegen bitreader asmparser irreader selectiondag native +LINK_COMPONENTS := mcjit instrumentation interpreter nativecodegen bitreader asmparser irreader selectiondag native # If Intel JIT Events support is confiured, link against the LLVM Intel JIT # Events interface library diff --git a/tools/lli/RPCChannel.h b/tools/lli/RPCChannel.h index 2d8c708..ebd3c65 100644 --- a/tools/lli/RPCChannel.h +++ b/tools/lli/RPCChannel.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLI_RPCCHANNEL_H -#define LLI_RPCCHANNEL_H +#ifndef LLVM_TOOLS_LLI_RPCCHANNEL_H +#define LLVM_TOOLS_LLI_RPCCHANNEL_H #include <stdlib.h> #include <string> @@ -46,4 +46,4 @@ public: } // end namespace llvm -#endif // LLI_RPCCHANNEL_H +#endif diff --git a/tools/lli/RemoteMemoryManager.cpp b/tools/lli/RemoteMemoryManager.cpp index 4816517..5a135ea 100644 --- a/tools/lli/RemoteMemoryManager.cpp +++ b/tools/lli/RemoteMemoryManager.cpp @@ -172,36 +172,3 @@ bool RemoteMemoryManager::finalizeMemory(std::string *ErrMsg) { return false; } - -void RemoteMemoryManager::setMemoryWritable() { llvm_unreachable("Unexpected!"); } -void RemoteMemoryManager::setMemoryExecutable() { llvm_unreachable("Unexpected!"); } -void RemoteMemoryManager::setPoisonMemory(bool poison) { llvm_unreachable("Unexpected!"); } -void RemoteMemoryManager::AllocateGOT() { llvm_unreachable("Unexpected!"); } -uint8_t *RemoteMemoryManager::getGOTBase() const { - llvm_unreachable("Unexpected!"); - return nullptr; -} -uint8_t *RemoteMemoryManager::startFunctionBody(const Function *F, uintptr_t &ActualSize){ - llvm_unreachable("Unexpected!"); - return nullptr; -} -uint8_t *RemoteMemoryManager::allocateStub(const GlobalValue* F, unsigned StubSize, - unsigned Alignment) { - llvm_unreachable("Unexpected!"); - return nullptr; -} -void RemoteMemoryManager::endFunctionBody(const Function *F, uint8_t *FunctionStart, - uint8_t *FunctionEnd) { - llvm_unreachable("Unexpected!"); -} -uint8_t *RemoteMemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) { - llvm_unreachable("Unexpected!"); - return nullptr; -} -uint8_t *RemoteMemoryManager::allocateGlobal(uintptr_t Size, unsigned Alignment) { - llvm_unreachable("Unexpected!"); - return nullptr; -} -void RemoteMemoryManager::deallocateFunctionBody(void *Body) { - llvm_unreachable("Unexpected!"); -} diff --git a/tools/lli/RemoteMemoryManager.h b/tools/lli/RemoteMemoryManager.h index cf5d7c6..0bdb4e2 100644 --- a/tools/lli/RemoteMemoryManager.h +++ b/tools/lli/RemoteMemoryManager.h @@ -12,20 +12,20 @@ // //===----------------------------------------------------------------------===// -#ifndef REMOTEMEMORYMANAGER_H -#define REMOTEMEMORYMANAGER_H +#ifndef LLVM_TOOLS_LLI_REMOTEMEMORYMANAGER_H +#define LLVM_TOOLS_LLI_REMOTEMEMORYMANAGER_H #include "RemoteTarget.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/ExecutionEngine/RTDyldMemoryManager.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Memory.h" #include <utility> namespace llvm { -class RemoteMemoryManager : public JITMemoryManager { +class RemoteMemoryManager : public RTDyldMemoryManager { public: // Notice that this structure takes ownership of the memory allocated. struct Allocation { @@ -93,22 +93,6 @@ public: // This is a non-interface function used by lli void setRemoteTarget(RemoteTarget *T) { Target = T; } - - // The following obsolete JITMemoryManager calls are stubbed out for - // this model. - void setMemoryWritable() override; - void setMemoryExecutable() override; - void setPoisonMemory(bool poison) override; - void AllocateGOT() override; - uint8_t *getGOTBase() const override; - uint8_t *startFunctionBody(const Function *F, uintptr_t &ActualSize) override; - uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, - unsigned Alignment) override; - void endFunctionBody(const Function *F, uint8_t *FunctionStart, - uint8_t *FunctionEnd) override; - uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) override; - uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) override; - void deallocateFunctionBody(void *Body) override; }; } // end namespace llvm diff --git a/tools/lli/RemoteTarget.h b/tools/lli/RemoteTarget.h index 73e8ae2..ee758a2 100644 --- a/tools/lli/RemoteTarget.h +++ b/tools/lli/RemoteTarget.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef REMOTEPROCESS_H -#define REMOTEPROCESS_H +#ifndef LLVM_TOOLS_LLI_REMOTETARGET_H +#define LLVM_TOOLS_LLI_REMOTETARGET_H #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" diff --git a/tools/lli/RemoteTargetExternal.h b/tools/lli/RemoteTargetExternal.h index f87fc61..bb621f5 100644 --- a/tools/lli/RemoteTargetExternal.h +++ b/tools/lli/RemoteTargetExternal.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLI_REMOTETARGETEXTERNAL_H -#define LLI_REMOTETARGETEXTERNAL_H +#ifndef LLVM_TOOLS_LLI_REMOTETARGETEXTERNAL_H +#define LLVM_TOOLS_LLI_REMOTETARGETEXTERNAL_H #include "RPCChannel.h" #include "RemoteTarget.h" @@ -140,4 +140,4 @@ private: } // end namespace llvm -#endif // LLI_REMOTETARGETEXTERNAL_H +#endif diff --git a/tools/lli/RemoteTargetMessage.h b/tools/lli/RemoteTargetMessage.h index cb934a1..c210e4b 100644 --- a/tools/lli/RemoteTargetMessage.h +++ b/tools/lli/RemoteTargetMessage.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLI_REMOTETARGETMESSAGE_H -#define LLI_REMOTETARGETMESSAGE_H +#ifndef LLVM_TOOLS_LLI_REMOTETARGETMESSAGE_H +#define LLVM_TOOLS_LLI_REMOTETARGETMESSAGE_H namespace llvm { diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index 48828c1..276740b 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -22,9 +22,7 @@ #include "llvm/CodeGen/LinkAllCodegenComponents.h" #include "llvm/ExecutionEngine/GenericValue.h" #include "llvm/ExecutionEngine/Interpreter.h" -#include "llvm/ExecutionEngine/JIT.h" #include "llvm/ExecutionEngine/JITEventListener.h" -#include "llvm/ExecutionEngine/JITMemoryManager.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/ObjectCache.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" @@ -76,10 +74,6 @@ namespace { cl::desc("Force interpretation: disable JIT"), cl::init(false)); - cl::opt<bool> UseMCJIT( - "use-mcjit", cl::desc("Enable use of the MC-based JIT (if available)"), - cl::init(false)); - cl::opt<bool> DebugIR( "debug-ir", cl::desc("Generate debug information to allow debugging IR."), cl::init(false)); @@ -263,23 +257,23 @@ public: } virtual ~LLIObjectCache() {} - void notifyObjectCompiled(const Module *M, const MemoryBuffer *Obj) override { + void notifyObjectCompiled(const Module *M, MemoryBufferRef Obj) override { const std::string ModuleID = M->getModuleIdentifier(); std::string CacheName; if (!getCacheFilename(ModuleID, CacheName)) return; - std::string errStr; if (!CacheDir.empty()) { // Create user-defined cache dir. SmallString<128> dir(CacheName); sys::path::remove_filename(dir); sys::fs::create_directories(Twine(dir)); } - raw_fd_ostream outfile(CacheName.c_str(), errStr, sys::fs::F_None); - outfile.write(Obj->getBufferStart(), Obj->getBufferSize()); + std::error_code EC; + raw_fd_ostream outfile(CacheName, EC, sys::fs::F_None); + outfile.write(Obj.getBufferStart(), Obj.getBufferSize()); outfile.close(); } - MemoryBuffer* getObject(const Module* M) override { + std::unique_ptr<MemoryBuffer> getObject(const Module* M) override { const std::string ModuleID = M->getModuleIdentifier(); std::string CacheName; if (!getCacheFilename(ModuleID, CacheName)) @@ -345,7 +339,7 @@ static void addCygMingExtraModule(ExecutionEngine *EE, Triple TargetTriple(TargetTripleStr); // Create a new module. - Module *M = new Module("CygMingHelper", Context); + std::unique_ptr<Module> M = make_unique<Module>("CygMingHelper", Context); M->setTargetTriple(TargetTripleStr); // Create an empty function named "__main". @@ -353,11 +347,11 @@ static void addCygMingExtraModule(ExecutionEngine *EE, if (TargetTriple.isArch64Bit()) { Result = Function::Create( TypeBuilder<int64_t(void), false>::get(Context), - GlobalValue::ExternalLinkage, "__main", M); + GlobalValue::ExternalLinkage, "__main", M.get()); } else { Result = Function::Create( TypeBuilder<int32_t(void), false>::get(Context), - GlobalValue::ExternalLinkage, "__main", M); + GlobalValue::ExternalLinkage, "__main", M.get()); } BasicBlock *BB = BasicBlock::Create(Context, "__main", Result); Builder.SetInsertPoint(BB); @@ -369,7 +363,7 @@ static void addCygMingExtraModule(ExecutionEngine *EE, Builder.CreateRet(ReturnVal); // Add this new module to the ExecutionEngine. - EE->addModule(M); + EE->addModule(std::move(M)); } @@ -398,19 +392,17 @@ int main(int argc, char **argv, char * const *envp) { // Load the bitcode... SMDiagnostic Err; - Module *Mod = ParseIRFile(InputFile, Err, Context); + std::unique_ptr<Module> Owner = parseIRFile(InputFile, Err, Context); + Module *Mod = Owner.get(); if (!Mod) { Err.print(argv[0], errs()); return 1; } if (EnableCacheManager) { - if (UseMCJIT) { - std::string CacheName("file:"); - CacheName.append(InputFile); - Mod->setModuleIdentifier(CacheName); - } else - errs() << "warning: -enable-cache-manager can only be used with MCJIT."; + std::string CacheName("file:"); + CacheName.append(InputFile); + Mod->setModuleIdentifier(CacheName); } // If not jitting lazily, load the whole bitcode file eagerly too. @@ -423,18 +415,12 @@ int main(int argc, char **argv, char * const *envp) { } if (DebugIR) { - if (!UseMCJIT) { - errs() << "warning: -debug-ir used without -use-mcjit. Only partial debug" - << " information will be emitted by the non-MC JIT engine. To see full" - << " source debug information, enable the flag '-use-mcjit'.\n"; - - } ModulePass *DebugIRPass = createDebugIRPass(); DebugIRPass->runOnModule(*Mod); } std::string ErrorMsg; - EngineBuilder builder(Mod); + EngineBuilder builder(std::move(Owner)); builder.setMArch(MArch); builder.setMCPU(MCPU); builder.setMAttrs(MAttrs); @@ -451,20 +437,16 @@ int main(int argc, char **argv, char * const *envp) { // Enable MCJIT if desired. RTDyldMemoryManager *RTDyldMM = nullptr; - if (UseMCJIT && !ForceInterpreter) { - builder.setUseMCJIT(true); + if (!ForceInterpreter) { if (RemoteMCJIT) RTDyldMM = new RemoteMemoryManager(); else RTDyldMM = new SectionMemoryManager(); builder.setMCJITMemoryManager(RTDyldMM); - } else { - if (RemoteMCJIT) { - errs() << "error: Remote process execution requires -use-mcjit\n"; - exit(1); - } - builder.setJITMemoryManager(ForceInterpreter ? nullptr : - JITMemoryManager::CreateDefaultMemManager()); + } else if (RemoteMCJIT) { + errs() << "error: Remote process execution does not work with the " + "interpreter.\n"; + exit(1); } CodeGenOpt::Level OLvl = CodeGenOpt::Default; @@ -511,46 +493,50 @@ int main(int argc, char **argv, char * const *envp) { // Load any additional modules specified on the command line. for (unsigned i = 0, e = ExtraModules.size(); i != e; ++i) { - Module *XMod = ParseIRFile(ExtraModules[i], Err, Context); + std::unique_ptr<Module> XMod = parseIRFile(ExtraModules[i], Err, Context); if (!XMod) { Err.print(argv[0], errs()); return 1; } if (EnableCacheManager) { - if (UseMCJIT) { - std::string CacheName("file:"); - CacheName.append(ExtraModules[i]); - XMod->setModuleIdentifier(CacheName); - } - // else, we already printed a warning above. + std::string CacheName("file:"); + CacheName.append(ExtraModules[i]); + XMod->setModuleIdentifier(CacheName); } - EE->addModule(XMod); + EE->addModule(std::move(XMod)); } for (unsigned i = 0, e = ExtraObjects.size(); i != e; ++i) { - ErrorOr<object::ObjectFile *> Obj = + ErrorOr<object::OwningBinary<object::ObjectFile>> Obj = object::ObjectFile::createObjectFile(ExtraObjects[i]); if (!Obj) { Err.print(argv[0], errs()); return 1; } - EE->addObjectFile(std::unique_ptr<object::ObjectFile>(Obj.get())); + object::OwningBinary<object::ObjectFile> &O = Obj.get(); + EE->addObjectFile(std::move(O)); } for (unsigned i = 0, e = ExtraArchives.size(); i != e; ++i) { - ErrorOr<std::unique_ptr<MemoryBuffer>> ArBuf = + ErrorOr<std::unique_ptr<MemoryBuffer>> ArBufOrErr = MemoryBuffer::getFileOrSTDIN(ExtraArchives[i]); - if (!ArBuf) { + if (!ArBufOrErr) { Err.print(argv[0], errs()); return 1; } - std::error_code EC; - object::Archive *Ar = new object::Archive(std::move(ArBuf.get()), EC); - if (EC || !Ar) { - Err.print(argv[0], errs()); + std::unique_ptr<MemoryBuffer> &ArBuf = ArBufOrErr.get(); + + ErrorOr<std::unique_ptr<object::Archive>> ArOrErr = + object::Archive::create(ArBuf->getMemBufferRef()); + if (std::error_code EC = ArOrErr.getError()) { + errs() << EC.message(); return 1; } - EE->addArchive(Ar); + std::unique_ptr<object::Archive> &Ar = ArOrErr.get(); + + object::OwningBinary<object::Archive> OB(std::move(Ar), std::move(ArBuf)); + + EE->addArchive(std::move(OB)); } // If the target is Cygwin/MingW and we are generating remote code, we @@ -610,20 +596,12 @@ int main(int argc, char **argv, char * const *envp) { NULL); // Run static constructors. - if (UseMCJIT && !ForceInterpreter) { + if (!ForceInterpreter) { // Give MCJIT a chance to apply relocations and set page permissions. EE->finalizeObject(); } EE->runStaticConstructorsDestructors(false); - if (!UseMCJIT && NoLazyCompilation) { - for (Module::iterator I = Mod->begin(), E = Mod->end(); I != E; ++I) { - Function *Fn = &*I; - if (Fn != EntryFn && !Fn->isDeclaration()) - EE->getPointerToFunction(Fn); - } - } - // Trigger compilation separately so code regions that need to be // invalidated will be known. (void)EE->getPointerToFunction(EntryFn); diff --git a/tools/llvm-ar/CMakeLists.txt b/tools/llvm-ar/CMakeLists.txt index 0e809a7..5193def 100644 --- a/tools/llvm-ar/CMakeLists.txt +++ b/tools/llvm-ar/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} + Core Object Support ) diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index f638e55..8ee66f6 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/StringSwitch.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Object/Archive.h" @@ -20,6 +21,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/LineIterator.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" @@ -45,7 +47,7 @@ static StringRef ToolName; static const char *TemporaryOutput; static int TmpArchiveFD = -1; -// fail - Show the error message and exit. +// Show the error message and exit. LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) { outs() << ToolName << ": " << Error << ".\n"; if (TmpArchiveFD != -1) @@ -67,14 +69,16 @@ static void failIfError(std::error_code EC, Twine Context = "") { // llvm-ar/llvm-ranlib remaining positional arguments. static cl::list<std::string> -RestOfArgs(cl::Positional, cl::OneOrMore, - cl::desc("[relpos] [count] <archive-file> [members]...")); + RestOfArgs(cl::Positional, cl::ZeroOrMore, + cl::desc("[relpos] [count] <archive-file> [members]...")); + +static cl::opt<bool> MRI("M", cl::desc("")); std::string Options; -// MoreHelp - Provide additional help output explaining the operations and -// modifiers of llvm-ar. This object instructs the CommandLine library -// to print the text of the constructor when the --help option is given. +// Provide additional help output explaining the operations and modifiers of +// llvm-ar. This object instructs the CommandLine library to print the text of +// the constructor when the --help option is given. static cl::extrahelp MoreHelp( "\nOPERATIONS:\n" " d[NsS] - delete file(s) from the archive\n" @@ -132,9 +136,9 @@ static std::string ArchiveName; // This variable holds the list of member files to proecess, as given // on the command line. -static std::vector<std::string> Members; +static std::vector<StringRef> Members; -// show_help - Show the error message, the help message and exit. +// Show the error message, the help message and exit. LLVM_ATTRIBUTE_NORETURN static void show_help(const std::string &msg) { errs() << ToolName << ": " << msg << "\n\n"; @@ -142,8 +146,8 @@ show_help(const std::string &msg) { std::exit(1); } -// getRelPos - Extract the member filename from the command line for -// the [relpos] argument associated with a, b, and i modifiers +// Extract the member filename from the command line for the [relpos] argument +// associated with a, b, and i modifiers static void getRelPos() { if(RestOfArgs.size() == 0) show_help("Expected [relpos] for a, b, or i modifier"); @@ -158,7 +162,7 @@ static void getOptions() { RestOfArgs.erase(RestOfArgs.begin()); } -// getArchive - Get the archive file name from the command line +// Get the archive file name from the command line static void getArchive() { if(RestOfArgs.size() == 0) show_help("An archive name must be specified"); @@ -166,17 +170,24 @@ static void getArchive() { RestOfArgs.erase(RestOfArgs.begin()); } -// getMembers - Copy over remaining items in RestOfArgs to our Members vector -// This is just for clarity. +// Copy over remaining items in RestOfArgs to our Members vector static void getMembers() { - if(RestOfArgs.size() > 0) - Members = std::vector<std::string>(RestOfArgs); + for (auto &Arg : RestOfArgs) + Members.push_back(Arg); } -// parseCommandLine - Parse the command line options as presented and return the -// operation specified. Process all modifiers and check to make sure that -// constraints on modifier/operation pairs have not been violated. +static void runMRIScript(); + +// Parse the command line options as presented and return the operation +// specified. Process all modifiers and check to make sure that constraints on +// modifier/operation pairs have not been violated. static ArchiveOperation parseCommandLine() { + if (MRI) { + if (!RestOfArgs.empty()) + fail("Cannot mix -M and other options"); + runMRIScript(); + } + getOptions(); // Keep track of number of operations. We can only specify one @@ -279,8 +290,8 @@ static void doPrint(StringRef Name, object::Archive::child_iterator I) { outs().write(Data.data(), Data.size()); } -// putMode - utility function for printing out the file mode when the 't' -// operation is in verbose mode. +// Utility function for printing out the file mode when the 't' operation is in +// verbose mode. static void printMode(unsigned mode) { if (mode & 004) outs() << "r"; @@ -401,20 +412,20 @@ class NewArchiveIterator { object::Archive::child_iterator OldI; - std::string NewFilename; + StringRef NewFilename; mutable int NewFD; mutable sys::fs::file_status NewStatus; public: NewArchiveIterator(object::Archive::child_iterator I, StringRef Name); - NewArchiveIterator(std::string *I, StringRef Name); + NewArchiveIterator(StringRef I, StringRef Name); NewArchiveIterator(); bool isNewMember() const; StringRef getName() const; object::Archive::child_iterator getOld() const; - const char *getNew() const; + StringRef getNew() const; int getFD() const; const sys::fs::file_status &getStatus() const; }; @@ -426,8 +437,8 @@ NewArchiveIterator::NewArchiveIterator(object::Archive::child_iterator I, StringRef Name) : IsNewMember(false), Name(Name), OldI(I) {} -NewArchiveIterator::NewArchiveIterator(std::string *NewFilename, StringRef Name) - : IsNewMember(true), Name(Name), NewFilename(*NewFilename), NewFD(-1) {} +NewArchiveIterator::NewArchiveIterator(StringRef NewFilename, StringRef Name) + : IsNewMember(true), Name(Name), NewFilename(NewFilename), NewFD(-1) {} StringRef NewArchiveIterator::getName() const { return Name; } @@ -438,9 +449,9 @@ object::Archive::child_iterator NewArchiveIterator::getOld() const { return OldI; } -const char *NewArchiveIterator::getNew() const { +StringRef NewArchiveIterator::getNew() const { assert(IsNewMember); - return NewFilename.c_str(); + return NewFilename; } int NewArchiveIterator::getFD() const { @@ -485,16 +496,17 @@ enum InsertAction { IA_MoveNewMember }; -static InsertAction -computeInsertAction(ArchiveOperation Operation, - object::Archive::child_iterator I, StringRef Name, - std::vector<std::string>::iterator &Pos) { +static InsertAction computeInsertAction(ArchiveOperation Operation, + object::Archive::child_iterator I, + StringRef Name, + std::vector<StringRef>::iterator &Pos) { if (Operation == QuickAppend || Members.empty()) return IA_AddOldMember; - std::vector<std::string>::iterator MI = std::find_if( - Members.begin(), Members.end(), - [Name](StringRef Path) { return Name == sys::path::filename(Path); }); + auto MI = + std::find_if(Members.begin(), Members.end(), [Name](StringRef Path) { + return Name == sys::path::filename(Path); + }); if (MI == Members.end()) return IA_AddOldMember; @@ -542,11 +554,9 @@ computeNewArchiveMembers(ArchiveOperation Operation, int InsertPos = -1; StringRef PosName = sys::path::filename(RelPos); if (OldArchive) { - for (object::Archive::child_iterator I = OldArchive->child_begin(), - E = OldArchive->child_end(); - I != E; ++I) { + for (auto &Child : OldArchive->children()) { int Pos = Ret.size(); - ErrorOr<StringRef> NameOrErr = I->getName(); + ErrorOr<StringRef> NameOrErr = Child.getName(); failIfError(NameOrErr.getError()); StringRef Name = NameOrErr.get(); if (Name == PosName) { @@ -557,22 +567,23 @@ computeNewArchiveMembers(ArchiveOperation Operation, InsertPos = Pos + 1; } - std::vector<std::string>::iterator MemberI = Members.end(); - InsertAction Action = computeInsertAction(Operation, I, Name, MemberI); + std::vector<StringRef>::iterator MemberI = Members.end(); + InsertAction Action = + computeInsertAction(Operation, Child, Name, MemberI); switch (Action) { case IA_AddOldMember: - addMember(Ret, I, Name); + addMember(Ret, Child, Name); break; case IA_AddNewMeber: - addMember(Ret, &*MemberI, Name); + addMember(Ret, *MemberI, Name); break; case IA_Delete: break; case IA_MoveOldMember: - addMember(Moved, I, Name); + addMember(Moved, Child, Name); break; case IA_MoveNewMember: - addMember(Moved, &*MemberI, Name); + addMember(Moved, *MemberI, Name); break; } if (MemberI != Members.end()) @@ -594,11 +605,10 @@ computeNewArchiveMembers(ArchiveOperation Operation, Ret.insert(Ret.begin() + InsertPos, Members.size(), NewArchiveIterator()); int Pos = InsertPos; - for (std::vector<std::string>::iterator I = Members.begin(), - E = Members.end(); - I != E; ++I, ++Pos) { - StringRef Name = sys::path::filename(*I); - addMember(Ret, &*I, Name, Pos); + for (auto &Member : Members) { + StringRef Name = sys::path::filename(Member); + addMember(Ret, Member, Name, Pos); + ++Pos; } return Ret; @@ -686,7 +696,7 @@ static void writeStringTable(raw_fd_ostream &Out, static void writeSymbolTable(raw_fd_ostream &Out, ArrayRef<NewArchiveIterator> Members, - MutableArrayRef<std::unique_ptr<MemoryBuffer>> Buffers, + ArrayRef<MemoryBufferRef> Buffers, std::vector<std::pair<unsigned, unsigned>> &MemberOffsetRefs) { unsigned StartOffset = 0; unsigned MemberNum = 0; @@ -697,13 +707,13 @@ writeSymbolTable(raw_fd_ostream &Out, ArrayRef<NewArchiveIterator> Members, for (ArrayRef<NewArchiveIterator>::iterator I = Members.begin(), E = Members.end(); I != E; ++I, ++MemberNum) { - std::unique_ptr<MemoryBuffer> &MemberBuffer = Buffers[MemberNum]; - ErrorOr<object::SymbolicFile *> ObjOrErr = + MemoryBufferRef MemberBuffer = Buffers[MemberNum]; + ErrorOr<std::unique_ptr<object::SymbolicFile>> ObjOrErr = object::SymbolicFile::createSymbolicFile( MemberBuffer, sys::fs::file_magic::unknown, &Context); if (!ObjOrErr) continue; // FIXME: check only for "not an object file" errors. - std::unique_ptr<object::SymbolicFile> Obj(ObjOrErr.get()); + object::SymbolicFile &Obj = *ObjOrErr.get(); if (!StartOffset) { printMemberHeader(Out, "", sys::TimeValue::now(), 0, 0, 0, 0); @@ -711,7 +721,7 @@ writeSymbolTable(raw_fd_ostream &Out, ArrayRef<NewArchiveIterator> Members, print32BE(Out, 0); } - for (const object::BasicSymbolRef &S : Obj->symbols()) { + for (const object::BasicSymbolRef &S : Obj.symbols()) { uint32_t Symflags = S.getFlags(); if (Symflags & object::SymbolRef::SF_FormatSpecific) continue; @@ -725,7 +735,6 @@ writeSymbolTable(raw_fd_ostream &Out, ArrayRef<NewArchiveIterator> Members, MemberOffsetRefs.push_back(std::make_pair(Out.tell(), MemberNum)); print32BE(Out, 0); } - MemberBuffer.reset(Obj->releaseBuffer()); } Out << NameOS.str(); @@ -743,8 +752,9 @@ writeSymbolTable(raw_fd_ostream &Out, ArrayRef<NewArchiveIterator> Members, Out.seek(Pos); } -static void performWriteOperation(ArchiveOperation Operation, - object::Archive *OldArchive) { +static void +performWriteOperation(ArchiveOperation Operation, object::Archive *OldArchive, + std::vector<NewArchiveIterator> &NewMembers) { SmallString<128> TmpArchive; failIfError(sys::fs::createUniqueFile(ArchiveName + ".temp-archive-%%%%%%%.a", TmpArchiveFD, TmpArchive)); @@ -754,38 +764,36 @@ static void performWriteOperation(ArchiveOperation Operation, raw_fd_ostream &Out = Output.os(); Out << "!<arch>\n"; - std::vector<NewArchiveIterator> NewMembers = - computeNewArchiveMembers(Operation, OldArchive); - std::vector<std::pair<unsigned, unsigned> > MemberOffsetRefs; - std::vector<std::unique_ptr<MemoryBuffer>> MemberBuffers; - MemberBuffers.resize(NewMembers.size()); + std::vector<std::unique_ptr<MemoryBuffer>> Buffers; + std::vector<MemoryBufferRef> Members; for (unsigned I = 0, N = NewMembers.size(); I < N; ++I) { - std::unique_ptr<MemoryBuffer> MemberBuffer; NewArchiveIterator &Member = NewMembers[I]; + MemoryBufferRef MemberRef; if (Member.isNewMember()) { - const char *Filename = Member.getNew(); + StringRef Filename = Member.getNew(); int FD = Member.getFD(); const sys::fs::file_status &Status = Member.getStatus(); ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr = MemoryBuffer::getOpenFile(FD, Filename, Status.getSize(), false); failIfError(MemberBufferOrErr.getError(), Filename); - MemberBuffer = std::move(MemberBufferOrErr.get()); + Buffers.push_back(std::move(MemberBufferOrErr.get())); + MemberRef = Buffers.back()->getMemBufferRef(); } else { object::Archive::child_iterator OldMember = Member.getOld(); - ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr = - OldMember->getMemoryBuffer(); + ErrorOr<MemoryBufferRef> MemberBufferOrErr = + OldMember->getMemoryBufferRef(); failIfError(MemberBufferOrErr.getError()); - MemberBuffer = std::move(MemberBufferOrErr.get()); + MemberRef = MemberBufferOrErr.get(); } - MemberBuffers[I].reset(MemberBuffer.release()); + Members.push_back(MemberRef); } if (Symtab) { - writeSymbolTable(Out, NewMembers, MemberBuffers, MemberOffsetRefs); + writeSymbolTable(Out, NewMembers, Members, MemberOffsetRefs); } std::vector<unsigned> StringMapIndexes; @@ -809,9 +817,9 @@ static void performWriteOperation(ArchiveOperation Operation, } Out.seek(Pos); - const MemoryBuffer *File = MemberBuffers[MemberNum].get(); + MemoryBufferRef File = Members[MemberNum]; if (I->isNewMember()) { - const char *FileName = I->getNew(); + StringRef FileName = I->getNew(); const sys::fs::file_status &Status = I->getStatus(); StringRef Name = sys::path::filename(FileName); @@ -839,7 +847,7 @@ static void performWriteOperation(ArchiveOperation Operation, OldMember->getSize()); } - Out << File->getBuffer(); + Out << File.getBuffer(); if (Out.tell() % 2) Out << '\n'; @@ -851,6 +859,18 @@ static void performWriteOperation(ArchiveOperation Operation, TemporaryOutput = nullptr; } +static void +performWriteOperation(ArchiveOperation Operation, object::Archive *OldArchive, + std::vector<NewArchiveIterator> *NewMembersP) { + if (NewMembersP) { + performWriteOperation(Operation, OldArchive, *NewMembersP); + return; + } + std::vector<NewArchiveIterator> NewMembers = + computeNewArchiveMembers(Operation, OldArchive); + performWriteOperation(Operation, OldArchive, NewMembers); +} + static void createSymbolTable(object::Archive *OldArchive) { // When an archive is created or modified, if the s option is given, the // resulting archive will have a current symbol table. If the S option @@ -861,11 +881,12 @@ static void createSymbolTable(object::Archive *OldArchive) { if (OldArchive->hasSymbolTable()) return; - performWriteOperation(CreateSymTab, OldArchive); + performWriteOperation(CreateSymTab, OldArchive, nullptr); } static void performOperation(ArchiveOperation Operation, - object::Archive *OldArchive) { + object::Archive *OldArchive, + std::vector<NewArchiveIterator> *NewMembers) { switch (Operation) { case Print: case DisplayTable: @@ -877,7 +898,7 @@ static void performOperation(ArchiveOperation Operation, case Move: case QuickAppend: case ReplaceOrInsert: - performWriteOperation(Operation, OldArchive); + performWriteOperation(Operation, OldArchive, NewMembers); return; case CreateSymTab: createSymbolTable(OldArchive); @@ -886,53 +907,8 @@ static void performOperation(ArchiveOperation Operation, llvm_unreachable("Unknown operation."); } -static int ar_main(char **argv); -static int ranlib_main(); - -// main - main program for llvm-ar .. see comments in the code -int main(int argc, char **argv) { - ToolName = argv[0]; - // Print a stack trace if we signal out. - sys::PrintStackTraceOnErrorSignal(); - PrettyStackTraceProgram X(argc, argv); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. - - // Have the command line options parsed and handle things - // like --help and --version. - cl::ParseCommandLineOptions(argc, argv, - "LLVM Archiver (llvm-ar)\n\n" - " This program archives bitcode files into single libraries\n" - ); - - llvm::InitializeAllTargetInfos(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllAsmParsers(); - - StringRef Stem = sys::path::stem(ToolName); - if (Stem.find("ar") != StringRef::npos) - return ar_main(argv); - if (Stem.find("ranlib") != StringRef::npos) - return ranlib_main(); - fail("Not ranlib or ar!"); -} - -static int performOperation(ArchiveOperation Operation); - -int ranlib_main() { - if (RestOfArgs.size() != 1) - fail(ToolName + "takes just one archive as argument"); - ArchiveName = RestOfArgs[0]; - return performOperation(CreateSymTab); -} - -int ar_main(char **argv) { - // Do our own parsing of the command line because the CommandLine utility - // can't handle the grouped positional parameters without a dash. - ArchiveOperation Operation = parseCommandLine(); - return performOperation(Operation); -} - -static int performOperation(ArchiveOperation Operation) { +static int performOperation(ArchiveOperation Operation, + std::vector<NewArchiveIterator> *NewMembers) { // Create or open the archive object. ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(ArchiveName, -1, false); @@ -944,14 +920,14 @@ static int performOperation(ArchiveOperation Operation) { } if (!EC) { - object::Archive Archive(std::move(Buf.get()), EC); + object::Archive Archive(Buf.get()->getMemBufferRef(), EC); if (EC) { errs() << ToolName << ": error loading '" << ArchiveName << "': " << EC.message() << "!\n"; return 1; } - performOperation(Operation, &Archive); + performOperation(Operation, &Archive, NewMembers); return 0; } @@ -966,6 +942,116 @@ static int performOperation(ArchiveOperation Operation) { } } - performOperation(Operation, nullptr); + performOperation(Operation, nullptr, NewMembers); return 0; } + +static void runMRIScript() { + enum class MRICommand { AddLib, AddMod, Create, Save, End, Invalid }; + + ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getSTDIN(); + failIfError(Buf.getError()); + const MemoryBuffer &Ref = *Buf.get(); + bool Saved = false; + std::vector<NewArchiveIterator> NewMembers; + std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; + std::vector<std::unique_ptr<object::Archive>> Archives; + + for (line_iterator I(Ref, /*SkipBlanks*/ true, ';'), E; I != E; ++I) { + StringRef Line = *I; + StringRef CommandStr, Rest; + std::tie(CommandStr, Rest) = Line.split(' '); + Rest = Rest.trim(); + if (!Rest.empty() && Rest.front() == '"' && Rest.back() == '"') + Rest = Rest.drop_front().drop_back(); + auto Command = StringSwitch<MRICommand>(CommandStr.lower()) + .Case("addlib", MRICommand::AddLib) + .Case("addmod", MRICommand::AddMod) + .Case("create", MRICommand::Create) + .Case("save", MRICommand::Save) + .Case("end", MRICommand::End) + .Default(MRICommand::Invalid); + + switch (Command) { + case MRICommand::AddLib: { + auto BufOrErr = MemoryBuffer::getFile(Rest, -1, false); + failIfError(BufOrErr.getError(), "Could not open library"); + ArchiveBuffers.push_back(std::move(*BufOrErr)); + auto LibOrErr = + object::Archive::create(ArchiveBuffers.back()->getMemBufferRef()); + failIfError(LibOrErr.getError(), "Could not parse library"); + Archives.push_back(std::move(*LibOrErr)); + object::Archive &Lib = *Archives.back(); + for (auto &Member : Lib.children()) { + ErrorOr<StringRef> NameOrErr = Member.getName(); + failIfError(NameOrErr.getError()); + addMember(NewMembers, Member, *NameOrErr); + } + break; + } + case MRICommand::AddMod: + addMember(NewMembers, Rest, sys::path::filename(Rest)); + break; + case MRICommand::Create: + Create = true; + if (!ArchiveName.empty()) + fail("Editing multiple archives not supported"); + if (Saved) + fail("File already saved"); + ArchiveName = Rest; + break; + case MRICommand::Save: + Saved = true; + break; + case MRICommand::End: + break; + case MRICommand::Invalid: + fail("Unknown command: " + CommandStr); + } + } + + // Nothing to do if not saved. + if (Saved) + performOperation(ReplaceOrInsert, &NewMembers); + exit(0); +} + +static int ar_main() { + // Do our own parsing of the command line because the CommandLine utility + // can't handle the grouped positional parameters without a dash. + ArchiveOperation Operation = parseCommandLine(); + return performOperation(Operation, nullptr); +} + +static int ranlib_main() { + if (RestOfArgs.size() != 1) + fail(ToolName + "takes just one archive as argument"); + ArchiveName = RestOfArgs[0]; + return performOperation(CreateSymTab, nullptr); +} + +int main(int argc, char **argv) { + ToolName = argv[0]; + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + // Have the command line options parsed and handle things + // like --help and --version. + cl::ParseCommandLineOptions(argc, argv, + "LLVM Archiver (llvm-ar)\n\n" + " This program archives bitcode files into single libraries\n" + ); + + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + + StringRef Stem = sys::path::stem(ToolName); + if (Stem.find("ar") != StringRef::npos) + return ar_main(); + if (Stem.find("ranlib") != StringRef::npos) + return ranlib_main(); + fail("Not ranlib or ar!"); +} diff --git a/tools/llvm-as/llvm-as.cpp b/tools/llvm-as/llvm-as.cpp index 007241c..5ccf505 100644 --- a/tools/llvm-as/llvm-as.cpp +++ b/tools/llvm-as/llvm-as.cpp @@ -69,11 +69,11 @@ static void WriteOutputFile(const Module *M) { } } - std::string ErrorInfo; + std::error_code EC; std::unique_ptr<tool_output_file> Out( - new tool_output_file(OutputFilename.c_str(), ErrorInfo, sys::fs::F_None)); - if (!ErrorInfo.empty()) { - errs() << ErrorInfo << '\n'; + new tool_output_file(OutputFilename, EC, sys::fs::F_None)); + if (EC) { + errs() << EC.message() << '\n'; exit(1); } @@ -94,7 +94,7 @@ int main(int argc, char **argv) { // Parse the file now... SMDiagnostic Err; - std::unique_ptr<Module> M(ParseAssemblyFile(InputFilename, Err, Context)); + std::unique_ptr<Module> M = parseAssemblyFile(InputFilename, Err, Context); if (!M.get()) { Err.print(argv[0], errs()); return 1; diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index 15567cf..f95b272 100644 --- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -31,6 +31,7 @@ #include "llvm/Bitcode/LLVMBitCodes.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/IR/Verifier.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Format.h" #include "llvm/Support/ManagedStatic.h" @@ -61,6 +62,10 @@ NonSymbolic("non-symbolic", cl::desc("Emit numeric info in dump even if" " symbolic info is available")); +static cl::opt<std::string> + BlockInfoFilename("block-info", + cl::desc("Use the BLOCK_INFO from the given file")); + namespace { /// CurStreamTypeType - A type for CurStreamType @@ -71,15 +76,11 @@ enum CurStreamTypeType { } -/// CurStreamType - If we can sniff the flavor of this stream, we can produce -/// better dump info. -static CurStreamTypeType CurStreamType; - - /// GetBlockName - Return a symbolic block name if known, otherwise return /// null. static const char *GetBlockName(unsigned BlockID, - const BitstreamReader &StreamFile) { + const BitstreamReader &StreamFile, + CurStreamTypeType CurStreamType) { // Standard blocks for all bitcode files. if (BlockID < bitc::FIRST_APPLICATION_BLOCKID) { if (BlockID == bitc::BLOCKINFO_BLOCK_ID) @@ -115,7 +116,8 @@ static const char *GetBlockName(unsigned BlockID, /// GetCodeName - Return a symbolic code name if known, otherwise return /// null. static const char *GetCodeName(unsigned CodeID, unsigned BlockID, - const BitstreamReader &StreamFile) { + const BitstreamReader &StreamFile, + CurStreamTypeType CurStreamType) { // Standard blocks for all bitcode files. if (BlockID < bitc::FIRST_APPLICATION_BLOCKID) { if (BlockID == bitc::BLOCKINFO_BLOCK_ID) { @@ -271,7 +273,8 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, case bitc::USELIST_BLOCK_ID: switch(CodeID) { default:return nullptr; - case bitc::USELIST_CODE_ENTRY: return "USELIST_CODE_ENTRY"; + case bitc::USELIST_CODE_DEFAULT: return "USELIST_CODE_DEFAULT"; + case bitc::USELIST_CODE_BB: return "USELIST_CODE_BB"; } } } @@ -315,14 +318,14 @@ static std::map<unsigned, PerBlockIDStats> BlockIDStats; /// Error - All bitcode analysis errors go through this function, making this a /// good place to breakpoint if debugging. -static bool Error(const std::string &Err) { +static bool Error(const Twine &Err) { errs() << Err << "\n"; return true; } /// ParseBlock - Read a block, updating statistics, etc. static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID, - unsigned IndentLevel) { + unsigned IndentLevel, CurStreamTypeType CurStreamType) { std::string Indent(IndentLevel*2, ' '); uint64_t BlockBitStart = Stream.GetCurrentBitNo(); @@ -348,7 +351,8 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID, const char *BlockName = nullptr; if (Dump) { outs() << Indent << "<"; - if ((BlockName = GetBlockName(BlockID, *Stream.getBitStreamReader()))) + if ((BlockName = GetBlockName(BlockID, *Stream.getBitStreamReader(), + CurStreamType))) outs() << BlockName; else outs() << "UnknownBlock" << BlockID; @@ -390,7 +394,7 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID, case BitstreamEntry::SubBlock: { uint64_t SubBlockBitStart = Stream.GetCurrentBitNo(); - if (ParseBlock(Stream, Entry.ID, IndentLevel+1)) + if (ParseBlock(Stream, Entry.ID, IndentLevel+1, CurStreamType)) return true; ++BlockStats.NumSubBlocks; uint64_t SubBlockBitEnd = Stream.GetCurrentBitNo(); @@ -431,12 +435,14 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID, if (Dump) { outs() << Indent << " <"; if (const char *CodeName = - GetCodeName(Code, BlockID, *Stream.getBitStreamReader())) + GetCodeName(Code, BlockID, *Stream.getBitStreamReader(), + CurStreamType)) outs() << CodeName; else outs() << "UnknownCode" << Code; if (NonSymbolic && - GetCodeName(Code, BlockID, *Stream.getBitStreamReader())) + GetCodeName(Code, BlockID, *Stream.getBitStreamReader(), + CurStreamType)) outs() << " codeid=" << Code; if (Entry.ID != bitc::UNABBREV_RECORD) outs() << " abbrevid=" << Entry.ID; @@ -474,21 +480,23 @@ static void PrintSize(uint64_t Bits) { (double)Bits/8, (unsigned long)(Bits/32)); } - -/// AnalyzeBitcode - Analyze the bitcode file specified by InputFilename. -static int AnalyzeBitcode() { +static bool openBitcodeFile(StringRef Path, + std::unique_ptr<MemoryBuffer> &MemBuf, + BitstreamReader &StreamFile, + BitstreamCursor &Stream, + CurStreamTypeType &CurStreamType) { // Read the input file. ErrorOr<std::unique_ptr<MemoryBuffer>> MemBufOrErr = - MemoryBuffer::getFileOrSTDIN(InputFilename); + MemoryBuffer::getFileOrSTDIN(Path); if (std::error_code EC = MemBufOrErr.getError()) - return Error("Error reading '" + InputFilename + "': " + EC.message()); - std::unique_ptr<MemoryBuffer> MemBuf = std::move(MemBufOrErr.get()); + return Error(Twine("Error reading '") + Path + "': " + EC.message()); + MemBuf = std::move(MemBufOrErr.get()); if (MemBuf->getBufferSize() & 3) return Error("Bitcode stream should be a multiple of 4 bytes in length"); const unsigned char *BufPtr = (const unsigned char *)MemBuf->getBufferStart(); - const unsigned char *EndBufPtr = BufPtr+MemBuf->getBufferSize(); + const unsigned char *EndBufPtr = BufPtr + MemBuf->getBufferSize(); // If we have a wrapper header, parse it and ignore the non-bc file contents. // The magic number is 0x0B17C0DE stored in little endian. @@ -496,8 +504,8 @@ static int AnalyzeBitcode() { if (SkipBitcodeWrapperHeader(BufPtr, EndBufPtr, true)) return Error("Invalid bitcode wrapper header"); - BitstreamReader StreamFile(BufPtr, EndBufPtr); - BitstreamCursor Stream(StreamFile); + StreamFile = BitstreamReader(BufPtr, EndBufPtr); + Stream = BitstreamCursor(StreamFile); StreamFile.CollectBlockInfoNames(); // Read the stream signature. @@ -516,6 +524,48 @@ static int AnalyzeBitcode() { Signature[4] == 0xE && Signature[5] == 0xD) CurStreamType = LLVMIRBitstream; + return false; +} + +/// AnalyzeBitcode - Analyze the bitcode file specified by InputFilename. +static int AnalyzeBitcode() { + std::unique_ptr<MemoryBuffer> StreamBuffer; + BitstreamReader StreamFile; + BitstreamCursor Stream; + CurStreamTypeType CurStreamType; + if (openBitcodeFile(InputFilename, StreamBuffer, StreamFile, Stream, + CurStreamType)) + return true; + + // Read block info from BlockInfoFilename, if specified. + // The block info must be a top-level block. + if (!BlockInfoFilename.empty()) { + std::unique_ptr<MemoryBuffer> BlockInfoBuffer; + BitstreamReader BlockInfoFile; + BitstreamCursor BlockInfoCursor; + CurStreamTypeType BlockInfoStreamType; + if (openBitcodeFile(BlockInfoFilename, BlockInfoBuffer, BlockInfoFile, + BlockInfoCursor, BlockInfoStreamType)) + return true; + + while (!BlockInfoCursor.AtEndOfStream()) { + unsigned Code = BlockInfoCursor.ReadCode(); + if (Code != bitc::ENTER_SUBBLOCK) + return Error("Invalid record at top-level in block info file"); + + unsigned BlockID = BlockInfoCursor.ReadSubBlockID(); + if (BlockID == bitc::BLOCKINFO_BLOCK_ID) { + if (BlockInfoCursor.ReadBlockInfoBlock()) + return Error("Malformed BlockInfoBlock in block info file"); + break; + } + + BlockInfoCursor.SkipBlock(); + } + + StreamFile.takeBlockInfo(std::move(BlockInfoFile)); + } + unsigned NumTopBlocks = 0; // Parse the top-level structure. We only allow blocks at the top-level. @@ -526,14 +576,14 @@ static int AnalyzeBitcode() { unsigned BlockID = Stream.ReadSubBlockID(); - if (ParseBlock(Stream, BlockID, 0)) + if (ParseBlock(Stream, BlockID, 0, CurStreamType)) return true; ++NumTopBlocks; } if (Dump) outs() << "\n\n"; - uint64_t BufferSizeBits = (EndBufPtr-BufPtr)*CHAR_BIT; + uint64_t BufferSizeBits = StreamFile.getBitcodeBytes().getExtent() * CHAR_BIT; // Print a summary of the read file. outs() << "Summary of " << InputFilename << ":\n"; outs() << " Total size: "; @@ -552,7 +602,8 @@ static int AnalyzeBitcode() { for (std::map<unsigned, PerBlockIDStats>::iterator I = BlockIDStats.begin(), E = BlockIDStats.end(); I != E; ++I) { outs() << " Block ID #" << I->first; - if (const char *BlockName = GetBlockName(I->first, StreamFile)) + if (const char *BlockName = GetBlockName(I->first, StreamFile, + CurStreamType)) outs() << " (" << BlockName << ")"; outs() << ":\n"; @@ -610,7 +661,8 @@ static int AnalyzeBitcode() { outs() << " "; if (const char *CodeName = - GetCodeName(FreqPairs[i].second, I->first, StreamFile)) + GetCodeName(FreqPairs[i].second, I->first, StreamFile, + CurStreamType)) outs() << CodeName << "\n"; else outs() << "UnknownCode" << FreqPairs[i].second << "\n"; diff --git a/tools/llvm-c-test/CMakeLists.txt b/tools/llvm-c-test/CMakeLists.txt index 34fea3d..989678b 100644 --- a/tools/llvm-c-test/CMakeLists.txt +++ b/tools/llvm-c-test/CMakeLists.txt @@ -7,6 +7,10 @@ set(LLVM_LINK_COMPONENTS Target ) +if(TARGET LLVM) + set(LLVM_LINK_COMPONENTS) +endif() + if (LLVM_COMPILER_IS_GCC_COMPATIBLE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wstrict-prototypes") endif () @@ -21,3 +25,8 @@ add_llvm_tool(llvm-c-test object.c targets.c ) + +# Use libLLVM.so if it is available. +if(TARGET LLVM) + target_link_libraries(llvm-c-test LLVM) +endif() diff --git a/tools/llvm-c-test/disassemble.c b/tools/llvm-c-test/disassemble.c index eb40bf3..05a9218 100644 --- a/tools/llvm-c-test/disassemble.c +++ b/tools/llvm-c-test/disassemble.c @@ -18,6 +18,7 @@ #include "llvm-c/Target.h" #include <stdio.h> #include <stdlib.h> +#include <string.h> static void pprint(int pos, unsigned char *buf, int len, const char *disasm) { int i; @@ -33,13 +34,15 @@ static void pprint(int pos, unsigned char *buf, int len, const char *disasm) { printf(" %s\n", disasm); } -static void do_disassemble(const char *triple, unsigned char *buf, int siz) { - LLVMDisasmContextRef D = LLVMCreateDisasm(triple, NULL, 0, NULL, NULL); +static void do_disassemble(const char *triple, const char *features, + unsigned char *buf, int siz) { + LLVMDisasmContextRef D = LLVMCreateDisasmCPUFeatures(triple, "", features, + NULL, 0, NULL, NULL); char outline[1024]; int pos; if (!D) { - printf("ERROR: Couldn't create disassebler for triple %s\n", triple); + printf("ERROR: Couldn't create disassembler for triple %s\n", triple); return; } @@ -62,19 +65,22 @@ static void do_disassemble(const char *triple, unsigned char *buf, int siz) { static void handle_line(char **tokens, int ntokens) { unsigned char disbuf[128]; size_t disbuflen = 0; - char *triple = tokens[0]; + const char *triple = tokens[0]; + const char *features = tokens[1]; int i; - printf("triple: %s\n", triple); + printf("triple: %s, features: %s\n", triple, features); + if (!strcmp(features, "NULL")) + features = ""; - for (i = 1; i < ntokens; i++) { + for (i = 2; i < ntokens; i++) { disbuf[disbuflen++] = strtol(tokens[i], NULL, 16); if (disbuflen >= sizeof(disbuf)) { fprintf(stderr, "Warning: Too long line, truncating\n"); break; } } - do_disassemble(triple, disbuf, disbuflen); + do_disassemble(triple, features, disbuf, disbuflen); } int disassemble(void) { diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt index 8d83762..50c84e6 100644 --- a/tools/llvm-config/CMakeLists.txt +++ b/tools/llvm-config/CMakeLists.txt @@ -33,3 +33,18 @@ add_llvm_tool(llvm-config # Add the dependency on the generation step. add_file_dependencies(${CMAKE_CURRENT_SOURCE_DIR}/llvm-config.cpp ${BUILDVARIABLES_OBJPATH}) + +if(CMAKE_CROSSCOMPILING) + set(${project}_LLVM_CONFIG_EXE "${LLVM_NATIVE_BUILD}/bin/llvm-config") + set(${project}_LLVM_CONFIG_EXE ${${project}_LLVM_CONFIG_EXE} PARENT_SCOPE) + + add_custom_command(OUTPUT "${${project}_LLVM_CONFIG_EXE}" + COMMAND ${CMAKE_COMMAND} --build . --target llvm-config --config $<CONFIGURATION> + DEPENDS ${LLVM_NATIVE_BUILD}/CMakeCache.txt + WORKING_DIRECTORY ${LLVM_NATIVE_BUILD} + COMMENT "Building native llvm-config...") + add_custom_target(${project}NativeLLVMConfig DEPENDS ${${project}_LLVM_CONFIG_EXE}) + add_dependencies(${project}NativeLLVMConfig ConfigureNativeLLVM) + + add_dependencies(llvm-config ${project}NativeLLVMConfig) +endif(CMAKE_CROSSCOMPILING) diff --git a/tools/llvm-cov/Android.mk b/tools/llvm-cov/Android.mk index dae211f..d76c940 100644 --- a/tools/llvm-cov/Android.mk +++ b/tools/llvm-cov/Android.mk @@ -8,9 +8,22 @@ LLVM_ROOT_PATH := $(LOCAL_PATH)/../.. #===---------------------------------------------------------------=== llvm_cov_SRC_FILES := \ - llvm-cov.cpp + CodeCoverage.cpp \ + CoverageFilters.cpp \ + CoverageReport.cpp \ + CoverageSummary.cpp \ + CoverageSummaryInfo.cpp \ + gcov.cpp \ + llvm-cov.cpp \ + SourceCoverageView.cpp \ + TestingSupport.cpp llvm_cov_STATIC_LIBRARIES := \ + libLLVMObject \ + libLLVMProfileData \ + libLLVMMC \ + libLLVMMCParser \ + libLLVMBitReader \ libLLVMCore \ libLLVMSupport \ diff --git a/tools/llvm-cov/CMakeLists.txt b/tools/llvm-cov/CMakeLists.txt index 67cea71..b2d2b89 100644 --- a/tools/llvm-cov/CMakeLists.txt +++ b/tools/llvm-cov/CMakeLists.txt @@ -1,5 +1,13 @@ -set(LLVM_LINK_COMPONENTS core support ) +set(LLVM_LINK_COMPONENTS core support object profiledata) add_llvm_tool(llvm-cov llvm-cov.cpp + gcov.cpp + CodeCoverage.cpp + CoverageFilters.cpp + CoverageReport.cpp + CoverageSummary.cpp + CoverageSummaryInfo.cpp + SourceCoverageView.cpp + TestingSupport.cpp ) diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp new file mode 100644 index 0000000..093525e --- /dev/null +++ b/tools/llvm-cov/CodeCoverage.cpp @@ -0,0 +1,484 @@ +//===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The 'CodeCoverageTool' class implements a command line tool to analyze and +// report coverage information using the profiling instrumentation and code +// coverage mapping. +// +//===----------------------------------------------------------------------===// + +#include "RenderingSupport.h" +#include "CoverageViewOptions.h" +#include "CoverageFilters.h" +#include "SourceCoverageView.h" +#include "CoverageSummary.h" +#include "CoverageReport.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/ProfileData/CoverageMapping.h" +#include "llvm/ProfileData/CoverageMappingReader.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryObject.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/PrettyStackTrace.h" +#include <functional> +#include <system_error> + +using namespace llvm; +using namespace coverage; + +namespace { +/// \brief The implementation of the coverage tool. +class CodeCoverageTool { +public: + enum Command { + /// \brief The show command. + Show, + /// \brief The report command. + Report + }; + + /// \brief Print the error message to the error output stream. + void error(const Twine &Message, StringRef Whence = ""); + + /// \brief Return a memory buffer for the given source file. + ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile); + + /// \brief Create source views for the expansions of the view. + void attachExpansionSubViews(SourceCoverageView &View, + ArrayRef<ExpansionRecord> Expansions, + CoverageMapping &Coverage); + + /// \brief Create the source view of a particular function. + std::unique_ptr<SourceCoverageView> + createFunctionView(const FunctionRecord &Function, CoverageMapping &Coverage); + + /// \brief Create the main source view of a particular source file. + std::unique_ptr<SourceCoverageView> + createSourceFileView(StringRef SourceFile, CoverageMapping &Coverage); + + /// \brief Load the coverage mapping data. Return true if an error occured. + std::unique_ptr<CoverageMapping> load(); + + int run(Command Cmd, int argc, const char **argv); + + typedef std::function<int(int, const char **)> CommandLineParserType; + + int show(int argc, const char **argv, + CommandLineParserType commandLineParser); + + int report(int argc, const char **argv, + CommandLineParserType commandLineParser); + + std::string ObjectFilename; + CoverageViewOptions ViewOpts; + std::string PGOFilename; + CoverageFiltersMatchAll Filters; + std::vector<std::string> SourceFiles; + std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> + LoadedSourceFiles; + bool CompareFilenamesOnly; + StringMap<std::string> RemappedFilenames; +}; +} + +void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { + errs() << "error: "; + if (!Whence.empty()) + errs() << Whence << ": "; + errs() << Message << "\n"; +} + +ErrorOr<const MemoryBuffer &> +CodeCoverageTool::getSourceFile(StringRef SourceFile) { + // If we've remapped filenames, look up the real location for this file. + if (!RemappedFilenames.empty()) { + auto Loc = RemappedFilenames.find(SourceFile); + if (Loc != RemappedFilenames.end()) + SourceFile = Loc->second; + } + for (const auto &Files : LoadedSourceFiles) + if (sys::fs::equivalent(SourceFile, Files.first)) + return *Files.second; + auto Buffer = MemoryBuffer::getFile(SourceFile); + if (auto EC = Buffer.getError()) { + error(EC.message(), SourceFile); + return EC; + } + LoadedSourceFiles.push_back( + std::make_pair(SourceFile, std::move(Buffer.get()))); + return *LoadedSourceFiles.back().second; +} + +void +CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View, + ArrayRef<ExpansionRecord> Expansions, + CoverageMapping &Coverage) { + if (!ViewOpts.ShowExpandedRegions) + return; + for (const auto &Expansion : Expansions) { + auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); + if (ExpansionCoverage.empty()) + continue; + auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename()); + if (!SourceBuffer) + continue; + + auto SubViewExpansions = ExpansionCoverage.getExpansions(); + auto SubView = llvm::make_unique<SourceCoverageView>( + SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage)); + attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); + View.addExpansion(Expansion.Region, std::move(SubView)); + } +} + +std::unique_ptr<SourceCoverageView> +CodeCoverageTool::createFunctionView(const FunctionRecord &Function, + CoverageMapping &Coverage) { + auto FunctionCoverage = Coverage.getCoverageForFunction(Function); + if (FunctionCoverage.empty()) + return nullptr; + auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename()); + if (!SourceBuffer) + return nullptr; + + auto Expansions = FunctionCoverage.getExpansions(); + auto View = llvm::make_unique<SourceCoverageView>( + SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage)); + attachExpansionSubViews(*View, Expansions, Coverage); + + return View; +} + +std::unique_ptr<SourceCoverageView> +CodeCoverageTool::createSourceFileView(StringRef SourceFile, + CoverageMapping &Coverage) { + auto SourceBuffer = getSourceFile(SourceFile); + if (!SourceBuffer) + return nullptr; + auto FileCoverage = Coverage.getCoverageForFile(SourceFile); + if (FileCoverage.empty()) + return nullptr; + + auto Expansions = FileCoverage.getExpansions(); + auto View = llvm::make_unique<SourceCoverageView>( + SourceBuffer.get(), ViewOpts, std::move(FileCoverage)); + attachExpansionSubViews(*View, Expansions, Coverage); + + for (auto Function : Coverage.getInstantiations(SourceFile)) { + auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); + auto SubViewExpansions = SubViewCoverage.getExpansions(); + auto SubView = llvm::make_unique<SourceCoverageView>( + SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); + attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); + + if (SubView) { + unsigned FileID = Function->CountedRegions.front().FileID; + unsigned Line = 0; + for (const auto &CR : Function->CountedRegions) + if (CR.FileID == FileID) + Line = std::max(CR.LineEnd, Line); + View->addInstantiation(Function->Name, Line, std::move(SubView)); + } + } + return View; +} + +std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { + auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename); + if (std::error_code EC = CoverageOrErr.getError()) { + colored_ostream(errs(), raw_ostream::RED) + << "error: Failed to load coverage: " << EC.message(); + errs() << "\n"; + return nullptr; + } + auto Coverage = std::move(CoverageOrErr.get()); + unsigned Mismatched = Coverage->getMismatchedCount(); + if (Mismatched) { + colored_ostream(errs(), raw_ostream::RED) + << "warning: " << Mismatched << " functions have mismatched data. "; + errs() << "\n"; + } + + if (CompareFilenamesOnly) { + auto CoveredFiles = Coverage.get()->getUniqueSourceFiles(); + for (auto &SF : SourceFiles) { + StringRef SFBase = sys::path::filename(SF); + for (const auto &CF : CoveredFiles) + if (SFBase == sys::path::filename(CF)) { + RemappedFilenames[CF] = SF; + SF = CF; + break; + } + } + } + + return Coverage; +} + +int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::opt<std::string, true> ObjectFilename( + cl::Positional, cl::Required, cl::location(this->ObjectFilename), + cl::desc("Covered executable or object file.")); + + cl::list<std::string> InputSourceFiles( + cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); + + cl::opt<std::string, true> PGOFilename( + "instr-profile", cl::Required, cl::location(this->PGOFilename), + cl::desc( + "File with the profile data obtained after an instrumented run")); + + cl::opt<bool> DebugDump("dump", cl::Optional, + cl::desc("Show internal debug dump")); + + cl::opt<bool> FilenameEquivalence( + "filename-equivalence", cl::Optional, + cl::desc("Treat source files as equivalent to paths in the coverage data " + "when the file names match, even if the full paths do not")); + + cl::OptionCategory FilteringCategory("Function filtering options"); + + cl::list<std::string> NameFilters( + "name", cl::Optional, + cl::desc("Show code coverage only for functions with the given name"), + cl::ZeroOrMore, cl::cat(FilteringCategory)); + + cl::list<std::string> NameRegexFilters( + "name-regex", cl::Optional, + cl::desc("Show code coverage only for functions that match the given " + "regular expression"), + cl::ZeroOrMore, cl::cat(FilteringCategory)); + + cl::opt<double> RegionCoverageLtFilter( + "region-coverage-lt", cl::Optional, + cl::desc("Show code coverage only for functions with region coverage " + "less than the given threshold"), + cl::cat(FilteringCategory)); + + cl::opt<double> RegionCoverageGtFilter( + "region-coverage-gt", cl::Optional, + cl::desc("Show code coverage only for functions with region coverage " + "greater than the given threshold"), + cl::cat(FilteringCategory)); + + cl::opt<double> LineCoverageLtFilter( + "line-coverage-lt", cl::Optional, + cl::desc("Show code coverage only for functions with line coverage less " + "than the given threshold"), + cl::cat(FilteringCategory)); + + cl::opt<double> LineCoverageGtFilter( + "line-coverage-gt", cl::Optional, + cl::desc("Show code coverage only for functions with line coverage " + "greater than the given threshold"), + cl::cat(FilteringCategory)); + + auto commandLineParser = [&, this](int argc, const char **argv) -> int { + cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); + ViewOpts.Debug = DebugDump; + CompareFilenamesOnly = FilenameEquivalence; + + // Create the function filters + if (!NameFilters.empty() || !NameRegexFilters.empty()) { + auto NameFilterer = new CoverageFilters; + for (const auto &Name : NameFilters) + NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); + for (const auto &Regex : NameRegexFilters) + NameFilterer->push_back( + llvm::make_unique<NameRegexCoverageFilter>(Regex)); + Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer)); + } + if (RegionCoverageLtFilter.getNumOccurrences() || + RegionCoverageGtFilter.getNumOccurrences() || + LineCoverageLtFilter.getNumOccurrences() || + LineCoverageGtFilter.getNumOccurrences()) { + auto StatFilterer = new CoverageFilters; + if (RegionCoverageLtFilter.getNumOccurrences()) + StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( + RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); + if (RegionCoverageGtFilter.getNumOccurrences()) + StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( + RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); + if (LineCoverageLtFilter.getNumOccurrences()) + StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( + LineCoverageFilter::LessThan, LineCoverageLtFilter)); + if (LineCoverageGtFilter.getNumOccurrences()) + StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( + RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); + Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer)); + } + + for (const auto &File : InputSourceFiles) { + SmallString<128> Path(File); + if (std::error_code EC = sys::fs::make_absolute(Path)) { + errs() << "error: " << File << ": " << EC.message(); + return 1; + } + SourceFiles.push_back(Path.str()); + } + return 0; + }; + + switch (Cmd) { + case Show: + return show(argc, argv, commandLineParser); + case Report: + return report(argc, argv, commandLineParser); + } + return 0; +} + +int CodeCoverageTool::show(int argc, const char **argv, + CommandLineParserType commandLineParser) { + + cl::OptionCategory ViewCategory("Viewing options"); + + cl::opt<bool> ShowLineExecutionCounts( + "show-line-counts", cl::Optional, + cl::desc("Show the execution counts for each line"), cl::init(true), + cl::cat(ViewCategory)); + + cl::opt<bool> ShowRegions( + "show-regions", cl::Optional, + cl::desc("Show the execution counts for each region"), + cl::cat(ViewCategory)); + + cl::opt<bool> ShowBestLineRegionsCounts( + "show-line-counts-or-regions", cl::Optional, + cl::desc("Show the execution counts for each line, or the execution " + "counts for each region on lines that have multiple regions"), + cl::cat(ViewCategory)); + + cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, + cl::desc("Show expanded source regions"), + cl::cat(ViewCategory)); + + cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, + cl::desc("Show function instantiations"), + cl::cat(ViewCategory)); + + cl::opt<bool> NoColors("no-colors", cl::Optional, + cl::desc("Don't show text colors"), cl::init(false), + cl::cat(ViewCategory)); + + auto Err = commandLineParser(argc, argv); + if (Err) + return Err; + + ViewOpts.Colors = !NoColors; + ViewOpts.ShowLineNumbers = true; + ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || + !ShowRegions || ShowBestLineRegionsCounts; + ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; + ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts; + ViewOpts.ShowExpandedRegions = ShowExpansions; + ViewOpts.ShowFunctionInstantiations = ShowInstantiations; + + auto Coverage = load(); + if (!Coverage) + return 1; + + if (!Filters.empty()) { + // Show functions + for (const auto &Function : Coverage->getCoveredFunctions()) { + if (!Filters.matches(Function)) + continue; + + auto mainView = createFunctionView(Function, *Coverage); + if (!mainView) { + ViewOpts.colored_ostream(outs(), raw_ostream::RED) + << "warning: Could not read coverage for '" << Function.Name; + outs() << "\n"; + continue; + } + ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << Function.Name + << ":"; + outs() << "\n"; + mainView->render(outs(), /*WholeFile=*/false); + outs() << "\n"; + } + return 0; + } + + // Show files + bool ShowFilenames = SourceFiles.size() != 1; + + if (SourceFiles.empty()) + // Get the source files from the function coverage mapping + for (StringRef Filename : Coverage->getUniqueSourceFiles()) + SourceFiles.push_back(Filename); + + for (const auto &SourceFile : SourceFiles) { + auto mainView = createSourceFileView(SourceFile, *Coverage); + if (!mainView) { + ViewOpts.colored_ostream(outs(), raw_ostream::RED) + << "warning: The file '" << SourceFile << "' isn't covered."; + outs() << "\n"; + continue; + } + + if (ShowFilenames) { + ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":"; + outs() << "\n"; + } + mainView->render(outs(), /*Wholefile=*/true); + if (SourceFiles.size() > 1) + outs() << "\n"; + } + + return 0; +} + +int CodeCoverageTool::report(int argc, const char **argv, + CommandLineParserType commandLineParser) { + cl::opt<bool> NoColors("no-colors", cl::Optional, + cl::desc("Don't show text colors"), cl::init(false)); + + auto Err = commandLineParser(argc, argv); + if (Err) + return Err; + + ViewOpts.Colors = !NoColors; + + auto Coverage = load(); + if (!Coverage) + return 1; + + CoverageSummary Summarizer; + Summarizer.createSummaries(*Coverage); + CoverageReport Report(ViewOpts, Summarizer); + if (SourceFiles.empty() && Filters.empty()) { + Report.renderFileReports(llvm::outs()); + return 0; + } + + Report.renderFunctionReports(llvm::outs()); + return 0; +} + +int showMain(int argc, const char *argv[]) { + CodeCoverageTool Tool; + return Tool.run(CodeCoverageTool::Show, argc, argv); +} + +int reportMain(int argc, const char *argv[]) { + CodeCoverageTool Tool; + return Tool.run(CodeCoverageTool::Report, argc, argv); +} diff --git a/tools/llvm-cov/CoverageFilters.cpp b/tools/llvm-cov/CoverageFilters.cpp new file mode 100644 index 0000000..325dd72 --- /dev/null +++ b/tools/llvm-cov/CoverageFilters.cpp @@ -0,0 +1,59 @@ +//===- CoverageFilters.cpp - Function coverage mapping filters ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These classes provide filtering for function coverage mapping records. +// +//===----------------------------------------------------------------------===// + +#include "CoverageFilters.h" +#include "CoverageSummaryInfo.h" +#include "llvm/Support/Regex.h" + +using namespace llvm; + +bool NameCoverageFilter::matches(const coverage::FunctionRecord &Function) { + StringRef FuncName = Function.Name; + return FuncName.find(Name) != StringRef::npos; +} + +bool +NameRegexCoverageFilter::matches(const coverage::FunctionRecord &Function) { + return llvm::Regex(Regex).match(Function.Name); +} + +bool RegionCoverageFilter::matches(const coverage::FunctionRecord &Function) { + return PassesThreshold(FunctionCoverageSummary::get(Function) + .RegionCoverage.getPercentCovered()); +} + +bool LineCoverageFilter::matches(const coverage::FunctionRecord &Function) { + return PassesThreshold( + FunctionCoverageSummary::get(Function).LineCoverage.getPercentCovered()); +} + +void CoverageFilters::push_back(std::unique_ptr<CoverageFilter> Filter) { + Filters.push_back(std::move(Filter)); +} + +bool CoverageFilters::matches(const coverage::FunctionRecord &Function) { + for (const auto &Filter : Filters) { + if (Filter->matches(Function)) + return true; + } + return false; +} + +bool +CoverageFiltersMatchAll::matches(const coverage::FunctionRecord &Function) { + for (const auto &Filter : Filters) { + if (!Filter->matches(Function)) + return false; + } + return true; +} diff --git a/tools/llvm-cov/CoverageFilters.h b/tools/llvm-cov/CoverageFilters.h new file mode 100644 index 0000000..e543005 --- /dev/null +++ b/tools/llvm-cov/CoverageFilters.h @@ -0,0 +1,127 @@ +//===- CoverageFilters.h - Function coverage mapping filters --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These classes provide filtering for function coverage mapping records. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGEFILTERS_H +#define LLVM_COV_COVERAGEFILTERS_H + +#include "llvm/ProfileData/CoverageMapping.h" +#include <vector> +#include <memory> + +namespace llvm { + +/// \brief Matches specific functions that pass the requirement of this filter. +class CoverageFilter { +public: + virtual ~CoverageFilter() {} + + /// \brief Return true if the function passes the requirements of this filter. + virtual bool matches(const coverage::FunctionRecord &Function) { + return true; + } +}; + +/// \brief Matches functions that contain a specific string in their name. +class NameCoverageFilter : public CoverageFilter { + StringRef Name; + +public: + NameCoverageFilter(StringRef Name) : Name(Name) {} + + bool matches(const coverage::FunctionRecord &Function) override; +}; + +/// \brief Matches functions whose name matches a certain regular expression. +class NameRegexCoverageFilter : public CoverageFilter { + StringRef Regex; + +public: + NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {} + + bool matches(const coverage::FunctionRecord &Function) override; +}; + +/// \brief Matches numbers that pass a certain threshold. +template <typename T> class StatisticThresholdFilter { +public: + enum Operation { LessThan, GreaterThan }; + +protected: + Operation Op; + T Threshold; + + StatisticThresholdFilter(Operation Op, T Threshold) + : Op(Op), Threshold(Threshold) {} + + /// \brief Return true if the given number is less than + /// or greater than the certain threshold. + bool PassesThreshold(T Value) const { + switch (Op) { + case LessThan: + return Value < Threshold; + case GreaterThan: + return Value > Threshold; + } + return false; + } +}; + +/// \brief Matches functions whose region coverage percentage +/// is above/below a certain percentage. +class RegionCoverageFilter : public CoverageFilter, + public StatisticThresholdFilter<double> { +public: + RegionCoverageFilter(Operation Op, double Threshold) + : StatisticThresholdFilter(Op, Threshold) {} + + bool matches(const coverage::FunctionRecord &Function) override; +}; + +/// \brief Matches functions whose line coverage percentage +/// is above/below a certain percentage. +class LineCoverageFilter : public CoverageFilter, + public StatisticThresholdFilter<double> { +public: + LineCoverageFilter(Operation Op, double Threshold) + : StatisticThresholdFilter(Op, Threshold) {} + + bool matches(const coverage::FunctionRecord &Function) override; +}; + +/// \brief A collection of filters. +/// Matches functions that match any filters contained +/// in an instance of this class. +class CoverageFilters : public CoverageFilter { +protected: + std::vector<std::unique_ptr<CoverageFilter>> Filters; + +public: + /// \brief Append a filter to this collection. + void push_back(std::unique_ptr<CoverageFilter> Filter); + + bool empty() const { return Filters.empty(); } + + bool matches(const coverage::FunctionRecord &Function) override; +}; + +/// \brief A collection of filters. +/// Matches functions that match all of the filters contained +/// in an instance of this class. +class CoverageFiltersMatchAll : public CoverageFilters { +public: + bool matches(const coverage::FunctionRecord &Function) override; +}; + +} // namespace llvm + +#endif // LLVM_COV_COVERAGEFILTERS_H diff --git a/tools/llvm-cov/CoverageReport.cpp b/tools/llvm-cov/CoverageReport.cpp new file mode 100644 index 0000000..7ac9355 --- /dev/null +++ b/tools/llvm-cov/CoverageReport.cpp @@ -0,0 +1,202 @@ +//===- CoverageReport.cpp - Code coverage report -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements rendering of a code coverage report. +// +//===----------------------------------------------------------------------===// + +#include "CoverageReport.h" +#include "CoverageSummary.h" +#include "RenderingSupport.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FileSystem.h" + +using namespace llvm; +namespace { +/// \brief Helper struct which prints trimmed and aligned columns. +struct Column { + enum TrimKind { NoTrim, LeftTrim, RightTrim }; + + enum AlignmentKind { LeftAlignment, RightAlignment }; + + StringRef Str; + unsigned Width; + TrimKind Trim; + AlignmentKind Alignment; + + Column(StringRef Str, unsigned Width) + : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {} + + Column &set(TrimKind Value) { + Trim = Value; + return *this; + } + + Column &set(AlignmentKind Value) { + Alignment = Value; + return *this; + } + + void render(raw_ostream &OS) const; +}; +raw_ostream &operator<<(raw_ostream &OS, const Column &Value) { + Value.render(OS); + return OS; +} +} + +void Column::render(raw_ostream &OS) const { + if (Str.size() <= Width) { + if (Alignment == RightAlignment) { + OS.indent(Width - Str.size()); + OS << Str; + return; + } + OS << Str; + OS.indent(Width - Str.size()); + return; + } + + switch (Trim) { + case NoTrim: + OS << Str.substr(0, Width); + break; + case LeftTrim: + OS << "..." << Str.substr(Str.size() - Width + 3); + break; + case RightTrim: + OS << Str.substr(0, Width - 3) << "..."; + break; + } +} + +static Column column(StringRef Str, unsigned Width) { + return Column(Str, Width); +} + +template <typename T> +static Column column(StringRef Str, unsigned Width, const T &Value) { + return Column(Str, Width).set(Value); +} + +static const unsigned FileReportColumns[] = {25, 10, 8, 8, 10, 10}; +static const unsigned FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8}; + +/// \brief Prints a horizontal divider which spans across the given columns. +template <typename T, size_t N> +static void renderDivider(T (&Columns)[N], raw_ostream &OS) { + unsigned Length = 0; + for (unsigned I = 0; I < N; ++I) + Length += Columns[I]; + for (unsigned I = 0; I < Length; ++I) + OS << '-'; +} + +/// \brief Return the color which correponds to the coverage +/// percentage of a certain metric. +template <typename T> +static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) { + if (Info.isFullyCovered()) + return raw_ostream::GREEN; + return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW + : raw_ostream::RED; +} + +void CoverageReport::render(const FileCoverageSummary &File, raw_ostream &OS) { + OS << column(File.Name, FileReportColumns[0], Column::LeftTrim) + << format("%*u", FileReportColumns[1], (unsigned)File.RegionCoverage.NumRegions); + Options.colored_ostream(OS, File.RegionCoverage.isFullyCovered() + ? raw_ostream::GREEN + : raw_ostream::RED) + << format("%*u", FileReportColumns[2], (unsigned)File.RegionCoverage.NotCovered); + Options.colored_ostream(OS, + determineCoveragePercentageColor(File.RegionCoverage)) + << format("%*.2f", FileReportColumns[3] - 1, + File.RegionCoverage.getPercentCovered()) << '%'; + OS << format("%*u", FileReportColumns[4], + (unsigned)File.FunctionCoverage.NumFunctions); + Options.colored_ostream( + OS, determineCoveragePercentageColor(File.FunctionCoverage)) + << format("%*.2f", FileReportColumns[5] - 1, + File.FunctionCoverage.getPercentCovered()) << '%'; + OS << "\n"; +} + +void CoverageReport::render(const FunctionCoverageSummary &Function, + raw_ostream &OS) { + OS << column(Function.Name, FunctionReportColumns[0], Column::RightTrim) + << format("%*u", FunctionReportColumns[1], + (unsigned)Function.RegionCoverage.NumRegions); + Options.colored_ostream(OS, Function.RegionCoverage.isFullyCovered() + ? raw_ostream::GREEN + : raw_ostream::RED) + << format("%*u", FunctionReportColumns[2], + (unsigned)Function.RegionCoverage.NotCovered); + Options.colored_ostream( + OS, determineCoveragePercentageColor(Function.RegionCoverage)) + << format("%*.2f", FunctionReportColumns[3] - 1, + Function.RegionCoverage.getPercentCovered()) << '%'; + OS << format("%*u", FunctionReportColumns[4], + (unsigned)Function.LineCoverage.NumLines); + Options.colored_ostream(OS, Function.LineCoverage.isFullyCovered() + ? raw_ostream::GREEN + : raw_ostream::RED) + << format("%*u", FunctionReportColumns[5], + (unsigned)Function.LineCoverage.NotCovered); + Options.colored_ostream( + OS, determineCoveragePercentageColor(Function.LineCoverage)) + << format("%*.2f", FunctionReportColumns[6] - 1, + Function.LineCoverage.getPercentCovered()) << '%'; + OS << "\n"; +} + +void CoverageReport::renderFunctionReports(raw_ostream &OS) { + bool isFirst = true; + for (const auto &File : Summary.getFileSummaries()) { + if (isFirst) + isFirst = false; + else + OS << "\n"; + OS << "File '" << File.Name << "':\n"; + OS << column("Name", FunctionReportColumns[0]) + << column("Regions", FunctionReportColumns[1], Column::RightAlignment) + << column("Miss", FunctionReportColumns[2], Column::RightAlignment) + << column("Cover", FunctionReportColumns[3], Column::RightAlignment) + << column("Lines", FunctionReportColumns[4], Column::RightAlignment) + << column("Miss", FunctionReportColumns[5], Column::RightAlignment) + << column("Cover", FunctionReportColumns[6], Column::RightAlignment); + OS << "\n"; + renderDivider(FunctionReportColumns, OS); + OS << "\n"; + for (const auto &Function : File.FunctionSummaries) + render(Function, OS); + renderDivider(FunctionReportColumns, OS); + OS << "\n"; + render(FunctionCoverageSummary("TOTAL", /*ExecutionCount=*/0, + File.RegionCoverage, File.LineCoverage), + OS); + } +} + +void CoverageReport::renderFileReports(raw_ostream &OS) { + OS << column("Filename", FileReportColumns[0]) + << column("Regions", FileReportColumns[1], Column::RightAlignment) + << column("Miss", FileReportColumns[2], Column::RightAlignment) + << column("Cover", FileReportColumns[3], Column::RightAlignment) + << column("Functions", FileReportColumns[4], Column::RightAlignment) + << column("Executed", FileReportColumns[5], Column::RightAlignment) + << "\n"; + renderDivider(FileReportColumns, OS); + OS << "\n"; + for (const auto &File : Summary.getFileSummaries()) + render(File, OS); + renderDivider(FileReportColumns, OS); + OS << "\n"; + render(Summary.getCombinedFileSummaries(), OS); +} diff --git a/tools/llvm-cov/CoverageReport.h b/tools/llvm-cov/CoverageReport.h new file mode 100644 index 0000000..e8d34f2 --- /dev/null +++ b/tools/llvm-cov/CoverageReport.h @@ -0,0 +1,40 @@ +//===- CoverageReport.h - Code coverage report ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements rendering of a code coverage report. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGEREPORT_H +#define LLVM_COV_COVERAGEREPORT_H + +#include "CoverageViewOptions.h" +#include "CoverageSummary.h" + +namespace llvm { + +/// \brief Displays the code coverage report. +class CoverageReport { + const CoverageViewOptions &Options; + CoverageSummary &Summary; + + void render(const FileCoverageSummary &File, raw_ostream &OS); + void render(const FunctionCoverageSummary &Function, raw_ostream &OS); + +public: + CoverageReport(const CoverageViewOptions &Options, CoverageSummary &Summary) + : Options(Options), Summary(Summary) {} + + void renderFunctionReports(raw_ostream &OS); + + void renderFileReports(raw_ostream &OS); +}; +} + +#endif // LLVM_COV_COVERAGEREPORT_H diff --git a/tools/llvm-cov/CoverageSummary.cpp b/tools/llvm-cov/CoverageSummary.cpp new file mode 100644 index 0000000..059c8c8 --- /dev/null +++ b/tools/llvm-cov/CoverageSummary.cpp @@ -0,0 +1,64 @@ +//===- CoverageSummary.cpp - Code coverage summary ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements data management and rendering for the code coverage +// summaries of all files and functions. +// +//===----------------------------------------------------------------------===// + +#include "CoverageSummary.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" + +using namespace llvm; + +unsigned CoverageSummary::getFileID(StringRef Filename) { + for (unsigned I = 0, E = Filenames.size(); I < E; ++I) { + if (sys::fs::equivalent(Filenames[I], Filename)) + return I; + } + Filenames.push_back(Filename); + return Filenames.size() - 1; +} + +void +CoverageSummary::createSummaries(const coverage::CoverageMapping &Coverage) { + for (StringRef Filename : Coverage.getUniqueSourceFiles()) { + size_t PrevSize = FunctionSummaries.size(); + for (const auto &F : Coverage.getCoveredFunctions(Filename)) + FunctionSummaries.push_back(FunctionCoverageSummary::get(F)); + size_t Count = FunctionSummaries.size() - PrevSize; + if (Count == 0) + continue; + FileSummaries.push_back(FileCoverageSummary::get( + Filename, makeArrayRef(FunctionSummaries.data() + PrevSize, Count))); + } +} + +FileCoverageSummary CoverageSummary::getCombinedFileSummaries() { + size_t NumRegions = 0, CoveredRegions = 0; + size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0; + size_t NumFunctionsExecuted = 0, NumFunctions = 0; + for (const auto &File : FileSummaries) { + NumRegions += File.RegionCoverage.NumRegions; + CoveredRegions += File.RegionCoverage.Covered; + + NumLines += File.LineCoverage.NumLines; + NonCodeLines += File.LineCoverage.NonCodeLines; + CoveredLines += File.LineCoverage.Covered; + + NumFunctionsExecuted += File.FunctionCoverage.Executed; + NumFunctions += File.FunctionCoverage.NumFunctions; + } + return FileCoverageSummary( + "TOTAL", RegionCoverageInfo(CoveredRegions, NumRegions), + LineCoverageInfo(CoveredLines, NonCodeLines, NumLines), + FunctionCoverageInfo(NumFunctionsExecuted, NumFunctions), + None); +} diff --git a/tools/llvm-cov/CoverageSummary.h b/tools/llvm-cov/CoverageSummary.h new file mode 100644 index 0000000..9dbebde --- /dev/null +++ b/tools/llvm-cov/CoverageSummary.h @@ -0,0 +1,45 @@ +//===- CoverageSummary.h - Code coverage summary --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements data management and rendering for the code coverage +// summaries of all files and functions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGESUMMARY_H +#define LLVM_COV_COVERAGESUMMARY_H + +#include "CoverageSummaryInfo.h" +#include <vector> + +namespace llvm { + +/// \brief Manager for the function and file code coverage summaries. +class CoverageSummary { + std::vector<StringRef> Filenames; + std::vector<FunctionCoverageSummary> FunctionSummaries; + std::vector<std::pair<unsigned, unsigned>> FunctionSummariesFileIDs; + std::vector<FileCoverageSummary> FileSummaries; + + unsigned getFileID(StringRef Filename); + +public: + void createSummaries(const coverage::CoverageMapping &Coverage); + + ArrayRef<FileCoverageSummary> getFileSummaries() { return FileSummaries; } + + FileCoverageSummary getCombinedFileSummaries(); + + void render(const FunctionCoverageSummary &Summary, raw_ostream &OS); + + void render(raw_ostream &OS); +}; +} + +#endif // LLVM_COV_COVERAGESUMMARY_H diff --git a/tools/llvm-cov/CoverageSummaryInfo.cpp b/tools/llvm-cov/CoverageSummaryInfo.cpp new file mode 100644 index 0000000..dd78ace --- /dev/null +++ b/tools/llvm-cov/CoverageSummaryInfo.cpp @@ -0,0 +1,96 @@ +//===- CoverageSummaryInfo.cpp - Coverage summary for function/file -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These structures are used to represent code coverage metrics +// for functions/files. +// +//===----------------------------------------------------------------------===// + +#include "CoverageSummaryInfo.h" + +using namespace llvm; +using namespace coverage; + +FunctionCoverageSummary +FunctionCoverageSummary::get(const coverage::FunctionRecord &Function) { + // Compute the region coverage + size_t NumCodeRegions = 0, CoveredRegions = 0; + for (auto &CR : Function.CountedRegions) { + if (CR.Kind != CounterMappingRegion::CodeRegion) + continue; + ++NumCodeRegions; + if (CR.ExecutionCount != 0) + ++CoveredRegions; + } + + // Compute the line coverage + size_t NumLines = 0, CoveredLines = 0; + for (unsigned FileID = 0, E = Function.Filenames.size(); FileID < E; + ++FileID) { + // Find the line start and end of the function's source code + // in that particular file + unsigned LineStart = std::numeric_limits<unsigned>::max(); + unsigned LineEnd = 0; + for (auto &CR : Function.CountedRegions) { + if (CR.FileID != FileID) + continue; + LineStart = std::min(LineStart, CR.LineStart); + LineEnd = std::max(LineEnd, CR.LineEnd); + } + unsigned LineCount = LineEnd - LineStart + 1; + + // Get counters + llvm::SmallVector<uint64_t, 16> ExecutionCounts; + ExecutionCounts.resize(LineCount, 0); + for (auto &CR : Function.CountedRegions) { + if (CR.FileID != FileID) + continue; + // Ignore the lines that were skipped by the preprocessor. + auto ExecutionCount = CR.ExecutionCount; + if (CR.Kind == CounterMappingRegion::SkippedRegion) { + LineCount -= CR.LineEnd - CR.LineStart + 1; + ExecutionCount = 1; + } + for (unsigned I = CR.LineStart; I <= CR.LineEnd; ++I) + ExecutionCounts[I - LineStart] = ExecutionCount; + } + CoveredLines += LineCount - std::count(ExecutionCounts.begin(), + ExecutionCounts.end(), 0); + NumLines += LineCount; + } + return FunctionCoverageSummary( + Function.Name, Function.ExecutionCount, + RegionCoverageInfo(CoveredRegions, NumCodeRegions), + LineCoverageInfo(CoveredLines, 0, NumLines)); +} + +FileCoverageSummary +FileCoverageSummary::get(StringRef Name, + ArrayRef<FunctionCoverageSummary> FunctionSummaries) { + size_t NumRegions = 0, CoveredRegions = 0; + size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0; + size_t NumFunctionsExecuted = 0; + for (const auto &Func : FunctionSummaries) { + CoveredRegions += Func.RegionCoverage.Covered; + NumRegions += Func.RegionCoverage.NumRegions; + + CoveredLines += Func.LineCoverage.Covered; + NonCodeLines += Func.LineCoverage.NonCodeLines; + NumLines += Func.LineCoverage.NumLines; + + if (Func.ExecutionCount != 0) + ++NumFunctionsExecuted; + } + + return FileCoverageSummary( + Name, RegionCoverageInfo(CoveredRegions, NumRegions), + LineCoverageInfo(CoveredLines, NonCodeLines, NumLines), + FunctionCoverageInfo(NumFunctionsExecuted, FunctionSummaries.size()), + FunctionSummaries); +} diff --git a/tools/llvm-cov/CoverageSummaryInfo.h b/tools/llvm-cov/CoverageSummaryInfo.h new file mode 100644 index 0000000..0036032 --- /dev/null +++ b/tools/llvm-cov/CoverageSummaryInfo.h @@ -0,0 +1,133 @@ +//===- CoverageSummaryInfo.h - Coverage summary for function/file ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These structures are used to represent code coverage metrics +// for functions/files. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGESUMMARYINFO_H +#define LLVM_COV_COVERAGESUMMARYINFO_H + +#include "llvm/ProfileData/CoverageMapping.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +/// \brief Provides information about region coverage for a function/file. +struct RegionCoverageInfo { + /// \brief The number of regions that were executed at least once. + size_t Covered; + + /// \brief The number of regions that weren't executed. + size_t NotCovered; + + /// \brief The total number of regions in a function/file. + size_t NumRegions; + + RegionCoverageInfo(size_t Covered, size_t NumRegions) + : Covered(Covered), NotCovered(NumRegions - Covered), + NumRegions(NumRegions) {} + + bool isFullyCovered() const { return Covered == NumRegions; } + + double getPercentCovered() const { + return double(Covered) / double(NumRegions) * 100.0; + } +}; + +/// \brief Provides information about line coverage for a function/file. +struct LineCoverageInfo { + /// \brief The number of lines that were executed at least once. + size_t Covered; + + /// \brief The number of lines that weren't executed. + size_t NotCovered; + + /// \brief The number of lines that aren't code. + size_t NonCodeLines; + + /// \brief The total number of lines in a function/file. + size_t NumLines; + + LineCoverageInfo(size_t Covered, size_t NumNonCodeLines, size_t NumLines) + : Covered(Covered), NotCovered(NumLines - NumNonCodeLines - Covered), + NonCodeLines(NumNonCodeLines), NumLines(NumLines) {} + + bool isFullyCovered() const { return Covered == (NumLines - NonCodeLines); } + + double getPercentCovered() const { + return double(Covered) / double(NumLines - NonCodeLines) * 100.0; + } +}; + +/// \brief Provides information about function coverage for a file. +struct FunctionCoverageInfo { + /// \brief The number of functions that were executed. + size_t Executed; + + /// \brief The total number of functions in this file. + size_t NumFunctions; + + FunctionCoverageInfo(size_t Executed, size_t NumFunctions) + : Executed(Executed), NumFunctions(NumFunctions) {} + + bool isFullyCovered() const { return Executed == NumFunctions; } + + double getPercentCovered() const { + return double(Executed) / double(NumFunctions) * 100.0; + } +}; + +/// \brief A summary of function's code coverage. +struct FunctionCoverageSummary { + StringRef Name; + uint64_t ExecutionCount; + RegionCoverageInfo RegionCoverage; + LineCoverageInfo LineCoverage; + + FunctionCoverageSummary(StringRef Name, uint64_t ExecutionCount, + const RegionCoverageInfo &RegionCoverage, + const LineCoverageInfo &LineCoverage) + : Name(Name), ExecutionCount(ExecutionCount), + RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) { + } + + /// \brief Compute the code coverage summary for the given function coverage + /// mapping record. + static FunctionCoverageSummary + get(const coverage::FunctionRecord &Function); +}; + +/// \brief A summary of file's code coverage. +struct FileCoverageSummary { + StringRef Name; + RegionCoverageInfo RegionCoverage; + LineCoverageInfo LineCoverage; + FunctionCoverageInfo FunctionCoverage; + /// \brief The summary of every function + /// in this file. + ArrayRef<FunctionCoverageSummary> FunctionSummaries; + + FileCoverageSummary(StringRef Name, const RegionCoverageInfo &RegionCoverage, + const LineCoverageInfo &LineCoverage, + const FunctionCoverageInfo &FunctionCoverage, + ArrayRef<FunctionCoverageSummary> FunctionSummaries) + : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage), + FunctionCoverage(FunctionCoverage), + FunctionSummaries(FunctionSummaries) {} + + /// \brief Compute the code coverage summary for a file. + static FileCoverageSummary + get(StringRef Name, ArrayRef<FunctionCoverageSummary> FunctionSummaries); +}; + +} // namespace llvm + +#endif // LLVM_COV_COVERAGESUMMARYINFO_H diff --git a/tools/llvm-cov/CoverageViewOptions.h b/tools/llvm-cov/CoverageViewOptions.h new file mode 100644 index 0000000..94b55fe --- /dev/null +++ b/tools/llvm-cov/CoverageViewOptions.h @@ -0,0 +1,36 @@ +//===- CoverageViewOptions.h - Code coverage display options -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGEVIEWOPTIONS_H +#define LLVM_COV_COVERAGEVIEWOPTIONS_H + +#include "RenderingSupport.h" + +namespace llvm { + +/// \brief The options for displaying the code coverage information. +struct CoverageViewOptions { + bool Debug; + bool Colors; + bool ShowLineNumbers; + bool ShowLineStats; + bool ShowRegionMarkers; + bool ShowLineStatsOrRegionMarkers; + bool ShowExpandedRegions; + bool ShowFunctionInstantiations; + + /// \brief Change the output's stream color if the colors are enabled. + ColoredRawOstream colored_ostream(raw_ostream &OS, + raw_ostream::Colors Color) const { + return llvm::colored_ostream(OS, Color, Colors); + } +}; +} + +#endif // LLVM_COV_COVERAGEVIEWOPTIONS_H diff --git a/tools/llvm-cov/LLVMBuild.txt b/tools/llvm-cov/LLVMBuild.txt index 87e00d1..d6eb74d 100644 --- a/tools/llvm-cov/LLVMBuild.txt +++ b/tools/llvm-cov/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-cov parent = Tools -required_libraries = Instrumentation +required_libraries = ProfileData Support Instrumentation diff --git a/tools/llvm-cov/Makefile b/tools/llvm-cov/Makefile index efed6cc..6e32b4d 100644 --- a/tools/llvm-cov/Makefile +++ b/tools/llvm-cov/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-cov -LINK_COMPONENTS := core support +LINK_COMPONENTS := core support profiledata object # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-cov/RenderingSupport.h b/tools/llvm-cov/RenderingSupport.h new file mode 100644 index 0000000..0271329 --- /dev/null +++ b/tools/llvm-cov/RenderingSupport.h @@ -0,0 +1,60 @@ +//===- RenderingSupport.h - output stream rendering support functions ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_RENDERINGSUPPORT_H +#define LLVM_COV_RENDERINGSUPPORT_H + +#include "llvm/Support/raw_ostream.h" +#include <utility> + +namespace llvm { + +/// \brief A helper class that resets the output stream's color if needed +/// when destroyed. +class ColoredRawOstream { + ColoredRawOstream(const ColoredRawOstream &OS) LLVM_DELETED_FUNCTION; + +public: + raw_ostream &OS; + bool IsColorUsed; + + ColoredRawOstream(raw_ostream &OS, bool IsColorUsed) + : OS(OS), IsColorUsed(IsColorUsed) {} + + ColoredRawOstream(ColoredRawOstream &&Other) + : OS(Other.OS), IsColorUsed(Other.IsColorUsed) { + // Reset the other IsColorUsed so that the other object won't reset the + // color when destroyed. + Other.IsColorUsed = false; + } + + ~ColoredRawOstream() { + if (IsColorUsed) + OS.resetColor(); + } +}; + +template <typename T> +inline raw_ostream &operator<<(const ColoredRawOstream &OS, T &&Value) { + return OS.OS << std::forward<T>(Value); +} + +/// \brief Change the color of the output stream if the `IsColorUsed` flag +/// is true. Returns an object that resets the color when destroyed. +inline ColoredRawOstream colored_ostream(raw_ostream &OS, + raw_ostream::Colors Color, + bool IsColorUsed = true, + bool Bold = false, bool BG = false) { + if (IsColorUsed) + OS.changeColor(Color, Bold, BG); + return ColoredRawOstream(OS, IsColorUsed); +} +} + +#endif // LLVM_COV_RENDERINGSUPPORT_H diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp new file mode 100644 index 0000000..015099c --- /dev/null +++ b/tools/llvm-cov/SourceCoverageView.cpp @@ -0,0 +1,260 @@ +//===- SourceCoverageView.cpp - Code coverage view for source code --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements rendering for code coverage of source code. +// +//===----------------------------------------------------------------------===// + +#include "SourceCoverageView.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/LineIterator.h" + +using namespace llvm; + +void SourceCoverageView::renderLine( + raw_ostream &OS, StringRef Line, int64_t LineNumber, + const coverage::CoverageSegment *WrappedSegment, + ArrayRef<const coverage::CoverageSegment *> Segments, + unsigned ExpansionCol) { + Optional<raw_ostream::Colors> Highlight; + SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges; + + // The first segment overlaps from a previous line, so we treat it specially. + if (WrappedSegment && WrappedSegment->HasCount && WrappedSegment->Count == 0) + Highlight = raw_ostream::RED; + + // Output each segment of the line, possibly highlighted. + unsigned Col = 1; + for (const auto *S : Segments) { + unsigned End = std::min(S->Col, static_cast<unsigned>(Line.size()) + 1); + colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, + Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true) + << Line.substr(Col - 1, End - Col); + if (Options.Debug && Highlight) + HighlightedRanges.push_back(std::make_pair(Col, End)); + Col = End; + if (Col == ExpansionCol) + Highlight = raw_ostream::CYAN; + else if (S->HasCount && S->Count == 0) + Highlight = raw_ostream::RED; + else + Highlight = None; + } + + // Show the rest of the line + colored_ostream(OS, Highlight ? *Highlight : raw_ostream::SAVEDCOLOR, + Options.Colors && Highlight, /*Bold=*/false, /*BG=*/true) + << Line.substr(Col - 1, Line.size() - Col + 1); + OS << "\n"; + + if (Options.Debug) { + for (const auto &Range : HighlightedRanges) + errs() << "Highlighted line " << LineNumber << ", " << Range.first + << " -> " << Range.second << "\n"; + if (Highlight) + errs() << "Highlighted line " << LineNumber << ", " << Col << " -> ?\n"; + } +} + +void SourceCoverageView::renderIndent(raw_ostream &OS, unsigned Level) { + for (unsigned I = 0; I < Level; ++I) + OS << " |"; +} + +void SourceCoverageView::renderViewDivider(unsigned Level, unsigned Length, + raw_ostream &OS) { + assert(Level != 0 && "Cannot render divider at top level"); + renderIndent(OS, Level - 1); + OS.indent(2); + for (unsigned I = 0; I < Length; ++I) + OS << "-"; +} + +void +SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS, + const LineCoverageInfo &Line) { + if (!Line.isMapped()) { + OS.indent(LineCoverageColumnWidth) << '|'; + return; + } + SmallString<32> Buffer; + raw_svector_ostream BufferOS(Buffer); + BufferOS << Line.ExecutionCount; + auto Str = BufferOS.str(); + // Trim + Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth)); + // Align to the right + OS.indent(LineCoverageColumnWidth - Str.size()); + colored_ostream(OS, raw_ostream::MAGENTA, + Line.hasMultipleRegions() && Options.Colors) + << Str; + OS << '|'; +} + +void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS, + unsigned LineNo) { + SmallString<32> Buffer; + raw_svector_ostream BufferOS(Buffer); + BufferOS << LineNo; + auto Str = BufferOS.str(); + // Trim and align to the right + Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth)); + OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|'; +} + +void SourceCoverageView::renderRegionMarkers( + raw_ostream &OS, ArrayRef<const coverage::CoverageSegment *> Segments) { + SmallString<32> Buffer; + raw_svector_ostream BufferOS(Buffer); + + unsigned PrevColumn = 1; + for (const auto *S : Segments) { + if (!S->IsRegionEntry) + continue; + // Skip to the new region + if (S->Col > PrevColumn) + OS.indent(S->Col - PrevColumn); + PrevColumn = S->Col + 1; + BufferOS << S->Count; + StringRef Str = BufferOS.str(); + // Trim the execution count + Str = Str.substr(0, std::min(Str.size(), (size_t)7)); + PrevColumn += Str.size(); + OS << '^' << Str; + Buffer.clear(); + } + OS << "\n"; + + if (Options.Debug) + for (const auto *S : Segments) + errs() << "Marker at " << S->Line << ":" << S->Col << " = " << S->Count + << (S->IsRegionEntry ? "\n" : " (pop)\n"); +} + +void SourceCoverageView::render(raw_ostream &OS, bool WholeFile, + unsigned IndentLevel) { + // The width of the leading columns + unsigned CombinedColumnWidth = + (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) + + (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0); + // The width of the line that is used to divide between the view and the + // subviews. + unsigned DividerWidth = CombinedColumnWidth + 4; + + // We need the expansions and instantiations sorted so we can go through them + // while we iterate lines. + std::sort(ExpansionSubViews.begin(), ExpansionSubViews.end()); + std::sort(InstantiationSubViews.begin(), InstantiationSubViews.end()); + auto NextESV = ExpansionSubViews.begin(); + auto EndESV = ExpansionSubViews.end(); + auto NextISV = InstantiationSubViews.begin(); + auto EndISV = InstantiationSubViews.end(); + + // Get the coverage information for the file. + auto NextSegment = CoverageInfo.begin(); + auto EndSegment = CoverageInfo.end(); + + unsigned FirstLine = NextSegment != EndSegment ? NextSegment->Line : 0; + const coverage::CoverageSegment *WrappedSegment = nullptr; + SmallVector<const coverage::CoverageSegment *, 8> LineSegments; + for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) { + // If we aren't rendering the whole file, we need to filter out the prologue + // and epilogue. + if (!WholeFile) { + if (NextSegment == EndSegment) + break; + else if (LI.line_number() < FirstLine) + continue; + } + + // Collect the coverage information relevant to this line. + if (LineSegments.size()) + WrappedSegment = LineSegments.back(); + LineSegments.clear(); + while (NextSegment != EndSegment && NextSegment->Line == LI.line_number()) + LineSegments.push_back(&*NextSegment++); + + // Calculate a count to be for the line as a whole. + LineCoverageInfo LineCount; + if (WrappedSegment && WrappedSegment->HasCount) + LineCount.addRegionCount(WrappedSegment->Count); + for (const auto *S : LineSegments) + if (S->HasCount && S->IsRegionEntry) + LineCount.addRegionStartCount(S->Count); + + // Render the line prefix. + renderIndent(OS, IndentLevel); + if (Options.ShowLineStats) + renderLineCoverageColumn(OS, LineCount); + if (Options.ShowLineNumbers) + renderLineNumberColumn(OS, LI.line_number()); + + // If there are expansion subviews, we want to highlight the first one. + unsigned ExpansionColumn = 0; + if (NextESV != EndESV && NextESV->getLine() == LI.line_number() && + Options.Colors) + ExpansionColumn = NextESV->getStartCol(); + + // Display the source code for the current line. + renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments, + ExpansionColumn); + + // Show the region markers. + if (Options.ShowRegionMarkers && (!Options.ShowLineStatsOrRegionMarkers || + LineCount.hasMultipleRegions()) && + !LineSegments.empty()) { + renderIndent(OS, IndentLevel); + OS.indent(CombinedColumnWidth); + renderRegionMarkers(OS, LineSegments); + } + + // Show the expansions and instantiations for this line. + unsigned NestedIndent = IndentLevel + 1; + bool RenderedSubView = false; + for (; NextESV != EndESV && NextESV->getLine() == LI.line_number(); + ++NextESV) { + renderViewDivider(NestedIndent, DividerWidth, OS); + OS << "\n"; + if (RenderedSubView) { + // Re-render the current line and highlight the expansion range for + // this subview. + ExpansionColumn = NextESV->getStartCol(); + renderIndent(OS, IndentLevel); + OS.indent(CombinedColumnWidth + (IndentLevel == 0 ? 0 : 1)); + renderLine(OS, *LI, LI.line_number(), WrappedSegment, LineSegments, + ExpansionColumn); + renderViewDivider(NestedIndent, DividerWidth, OS); + OS << "\n"; + } + // Render the child subview + if (Options.Debug) + errs() << "Expansion at line " << NextESV->getLine() << ", " + << NextESV->getStartCol() << " -> " << NextESV->getEndCol() + << "\n"; + NextESV->View->render(OS, false, NestedIndent); + RenderedSubView = true; + } + for (; NextISV != EndISV && NextISV->Line == LI.line_number(); ++NextISV) { + renderViewDivider(NestedIndent, DividerWidth, OS); + OS << "\n"; + renderIndent(OS, NestedIndent); + OS << ' '; + Options.colored_ostream(OS, raw_ostream::CYAN) << NextISV->FunctionName + << ":"; + OS << "\n"; + NextISV->View->render(OS, false, NestedIndent); + RenderedSubView = true; + } + if (RenderedSubView) { + renderViewDivider(NestedIndent, DividerWidth, OS); + OS << "\n"; + } + } +} diff --git a/tools/llvm-cov/SourceCoverageView.h b/tools/llvm-cov/SourceCoverageView.h new file mode 100644 index 0000000..d92a748 --- /dev/null +++ b/tools/llvm-cov/SourceCoverageView.h @@ -0,0 +1,162 @@ +//===- SourceCoverageView.h - Code coverage view for source code ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements rendering for code coverage of source code. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_SOURCECOVERAGEVIEW_H +#define LLVM_COV_SOURCECOVERAGEVIEW_H + +#include "CoverageViewOptions.h" +#include "llvm/ProfileData/CoverageMapping.h" +#include "llvm/Support/MemoryBuffer.h" +#include <vector> + +namespace llvm { + +class SourceCoverageView; + +/// \brief A view that represents a macro or include expansion +struct ExpansionView { + coverage::CounterMappingRegion Region; + std::unique_ptr<SourceCoverageView> View; + + ExpansionView(const coverage::CounterMappingRegion &Region, + std::unique_ptr<SourceCoverageView> View) + : Region(Region), View(std::move(View)) {} + ExpansionView(ExpansionView &&RHS) + : Region(std::move(RHS.Region)), View(std::move(RHS.View)) {} + ExpansionView &operator=(ExpansionView &&RHS) { + Region = std::move(RHS.Region); + View = std::move(RHS.View); + return *this; + } + + unsigned getLine() const { return Region.LineStart; } + unsigned getStartCol() const { return Region.ColumnStart; } + unsigned getEndCol() const { return Region.ColumnEnd; } + + friend bool operator<(const ExpansionView &LHS, const ExpansionView &RHS) { + return LHS.Region.startLoc() < RHS.Region.startLoc(); + } +}; + +/// \brief A view that represents a function instantiation +struct InstantiationView { + StringRef FunctionName; + unsigned Line; + std::unique_ptr<SourceCoverageView> View; + + InstantiationView(StringRef FunctionName, unsigned Line, + std::unique_ptr<SourceCoverageView> View) + : FunctionName(FunctionName), Line(Line), View(std::move(View)) {} + InstantiationView(InstantiationView &&RHS) + : FunctionName(std::move(RHS.FunctionName)), Line(std::move(RHS.Line)), + View(std::move(RHS.View)) {} + InstantiationView &operator=(InstantiationView &&RHS) { + FunctionName = std::move(RHS.FunctionName); + Line = std::move(RHS.Line); + View = std::move(RHS.View); + return *this; + } + + friend bool operator<(const InstantiationView &LHS, + const InstantiationView &RHS) { + return LHS.Line < RHS.Line; + } +}; + +/// \brief A code coverage view of a specific source file. +/// It can have embedded coverage views. +class SourceCoverageView { +private: + /// \brief Coverage information for a single line. + struct LineCoverageInfo { + uint64_t ExecutionCount; + unsigned RegionCount; + bool Mapped; + + LineCoverageInfo() : ExecutionCount(0), RegionCount(0), Mapped(false) {} + + bool isMapped() const { return Mapped; } + + bool hasMultipleRegions() const { return RegionCount > 1; } + + void addRegionStartCount(uint64_t Count) { + Mapped = true; + ExecutionCount = Count; + ++RegionCount; + } + + void addRegionCount(uint64_t Count) { + Mapped = true; + if (!RegionCount) + ExecutionCount = Count; + } + }; + + const MemoryBuffer &File; + const CoverageViewOptions &Options; + coverage::CoverageData CoverageInfo; + std::vector<ExpansionView> ExpansionSubViews; + std::vector<InstantiationView> InstantiationSubViews; + + /// \brief Render a source line with highlighting. + void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber, + const coverage::CoverageSegment *WrappedSegment, + ArrayRef<const coverage::CoverageSegment *> Segments, + unsigned ExpansionCol); + + void renderIndent(raw_ostream &OS, unsigned Level); + + void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS); + + /// \brief Render the line's execution count column. + void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line); + + /// \brief Render the line number column. + void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo); + + /// \brief Render all the region's execution counts on a line. + void + renderRegionMarkers(raw_ostream &OS, + ArrayRef<const coverage::CoverageSegment *> Segments); + + static const unsigned LineCoverageColumnWidth = 7; + static const unsigned LineNumberColumnWidth = 5; + +public: + SourceCoverageView(const MemoryBuffer &File, + const CoverageViewOptions &Options, + coverage::CoverageData &&CoverageInfo) + : File(File), Options(Options), CoverageInfo(std::move(CoverageInfo)) {} + + const CoverageViewOptions &getOptions() const { return Options; } + + /// \brief Add an expansion subview to this view. + void addExpansion(const coverage::CounterMappingRegion &Region, + std::unique_ptr<SourceCoverageView> View) { + ExpansionSubViews.emplace_back(Region, std::move(View)); + } + + /// \brief Add a function instantiation subview to this view. + void addInstantiation(StringRef FunctionName, unsigned Line, + std::unique_ptr<SourceCoverageView> View) { + InstantiationSubViews.emplace_back(FunctionName, Line, std::move(View)); + } + + /// \brief Print the code coverage information for a specific + /// portion of a source file to the output stream. + void render(raw_ostream &OS, bool WholeFile, unsigned IndentLevel = 0); +}; + +} // namespace llvm + +#endif // LLVM_COV_SOURCECOVERAGEVIEW_H diff --git a/tools/llvm-cov/TestingSupport.cpp b/tools/llvm-cov/TestingSupport.cpp new file mode 100644 index 0000000..537f133 --- /dev/null +++ b/tools/llvm-cov/TestingSupport.cpp @@ -0,0 +1,91 @@ +//===- TestingSupport.cpp - Convert objects files into test files --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryObject.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/PrettyStackTrace.h" +#include <system_error> +#include <functional> + +using namespace llvm; +using namespace object; + +int convertForTestingMain(int argc, const char *argv[]) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::opt<std::string> InputSourceFile(cl::Positional, cl::Required, + cl::desc("<Source file>")); + + cl::opt<std::string> OutputFilename( + "o", cl::Required, + cl::desc( + "File with the profile data obtained after an instrumented run")); + + cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); + + auto ObjErr = llvm::object::ObjectFile::createObjectFile(InputSourceFile); + if (auto Err = ObjErr.getError()) { + errs() << "error: " << Err.message() << "\n"; + return 1; + } + ObjectFile *OF = ObjErr.get().getBinary(); + auto BytesInAddress = OF->getBytesInAddress(); + if (BytesInAddress != 8) { + errs() << "error: 64 bit binary expected\n"; + return 1; + } + + // Look for the sections that we are interested in. + int FoundSectionCount = 0; + SectionRef ProfileNames, CoverageMapping; + for (const auto &Section : OF->sections()) { + StringRef Name; + if (Section.getName(Name)) + return 1; + if (Name == "__llvm_prf_names") { + ProfileNames = Section; + } else if (Name == "__llvm_covmap") { + CoverageMapping = Section; + } else + continue; + ++FoundSectionCount; + } + if (FoundSectionCount != 2) + return 1; + + // Get the contents of the given sections. + uint64_t ProfileNamesAddress = ProfileNames.getAddress(); + StringRef CoverageMappingData; + StringRef ProfileNamesData; + if (CoverageMapping.getContents(CoverageMappingData) || + ProfileNames.getContents(ProfileNamesData)) + return 1; + + int FD; + if (auto Err = + sys::fs::openFileForWrite(OutputFilename, FD, sys::fs::F_None)) { + errs() << "error: " << Err.message() << "\n"; + return 1; + } + + raw_fd_ostream OS(FD, true); + OS << "llvmcovmtestdata"; + encodeULEB128(ProfileNamesData.size(), OS); + encodeULEB128(ProfileNamesAddress, OS); + OS << ProfileNamesData << CoverageMappingData; + + return 0; +} diff --git a/tools/llvm-cov/gcov.cpp b/tools/llvm-cov/gcov.cpp new file mode 100644 index 0000000..4c9195a --- /dev/null +++ b/tools/llvm-cov/gcov.cpp @@ -0,0 +1,153 @@ +//===- gcov.cpp - GCOV compatible LLVM coverage tool ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// llvm-cov is a command line tools to analyze and report coverage information. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/GCOV.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryObject.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include <system_error> +using namespace llvm; + +void reportCoverage(StringRef SourceFile, StringRef ObjectDir, + const std::string &InputGCNO, const std::string &InputGCDA, + bool DumpGCOV, const GCOVOptions &Options) { + SmallString<128> CoverageFileStem(ObjectDir); + if (CoverageFileStem.empty()) { + // If no directory was specified with -o, look next to the source file. + CoverageFileStem = sys::path::parent_path(SourceFile); + sys::path::append(CoverageFileStem, sys::path::stem(SourceFile)); + } else if (sys::fs::is_directory(ObjectDir)) + // A directory name was given. Use it and the source file name. + sys::path::append(CoverageFileStem, sys::path::stem(SourceFile)); + else + // A file was given. Ignore the source file and look next to this file. + sys::path::replace_extension(CoverageFileStem, ""); + + std::string GCNO = InputGCNO.empty() + ? std::string(CoverageFileStem.str()) + ".gcno" + : InputGCNO; + std::string GCDA = InputGCDA.empty() + ? std::string(CoverageFileStem.str()) + ".gcda" + : InputGCDA; + GCOVFile GF; + + ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff = + MemoryBuffer::getFileOrSTDIN(GCNO); + if (std::error_code EC = GCNO_Buff.getError()) { + errs() << GCNO << ": " << EC.message() << "\n"; + return; + } + GCOVBuffer GCNO_GB(GCNO_Buff.get().get()); + if (!GF.readGCNO(GCNO_GB)) { + errs() << "Invalid .gcno File!\n"; + return; + } + + ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff = + MemoryBuffer::getFileOrSTDIN(GCDA); + if (std::error_code EC = GCDA_Buff.getError()) { + if (EC != errc::no_such_file_or_directory) { + errs() << GCDA << ": " << EC.message() << "\n"; + return; + } + // Clear the filename to make it clear we didn't read anything. + GCDA = "-"; + } else { + GCOVBuffer GCDA_GB(GCDA_Buff.get().get()); + if (!GF.readGCDA(GCDA_GB)) { + errs() << "Invalid .gcda File!\n"; + return; + } + } + + if (DumpGCOV) + GF.dump(); + + FileInfo FI(Options); + GF.collectLineCounts(FI); + FI.print(SourceFile, GCNO, GCDA); +} + +int gcovMain(int argc, const char *argv[]) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::list<std::string> SourceFiles(cl::Positional, cl::OneOrMore, + cl::desc("SOURCEFILE")); + + cl::opt<bool> AllBlocks("a", cl::Grouping, cl::init(false), + cl::desc("Display all basic blocks")); + cl::alias AllBlocksA("all-blocks", cl::aliasopt(AllBlocks)); + + cl::opt<bool> BranchProb("b", cl::Grouping, cl::init(false), + cl::desc("Display branch probabilities")); + cl::alias BranchProbA("branch-probabilities", cl::aliasopt(BranchProb)); + + cl::opt<bool> BranchCount("c", cl::Grouping, cl::init(false), + cl::desc("Display branch counts instead " + "of percentages (requires -b)")); + cl::alias BranchCountA("branch-counts", cl::aliasopt(BranchCount)); + + cl::opt<bool> LongNames("l", cl::Grouping, cl::init(false), + cl::desc("Prefix filenames with the main file")); + cl::alias LongNamesA("long-file-names", cl::aliasopt(LongNames)); + + cl::opt<bool> FuncSummary("f", cl::Grouping, cl::init(false), + cl::desc("Show coverage for each function")); + cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary)); + + cl::opt<bool> NoOutput("n", cl::Grouping, cl::init(false), + cl::desc("Do not output any .gcov files")); + cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput)); + + cl::opt<std::string> ObjectDir( + "o", cl::value_desc("DIR|FILE"), cl::init(""), + cl::desc("Find objects in DIR or based on FILE's path")); + cl::alias ObjectDirA("object-directory", cl::aliasopt(ObjectDir)); + cl::alias ObjectDirB("object-file", cl::aliasopt(ObjectDir)); + + cl::opt<bool> PreservePaths("p", cl::Grouping, cl::init(false), + cl::desc("Preserve path components")); + cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths)); + + cl::opt<bool> UncondBranch("u", cl::Grouping, cl::init(false), + cl::desc("Display unconditional branch info " + "(requires -b)")); + cl::alias UncondBranchA("unconditional-branches", cl::aliasopt(UncondBranch)); + + cl::OptionCategory DebugCat("Internal and debugging options"); + cl::opt<bool> DumpGCOV("dump", cl::init(false), cl::cat(DebugCat), + cl::desc("Dump the gcov file to stderr")); + cl::opt<std::string> InputGCNO("gcno", cl::cat(DebugCat), cl::init(""), + cl::desc("Override inferred gcno file")); + cl::opt<std::string> InputGCDA("gcda", cl::cat(DebugCat), cl::init(""), + cl::desc("Override inferred gcda file")); + + cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); + + GCOVOptions Options(AllBlocks, BranchProb, BranchCount, FuncSummary, + PreservePaths, UncondBranch, LongNames, NoOutput); + + for (const auto &SourceFile : SourceFiles) + reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV, + Options); + return 0; +} diff --git a/tools/llvm-cov/llvm-cov.cpp b/tools/llvm-cov/llvm-cov.cpp index 18cc1b1..a67859e 100644 --- a/tools/llvm-cov/llvm-cov.cpp +++ b/tools/llvm-cov/llvm-cov.cpp @@ -11,140 +11,68 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/GCOV.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/MemoryObject.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Support/Path.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Signals.h" -#include <system_error> -using namespace llvm; - -static cl::list<std::string> SourceFiles(cl::Positional, cl::OneOrMore, - cl::desc("SOURCEFILE")); - -static cl::opt<bool> AllBlocks("a", cl::Grouping, cl::init(false), - cl::desc("Display all basic blocks")); -static cl::alias AllBlocksA("all-blocks", cl::aliasopt(AllBlocks)); - -static cl::opt<bool> BranchProb("b", cl::Grouping, cl::init(false), - cl::desc("Display branch probabilities")); -static cl::alias BranchProbA("branch-probabilities", cl::aliasopt(BranchProb)); - -static cl::opt<bool> BranchCount("c", cl::Grouping, cl::init(false), - cl::desc("Display branch counts instead " - "of percentages (requires -b)")); -static cl::alias BranchCountA("branch-counts", cl::aliasopt(BranchCount)); - -static cl::opt<bool> LongNames("l", cl::Grouping, cl::init(false), - cl::desc("Prefix filenames with the main file")); -static cl::alias LongNamesA("long-file-names", cl::aliasopt(LongNames)); +#include <string> -static cl::opt<bool> FuncSummary("f", cl::Grouping, cl::init(false), - cl::desc("Show coverage for each function")); -static cl::alias FuncSummaryA("function-summaries", cl::aliasopt(FuncSummary)); - -static cl::opt<bool> NoOutput("n", cl::Grouping, cl::init(false), - cl::desc("Do not output any .gcov files")); -static cl::alias NoOutputA("no-output", cl::aliasopt(NoOutput)); - -static cl::opt<std::string> -ObjectDir("o", cl::value_desc("DIR|FILE"), cl::init(""), - cl::desc("Find objects in DIR or based on FILE's path")); -static cl::alias ObjectDirA("object-directory", cl::aliasopt(ObjectDir)); -static cl::alias ObjectDirB("object-file", cl::aliasopt(ObjectDir)); - -static cl::opt<bool> PreservePaths("p", cl::Grouping, cl::init(false), - cl::desc("Preserve path components")); -static cl::alias PreservePathsA("preserve-paths", cl::aliasopt(PreservePaths)); +using namespace llvm; -static cl::opt<bool> UncondBranch("u", cl::Grouping, cl::init(false), - cl::desc("Display unconditional branch info " - "(requires -b)")); -static cl::alias UncondBranchA("unconditional-branches", - cl::aliasopt(UncondBranch)); +/// \brief The main entry point for the 'show' subcommand. +int showMain(int argc, const char *argv[]); -static cl::OptionCategory DebugCat("Internal and debugging options"); -static cl::opt<bool> DumpGCOV("dump", cl::init(false), cl::cat(DebugCat), - cl::desc("Dump the gcov file to stderr")); -static cl::opt<std::string> InputGCNO("gcno", cl::cat(DebugCat), cl::init(""), - cl::desc("Override inferred gcno file")); -static cl::opt<std::string> InputGCDA("gcda", cl::cat(DebugCat), cl::init(""), - cl::desc("Override inferred gcda file")); +/// \brief The main entry point for the 'report' subcommand. +int reportMain(int argc, const char *argv[]); -void reportCoverage(StringRef SourceFile) { - SmallString<128> CoverageFileStem(ObjectDir); - if (CoverageFileStem.empty()) { - // If no directory was specified with -o, look next to the source file. - CoverageFileStem = sys::path::parent_path(SourceFile); - sys::path::append(CoverageFileStem, sys::path::stem(SourceFile)); - } else if (sys::fs::is_directory(ObjectDir)) - // A directory name was given. Use it and the source file name. - sys::path::append(CoverageFileStem, sys::path::stem(SourceFile)); - else - // A file was given. Ignore the source file and look next to this file. - sys::path::replace_extension(CoverageFileStem, ""); +/// \brief The main entry point for the 'convert-for-testing' subcommand. +int convertForTestingMain(int argc, const char *argv[]); - std::string GCNO = InputGCNO.empty() - ? std::string(CoverageFileStem.str()) + ".gcno" - : InputGCNO; - std::string GCDA = InputGCDA.empty() - ? std::string(CoverageFileStem.str()) + ".gcda" - : InputGCDA; - GCOVFile GF; +/// \brief The main entry point for the gcov compatible coverage tool. +int gcovMain(int argc, const char *argv[]); - ErrorOr<std::unique_ptr<MemoryBuffer>> GCNO_Buff = - MemoryBuffer::getFileOrSTDIN(GCNO); - if (std::error_code EC = GCNO_Buff.getError()) { - errs() << GCNO << ": " << EC.message() << "\n"; - return; - } - GCOVBuffer GCNO_GB(GCNO_Buff.get().get()); - if (!GF.readGCNO(GCNO_GB)) { - errs() << "Invalid .gcno File!\n"; - return; - } +/// \brief Top level help. +int helpMain(int argc, const char *argv[]) { + errs() << "OVERVIEW: LLVM code coverage tool\n\n" + << "USAGE: llvm-cov {gcov|report|show}\n"; + return 0; +} - ErrorOr<std::unique_ptr<MemoryBuffer>> GCDA_Buff = - MemoryBuffer::getFileOrSTDIN(GCDA); - if (std::error_code EC = GCDA_Buff.getError()) { - if (EC != errc::no_such_file_or_directory) { - errs() << GCDA << ": " << EC.message() << "\n"; - return; - } - // Clear the filename to make it clear we didn't read anything. - GCDA = "-"; - } else { - GCOVBuffer GCDA_GB(GCDA_Buff.get().get()); - if (!GF.readGCDA(GCDA_GB)) { - errs() << "Invalid .gcda File!\n"; - return; +int main(int argc, const char **argv) { + // If argv[0] is or ends with 'gcov', always be gcov compatible + if (sys::path::stem(argv[0]).endswith_lower("gcov")) + return gcovMain(argc, argv); + + // Check if we are invoking a specific tool command. + if (argc > 1) { + typedef int (*MainFunction)(int, const char *[]); + MainFunction Func = StringSwitch<MainFunction>(argv[1]) + .Case("convert-for-testing", convertForTestingMain) + .Case("gcov", gcovMain) + .Case("report", reportMain) + .Case("show", showMain) + .Cases("-h", "-help", "--help", helpMain) + .Default(nullptr); + + if (Func) { + std::string Invocation = std::string(argv[0]) + " " + argv[1]; + argv[1] = Invocation.c_str(); + return Func(argc - 1, argv + 1); } } - if (DumpGCOV) - GF.dump(); - - GCOVOptions Options(AllBlocks, BranchProb, BranchCount, FuncSummary, - PreservePaths, UncondBranch, LongNames, NoOutput); - FileInfo FI(Options); - GF.collectLineCounts(FI); - FI.print(SourceFile, GCNO, GCDA); -} - -int main(int argc, char **argv) { - // Print a stack trace if we signal out. - sys::PrintStackTraceOnErrorSignal(); - PrettyStackTraceProgram X(argc, argv); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. - - cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); - - for (const auto &SourceFile : SourceFiles) - reportCoverage(SourceFile); - return 0; + // Give a warning and fall back to gcov + errs().changeColor(raw_ostream::RED); + errs() << "warning:"; + // Assume that argv[1] wasn't a command when it stats with a '-' or is a + // filename (i.e. contains a '.') + if (argc > 1 && !StringRef(argv[1]).startswith("-") && + StringRef(argv[1]).find(".") == StringRef::npos) + errs() << " Unrecognized command '" << argv[1] << "'."; + errs() << " Using the gcov compatible mode " + "(this behaviour may be dropped in the future)."; + errs().resetColor(); + errs() << "\n"; + + return gcovMain(argc, argv); } diff --git a/tools/llvm-diff/DiffConsumer.h b/tools/llvm-diff/DiffConsumer.h index ac13a5e..855f688 100644 --- a/tools/llvm-diff/DiffConsumer.h +++ b/tools/llvm-diff/DiffConsumer.h @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#ifndef _LLVM_DIFFCONSUMER_H_ -#define _LLVM_DIFFCONSUMER_H_ +#ifndef LLVM_TOOLS_LLVM_DIFF_DIFFCONSUMER_H +#define LLVM_TOOLS_LLVM_DIFF_DIFFCONSUMER_H #include "DiffLog.h" #include "llvm/ADT/DenseMap.h" diff --git a/tools/llvm-diff/DiffLog.h b/tools/llvm-diff/DiffLog.h index 43e318a..8eb53ff 100644 --- a/tools/llvm-diff/DiffLog.h +++ b/tools/llvm-diff/DiffLog.h @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#ifndef _LLVM_DIFFLOG_H_ -#define _LLVM_DIFFLOG_H_ +#ifndef LLVM_TOOLS_LLVM_DIFF_DIFFLOG_H +#define LLVM_TOOLS_LLVM_DIFF_DIFFLOG_H #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" diff --git a/tools/llvm-diff/DifferenceEngine.h b/tools/llvm-diff/DifferenceEngine.h index 4470968..f0d8311 100644 --- a/tools/llvm-diff/DifferenceEngine.h +++ b/tools/llvm-diff/DifferenceEngine.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef _LLVM_DIFFERENCE_ENGINE_H_ -#define _LLVM_DIFFERENCE_ENGINE_H_ +#ifndef LLVM_TOOLS_LLVM_DIFF_DIFFERENCEENGINE_H +#define LLVM_TOOLS_LLVM_DIFF_DIFFERENCEENGINE_H #include "DiffConsumer.h" #include "DiffLog.h" diff --git a/tools/llvm-diff/llvm-diff.cpp b/tools/llvm-diff/llvm-diff.cpp index f70219e..ae58f5c 100644 --- a/tools/llvm-diff/llvm-diff.cpp +++ b/tools/llvm-diff/llvm-diff.cpp @@ -32,21 +32,22 @@ using namespace llvm; /// Reads a module from a file. On error, messages are written to stderr /// and null is returned. -static Module *ReadModule(LLVMContext &Context, StringRef Name) { +static std::unique_ptr<Module> readModule(LLVMContext &Context, + StringRef Name) { SMDiagnostic Diag; - Module *M = ParseIRFile(Name, Diag, Context); + std::unique_ptr<Module> M = parseIRFile(Name, Diag, Context); if (!M) Diag.print("llvm-diff", errs()); return M; } -static void diffGlobal(DifferenceEngine &Engine, Module *L, Module *R, +static void diffGlobal(DifferenceEngine &Engine, Module &L, Module &R, StringRef Name) { // Drop leading sigils from the global name. if (Name.startswith("@")) Name = Name.substr(1); - Function *LFn = L->getFunction(Name); - Function *RFn = R->getFunction(Name); + Function *LFn = L.getFunction(Name); + Function *RFn = R.getFunction(Name); if (LFn && RFn) Engine.diff(LFn, RFn); else if (!LFn && !RFn) @@ -72,8 +73,8 @@ int main(int argc, char **argv) { LLVMContext Context; // Load both modules. Die if that fails. - Module *LModule = ReadModule(Context, LeftFilename); - Module *RModule = ReadModule(Context, RightFilename); + std::unique_ptr<Module> LModule = readModule(Context, LeftFilename); + std::unique_ptr<Module> RModule = readModule(Context, RightFilename); if (!LModule || !RModule) return 1; DiffConsumer Consumer; @@ -82,15 +83,12 @@ int main(int argc, char **argv) { // If any global names were given, just diff those. if (!GlobalsToCompare.empty()) { for (unsigned I = 0, E = GlobalsToCompare.size(); I != E; ++I) - diffGlobal(Engine, LModule, RModule, GlobalsToCompare[I]); + diffGlobal(Engine, *LModule, *RModule, GlobalsToCompare[I]); // Otherwise, diff everything in the module. } else { - Engine.diff(LModule, RModule); + Engine.diff(LModule.get(), RModule.get()); } - delete LModule; - delete RModule; - return Consumer.hadDifferences(); } diff --git a/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp index 3b0f838..fb73717 100644 --- a/tools/llvm-dis/llvm-dis.cpp +++ b/tools/llvm-dis/llvm-dis.cpp @@ -171,11 +171,11 @@ int main(int argc, char **argv) { } } - std::string ErrorInfo; + std::error_code EC; std::unique_ptr<tool_output_file> Out( - new tool_output_file(OutputFilename.c_str(), ErrorInfo, sys::fs::F_None)); - if (!ErrorInfo.empty()) { - errs() << ErrorInfo << '\n'; + new tool_output_file(OutputFilename, EC, sys::fs::F_None)); + if (EC) { + errs() << EC.message() << '\n'; return 1; } diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index f44b0e3..1c540c9 100644 --- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -45,6 +45,10 @@ DumpType("debug-dump", cl::init(DIDT_All), clEnumValN(DIDT_All, "all", "Dump all debug sections"), clEnumValN(DIDT_Abbrev, "abbrev", ".debug_abbrev"), clEnumValN(DIDT_AbbrevDwo, "abbrev.dwo", ".debug_abbrev.dwo"), + clEnumValN(DIDT_AppleNames, "apple_names", ".apple_names"), + clEnumValN(DIDT_AppleTypes, "apple_types", ".apple_types"), + clEnumValN(DIDT_AppleNamespaces, "apple_namespaces", ".apple_namespaces"), + clEnumValN(DIDT_AppleObjC, "apple_objc", ".apple_objc"), clEnumValN(DIDT_Aranges, "aranges", ".debug_aranges"), clEnumValN(DIDT_Info, "info", ".debug_info"), clEnumValN(DIDT_InfoDwo, "info.dwo", ".debug_info.dwo"), @@ -65,26 +69,28 @@ DumpType("debug-dump", cl::init(DIDT_All), clEnumValN(DIDT_StrOffsetsDwo, "str_offsets.dwo", ".debug_str_offsets.dwo"), clEnumValEnd)); -static void DumpInput(const StringRef &Filename) { - ErrorOr<std::unique_ptr<MemoryBuffer>> Buff = +static void DumpInput(StringRef Filename) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = MemoryBuffer::getFileOrSTDIN(Filename); - if (std::error_code EC = Buff.getError()) { + if (std::error_code EC = BuffOrErr.getError()) { errs() << Filename << ": " << EC.message() << "\n"; return; } + std::unique_ptr<MemoryBuffer> Buff = std::move(BuffOrErr.get()); - ErrorOr<ObjectFile *> ObjOrErr(ObjectFile::createObjectFile(Buff.get())); + ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr = + ObjectFile::createObjectFile(Buff->getMemBufferRef()); if (std::error_code EC = ObjOrErr.getError()) { errs() << Filename << ": " << EC.message() << '\n'; return; } - std::unique_ptr<ObjectFile> Obj(ObjOrErr.get()); + ObjectFile &Obj = *ObjOrErr.get(); - std::unique_ptr<DIContext> DICtx(DIContext::getDWARFContext(Obj.get())); + std::unique_ptr<DIContext> DICtx(DIContext::getDWARFContext(Obj)); outs() << Filename - << ":\tfile format " << Obj->getFileFormatName() << "\n\n"; + << ":\tfile format " << Obj.getFileFormatName() << "\n\n"; // Dump the complete DWARF structure. DICtx->dump(outs(), DumpType); } diff --git a/tools/llvm-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp index 0f70868..53b2f0d 100644 --- a/tools/llvm-extract/llvm-extract.cpp +++ b/tools/llvm-extract/llvm-extract.cpp @@ -101,8 +101,7 @@ int main(int argc, char **argv) { // Use lazy loading, since we only care about selected global values. SMDiagnostic Err; - std::unique_ptr<Module> M; - M.reset(getLazyIRFileModule(InputFilename, Err, Context)); + std::unique_ptr<Module> M = getLazyIRFileModule(InputFilename, Err, Context); if (!M.get()) { Err.print(argv[0], errs()); @@ -217,31 +216,28 @@ int main(int argc, char **argv) { if (!DeleteFn) for (size_t i = 0, e = GVs.size(); i != e; ++i) { GlobalValue *GV = GVs[i]; - if (GV->isMaterializable()) { - std::string ErrInfo; - if (GV->Materialize(&ErrInfo)) { - errs() << argv[0] << ": error reading input: " << ErrInfo << "\n"; - return 1; - } + if (std::error_code EC = GV->materialize()) { + errs() << argv[0] << ": error reading input: " << EC.message() << "\n"; + return 1; } } else { // Deleting. Materialize every GV that's *not* in GVs. SmallPtrSet<GlobalValue *, 8> GVSet(GVs.begin(), GVs.end()); for (auto &G : M->globals()) { - if (!GVSet.count(&G) && G.isMaterializable()) { - std::string ErrInfo; - if (G.Materialize(&ErrInfo)) { - errs() << argv[0] << ": error reading input: " << ErrInfo << "\n"; + if (!GVSet.count(&G)) { + if (std::error_code EC = G.materialize()) { + errs() << argv[0] << ": error reading input: " << EC.message() + << "\n"; return 1; } } } for (auto &F : *M) { - if (!GVSet.count(&F) && F.isMaterializable()) { - std::string ErrInfo; - if (F.Materialize(&ErrInfo)) { - errs() << argv[0] << ": error reading input: " << ErrInfo << "\n"; + if (!GVSet.count(&F)) { + if (std::error_code EC = F.materialize()) { + errs() << argv[0] << ": error reading input: " << EC.message() + << "\n"; return 1; } } @@ -251,7 +247,7 @@ int main(int argc, char **argv) { // In addition to deleting all other functions, we also want to spiff it // up a little bit. Do this now. PassManager Passes; - Passes.add(new DataLayoutPass(M.get())); // Use correct DataLayout + Passes.add(new DataLayoutPass()); // Use correct DataLayout std::vector<GlobalValue*> Gvs(GVs.begin(), GVs.end()); @@ -261,10 +257,10 @@ int main(int argc, char **argv) { Passes.add(createStripDeadDebugInfoPass()); // Remove dead debug info Passes.add(createStripDeadPrototypesPass()); // Remove dead func decls - std::string ErrorInfo; - tool_output_file Out(OutputFilename.c_str(), ErrorInfo, sys::fs::F_None); - if (!ErrorInfo.empty()) { - errs() << ErrorInfo << '\n'; + std::error_code EC; + tool_output_file Out(OutputFilename, EC, sys::fs::F_None); + if (EC) { + errs() << EC.message() << '\n'; return 1; } diff --git a/tools/llvm-go/CMakeLists.txt b/tools/llvm-go/CMakeLists.txt new file mode 100644 index 0000000..20393f7 --- /dev/null +++ b/tools/llvm-go/CMakeLists.txt @@ -0,0 +1,9 @@ +if(LLVM_BINDINGS MATCHES "go") + set(binpath ${CMAKE_BINARY_DIR}/bin/llvm-go${CMAKE_EXECUTABLE_SUFFIX}) + add_custom_command(OUTPUT ${binpath} + COMMAND ${GO_EXECUTABLE} build -o ${binpath} llvm-go.go + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/llvm-go.go + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Building Go executable llvm-go") + add_custom_target(llvm-go ALL DEPENDS ${binpath}) +endif() diff --git a/tools/llvm-go/Makefile b/tools/llvm-go/Makefile new file mode 100644 index 0000000..4465b2a --- /dev/null +++ b/tools/llvm-go/Makefile @@ -0,0 +1,16 @@ +##===- tools/llvm-go/Makefile ------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL := ../.. +include $(LEVEL)/Makefile.common + +all:: $(ToolDir)/llvm-go$(EXEEXT) + +$(ToolDir)/llvm-go$(EXEEXT): $(PROJ_SRC_DIR)/llvm-go.go + $(GO) build -o $@ $< diff --git a/tools/llvm-go/llvm-go.go b/tools/llvm-go/llvm-go.go new file mode 100644 index 0000000..47f9481 --- /dev/null +++ b/tools/llvm-go/llvm-go.go @@ -0,0 +1,261 @@ +//===-- llvm-go.go - go tool wrapper for LLVM -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tool lets us build LLVM components within the tree by setting up a +// $GOPATH that resembles a tree fetched in the normal way with "go get". +// +//===----------------------------------------------------------------------===// + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" +) + +type pkg struct { + llvmpath, pkgpath string +} + +var packages = []pkg{ + {"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"}, +} + +type compilerFlags struct { + cpp, cxx, ld string +} + +var components = []string{ + "all-targets", + "analysis", + "asmparser", + "asmprinter", + "bitreader", + "bitwriter", + "codegen", + "core", + "debuginfo", + "executionengine", + "instrumentation", + "interpreter", + "ipo", + "irreader", + "linker", + "mc", + "mcjit", + "objcarcopts", + "option", + "profiledata", + "scalaropts", + "support", + "target", +} + +func llvmConfig(args ...string) string { + configpath := os.Getenv("LLVM_CONFIG") + if configpath == "" { + // strip llvm-go, add llvm-config + configpath = os.Args[0][:len(os.Args[0])-7] + "llvm-config" + } + + cmd := exec.Command(configpath, args...) + out, err := cmd.Output() + if err != nil { + panic(err.Error()) + } + + outstr := string(out) + outstr = strings.TrimSuffix(outstr, "\n") + return strings.Replace(outstr, "\n", " ", -1) +} + +func llvmFlags() compilerFlags { + ldflags := llvmConfig(append([]string{"--ldflags", "--libs", "--system-libs"}, components...)...) + if runtime.GOOS != "darwin" { + // OS X doesn't like -rpath with cgo. See: + // https://code.google.com/p/go/issues/detail?id=7293 + ldflags = "-Wl,-rpath," + llvmConfig("--libdir") + " " + ldflags + } + return compilerFlags{ + cpp: llvmConfig("--cppflags"), + cxx: "-std=c++11", + ld: ldflags, + } +} + +func addTag(args []string, tag string) []string { + args = append([]string{}, args...) + addedTag := false + for i, a := range args { + if strings.HasPrefix(a, "-tags=") { + args[i] = a + " " + tag + addedTag = true + } else if a == "-tags" && i+1 < len(args) { + args[i+1] = args[i+1] + " " + tag + addedTag = true + } + } + if !addedTag { + args = append([]string{args[0], "-tags", tag}, args[1:]...) + } + return args +} + +func printComponents() { + fmt.Println(strings.Join(components, " ")) +} + +func printConfig() { + flags := llvmFlags() + + fmt.Printf(`// +build !byollvm + +// This file is generated by llvm-go, do not edit. + +package llvm + +/* +#cgo CPPFLAGS: %s +#cgo CXXFLAGS: %s +#cgo LDFLAGS: %s +*/ +import "C" + +type (run_build_sh int) +`, flags.cpp, flags.cxx, flags.ld) +} + +func runGoWithLLVMEnv(args []string, cc, cxx, cppflags, cxxflags, ldflags string) { + args = addTag(args, "byollvm") + + srcdir := llvmConfig("--src-root") + + tmpgopath, err := ioutil.TempDir("", "gopath") + if err != nil { + panic(err.Error()) + } + + for _, p := range packages { + path := filepath.Join(tmpgopath, "src", p.pkgpath) + err := os.MkdirAll(filepath.Dir(path), os.ModePerm) + if err != nil { + panic(err.Error()) + } + + err = os.Symlink(filepath.Join(srcdir, p.llvmpath), path) + if err != nil { + panic(err.Error()) + } + } + + newgopathlist := []string{tmpgopath} + newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...) + newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator)) + + flags := llvmFlags() + + newenv := []string{ + "CC=" + cc, + "CXX=" + cxx, + "CGO_CPPFLAGS=" + flags.cpp + " " + cppflags, + "CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags, + "CGO_LDFLAGS=" + flags.ld + " " + ldflags, + "GOPATH=" + newgopath, + } + for _, v := range os.Environ() { + if !strings.HasPrefix(v, "CC=") && + !strings.HasPrefix(v, "CXX=") && + !strings.HasPrefix(v, "CGO_CPPFLAGS=") && + !strings.HasPrefix(v, "CGO_CXXFLAGS=") && + !strings.HasPrefix(v, "CGO_LDFLAGS=") && + !strings.HasPrefix(v, "GOPATH=") { + newenv = append(newenv, v) + } + } + + gocmdpath, err := exec.LookPath("go") + if err != nil { + panic(err.Error()) + } + + proc, err := os.StartProcess(gocmdpath, append([]string{"go"}, args...), + &os.ProcAttr{ + Env: newenv, + Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, + }) + if err != nil { + panic(err.Error()) + } + ps, err := proc.Wait() + if err != nil { + panic(err.Error()) + } + + os.RemoveAll(tmpgopath) + + if !ps.Success() { + os.Exit(1) + } +} + +func usage() { + fmt.Println(`Usage: llvm-go subcommand [flags] + +Available subcommands: build get install run test print-components print-config`) + os.Exit(0) +} + +func main() { + cc := os.Getenv("CC") + cxx := os.Getenv("CXX") + cppflags := os.Getenv("CGO_CPPFLAGS") + cxxflags := os.Getenv("CGO_CXXFLAGS") + ldflags := os.Getenv("CGO_LDFLAGS") + + args := os.Args[1:] + DONE: for { + switch { + case len(args) == 0: + usage() + case strings.HasPrefix(args[0], "cc="): + cc = args[0][3:] + args = args[1:] + case strings.HasPrefix(args[0], "cxx="): + cxx = args[0][4:] + args = args[1:] + case strings.HasPrefix(args[0], "cppflags="): + cppflags = args[0][9:] + args = args[1:] + case strings.HasPrefix(args[0], "cxxflags="): + cxxflags = args[0][9:] + args = args[1:] + case strings.HasPrefix(args[0], "ldflags="): + ldflags = args[0][8:] + args = args[1:] + default: + break DONE + } + } + + switch args[0] { + case "build", "get", "install", "run", "test": + runGoWithLLVMEnv(args, cc, cxx, cppflags, cxxflags, ldflags) + case "print-components": + printComponents() + case "print-config": + printConfig() + default: + usage() + } +} diff --git a/tools/llvm-jitlistener/CMakeLists.txt b/tools/llvm-jitlistener/CMakeLists.txt index c9704fb..68a4303 100644 --- a/tools/llvm-jitlistener/CMakeLists.txt +++ b/tools/llvm-jitlistener/CMakeLists.txt @@ -1,22 +1,21 @@ -# This tool is excluded from the CMake build if Intel JIT events are disabled.
-
-link_directories( ${LLVM_INTEL_JITEVENTS_LIBDIR} )
-include_directories( ${LLVM_INTEL_JITEVENTS_INCDIR} )
-
-set(LLVM_LINK_COMPONENTS
- asmparser
- bitreader
- debuginfo
- inteljitevents
- interpreter
- irreader
- jit
- mcjit
- nativecodegen
- object
- selectiondag
- )
-
-add_llvm_tool(llvm-jitlistener
- llvm-jitlistener.cpp
- )
+# This tool is excluded from the CMake build if Intel JIT events are disabled. + +link_directories( ${LLVM_INTEL_JITEVENTS_LIBDIR} ) +include_directories( ${LLVM_INTEL_JITEVENTS_INCDIR} ) + +set(LLVM_LINK_COMPONENTS + asmparser + bitreader + debuginfo + inteljitevents + interpreter + irreader + mcjit + nativecodegen + object + selectiondag + ) + +add_llvm_tool(llvm-jitlistener + llvm-jitlistener.cpp + ) diff --git a/tools/llvm-jitlistener/LLVMBuild.txt b/tools/llvm-jitlistener/LLVMBuild.txt index 1ce78ac..e6ed20b 100644 --- a/tools/llvm-jitlistener/LLVMBuild.txt +++ b/tools/llvm-jitlistener/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-jitlistener parent = Tools -required_libraries = AsmParser BitReader IRReader Interpreter JIT MCJIT NativeCodeGen Object SelectionDAG Native +required_libraries = AsmParser BitReader IRReader Interpreter MCJIT NativeCodeGen Object SelectionDAG Native diff --git a/tools/llvm-jitlistener/Makefile b/tools/llvm-jitlistener/Makefile index b132227..6d72427 100644 --- a/tools/llvm-jitlistener/Makefile +++ b/tools/llvm-jitlistener/Makefile @@ -12,7 +12,7 @@ TOOLNAME := llvm-jitlistener include $(LEVEL)/Makefile.config
-LINK_COMPONENTS := mcjit jit interpreter nativecodegen bitreader asmparser irreader selectiondag Object
+LINK_COMPONENTS := mcjit interpreter nativecodegen bitreader asmparser irreader selectiondag Object
# If Intel JIT Events support is configured, link against the LLVM Intel JIT
# Events interface library. If not, this tool will do nothing useful, but it
diff --git a/tools/llvm-jitlistener/llvm-jitlistener.cpp b/tools/llvm-jitlistener/llvm-jitlistener.cpp index c159aa5..0bb6e8b 100644 --- a/tools/llvm-jitlistener/llvm-jitlistener.cpp +++ b/tools/llvm-jitlistener/llvm-jitlistener.cpp @@ -17,7 +17,7 @@ #include "../../lib/ExecutionEngine/IntelJITEvents/IntelJITEventsWrapper.h" #include "llvm/ADT/Triple.h" #include "llvm/ExecutionEngine/JITEventListener.h" -#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/ObjectImage.h" #include "llvm/IR/Module.h" @@ -113,26 +113,18 @@ protected: // Parse the bitcode... SMDiagnostic Err; - TheModule = ParseIRFile(IRFile, Err, Context); + std::unique_ptr<Module> TheModule(parseIRFile(IRFile, Err, Context)); if (!TheModule) { errs() << Err.getMessage(); return; } - // FIXME: This is using the default legacy JITMemoryManager because it - // supports poison memory. At some point, we'll need to update this to - // use an MCJIT-specific memory manager. It might be nice to have the - // poison memory option there too. - JITMemoryManager *MemMgr = JITMemoryManager::CreateDefaultMemManager(); + RTDyldMemoryManager *MemMgr = new SectionMemoryManager(); if (!MemMgr) { errs() << "Unable to create memory manager."; return; } - // Tell the memory manager to poison freed memory so that accessing freed - // memory is more easily tested. - MemMgr->setPoisonMemory(true); - // Override the triple to generate ELF on Windows since that's supported Triple Tuple(TheModule->getTargetTriple()); if (Tuple.getTriple().empty()) @@ -145,11 +137,10 @@ protected: // Compile the IR std::string Error; - TheJIT.reset(EngineBuilder(TheModule) + TheJIT.reset(EngineBuilder(std::move(TheModule)) .setEngineKind(EngineKind::JIT) .setErrorStr(&Error) - .setJITMemoryManager(MemMgr) - .setUseMCJIT(true) + .setMCJITMemoryManager(MemMgr) .create()); if (Error.empty() == false) errs() << Error; @@ -160,8 +151,6 @@ protected: } LLVMContext Context; // Global ownership - Module *TheModule; // Owned by ExecutionEngine. - JITMemoryManager *JMM; // Owned by ExecutionEngine. std::unique_ptr<ExecutionEngine> TheJIT; public: diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp index ed8c06e..828b9bb 100644 --- a/tools/llvm-link/llvm-link.cpp +++ b/tools/llvm-link/llvm-link.cpp @@ -14,6 +14,8 @@ #include "llvm/Linker/Linker.h" #include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" @@ -55,20 +57,39 @@ static cl::opt<bool> SuppressWarnings("suppress-warnings", cl::desc("Suppress all linking warnings"), cl::init(false)); -// LoadFile - Read the specified bitcode file in and return it. This routine -// searches the link path for the specified file to try to find it... +// Read the specified bitcode file in and return it. This routine searches the +// link path for the specified file to try to find it... // -static inline Module *LoadFile(const char *argv0, const std::string &FN, - LLVMContext& Context) { +static std::unique_ptr<Module> +loadFile(const char *argv0, const std::string &FN, LLVMContext &Context) { SMDiagnostic Err; if (Verbose) errs() << "Loading '" << FN << "'\n"; - Module* Result = nullptr; + std::unique_ptr<Module> Result = getLazyIRFileModule(FN, Err, Context); + if (!Result) + Err.print(argv0, errs()); - Result = ParseIRFile(FN, Err, Context); - if (Result) return Result; // Load successful! + return Result; +} + +static void diagnosticHandler(const DiagnosticInfo &DI) { + unsigned Severity = DI.getSeverity(); + switch (Severity) { + case DS_Error: + errs() << "ERROR: "; + break; + case DS_Warning: + if (SuppressWarnings) + return; + errs() << "WARNING: "; + break; + case DS_Remark: + case DS_Note: + llvm_unreachable("Only expecting warnings and errors"); + } - Err.print(argv0, errs()); - return nullptr; + DiagnosticPrinterRawOStream DP(errs()); + DI.print(DP); + errs() << '\n'; } int main(int argc, char **argv) { @@ -80,20 +101,11 @@ int main(int argc, char **argv) { llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. cl::ParseCommandLineOptions(argc, argv, "llvm linker\n"); - unsigned BaseArg = 0; - std::string ErrorMessage; + auto Composite = make_unique<Module>("llvm-link", Context); + Linker L(Composite.get(), diagnosticHandler); - std::unique_ptr<Module> Composite( - LoadFile(argv[0], InputFilenames[BaseArg], Context)); - if (!Composite.get()) { - errs() << argv[0] << ": error loading file '" - << InputFilenames[BaseArg] << "'\n"; - return 1; - } - - Linker L(Composite.get(), SuppressWarnings); - for (unsigned i = BaseArg+1; i < InputFilenames.size(); ++i) { - std::unique_ptr<Module> M(LoadFile(argv[0], InputFilenames[i], Context)); + for (unsigned i = 0; i < InputFilenames.size(); ++i) { + std::unique_ptr<Module> M = loadFile(argv[0], InputFilenames[i], Context); if (!M.get()) { errs() << argv[0] << ": error loading file '" <<InputFilenames[i]<< "'\n"; return 1; @@ -101,19 +113,16 @@ int main(int argc, char **argv) { if (Verbose) errs() << "Linking in '" << InputFilenames[i] << "'\n"; - if (L.linkInModule(M.get(), &ErrorMessage)) { - errs() << argv[0] << ": link error in '" << InputFilenames[i] - << "': " << ErrorMessage << "\n"; + if (L.linkInModule(M.get())) return 1; - } } if (DumpAsm) errs() << "Here's the assembly:\n" << *Composite; - std::string ErrorInfo; - tool_output_file Out(OutputFilename.c_str(), ErrorInfo, sys::fs::F_None); - if (!ErrorInfo.empty()) { - errs() << ErrorInfo << '\n'; + std::error_code EC; + tool_output_file Out(OutputFilename, EC, sys::fs::F_None); + if (EC) { + errs() << EC.message() << '\n'; return 1; } diff --git a/tools/llvm-lto/CMakeLists.txt b/tools/llvm-lto/CMakeLists.txt index 485b03d..9adf629 100644 --- a/tools/llvm-lto/CMakeLists.txt +++ b/tools/llvm-lto/CMakeLists.txt @@ -1,6 +1,5 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} - Core LTO MC Support diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp index 8b39f12..3c950ba 100644 --- a/tools/llvm-lto/llvm-lto.cpp +++ b/tools/llvm-lto/llvm-lto.cpp @@ -38,6 +38,14 @@ static cl::opt<bool> DisableGVNLoadPRE("disable-gvn-loadpre", cl::init(false), cl::desc("Do not run the GVN load PRE pass")); +static cl::opt<bool> +DisableLTOVectorization("disable-lto-vectorization", cl::init(false), + cl::desc("Do not run loop or slp vectorization during LTO")); + +static cl::opt<bool> +UseDiagnosticHandler("use-diagnostic-handler", cl::init(false), + cl::desc("Use a diagnostic handler to test the handler interface")); + static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, cl::desc("<input bitcode files>")); @@ -63,6 +71,25 @@ struct ModuleInfo { }; } +void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity, + const char *Msg, void *) { + switch (Severity) { + case LTO_DS_NOTE: + errs() << "note: "; + break; + case LTO_DS_REMARK: + errs() << "remark: "; + break; + case LTO_DS_ERROR: + errs() << "error: "; + break; + case LTO_DS_WARNING: + errs() << "warning: "; + break; + } + errs() << Msg << "\n"; +} + int main(int argc, char **argv) { // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal(); @@ -84,6 +111,9 @@ int main(int argc, char **argv) { LTOCodeGenerator CodeGen; + if (UseDiagnosticHandler) + CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr); + switch (RelocModel) { case Reloc::Static: CodeGen.setCodePICModel(LTO_CODEGEN_PIC_MODEL_STATIC); @@ -117,12 +147,8 @@ int main(int argc, char **argv) { return 1; } - - if (!CodeGen.addModule(Module.get(), error)) { - errs() << argv[0] << ": error adding file '" << InputFilenames[i] - << "': " << error << "\n"; + if (!CodeGen.addModule(Module.get())) return 1; - } unsigned NumSyms = Module->getSymbolCount(); for (unsigned I = 0; I < NumSyms; ++I) { @@ -157,19 +183,20 @@ int main(int argc, char **argv) { if (!OutputFilename.empty()) { size_t len = 0; std::string ErrorInfo; - const void *Code = CodeGen.compile(&len, DisableOpt, DisableInline, - DisableGVNLoadPRE, ErrorInfo); + const void *Code = + CodeGen.compile(&len, DisableOpt, DisableInline, DisableGVNLoadPRE, + DisableLTOVectorization, ErrorInfo); if (!Code) { errs() << argv[0] << ": error compiling the code: " << ErrorInfo << "\n"; return 1; } - raw_fd_ostream FileStream(OutputFilename.c_str(), ErrorInfo, - sys::fs::F_None); - if (!ErrorInfo.empty()) { + std::error_code EC; + raw_fd_ostream FileStream(OutputFilename, EC, sys::fs::F_None); + if (EC) { errs() << argv[0] << ": error opening the file '" << OutputFilename - << "': " << ErrorInfo << "\n"; + << "': " << EC.message() << "\n"; return 1; } @@ -178,7 +205,8 @@ int main(int argc, char **argv) { std::string ErrorInfo; const char *OutputName = nullptr; if (!CodeGen.compile_to_file(&OutputName, DisableOpt, DisableInline, - DisableGVNLoadPRE, ErrorInfo)) { + DisableGVNLoadPRE, DisableLTOVectorization, + ErrorInfo)) { errs() << argv[0] << ": error compiling the code: " << ErrorInfo << "\n"; diff --git a/tools/llvm-mc/Android.mk b/tools/llvm-mc/Android.mk index d794c8a..e6de9eb 100644 --- a/tools/llvm-mc/Android.mk +++ b/tools/llvm-mc/Android.mk @@ -34,7 +34,10 @@ llvm_mc_STATIC_LIBRARIES := \ libLLVMX86AsmPrinter \ libLLVMX86Utils \ libLLVMX86Disassembler \ + libLLVMX86CodeGen \ libLLVMAsmPrinter \ + libLLVMCodeGen \ + libLLVMAnalysis \ libLLVMTarget \ libLLVMMC \ libLLVMObject \ diff --git a/tools/llvm-mc/Disassembler.cpp b/tools/llvm-mc/Disassembler.cpp index 9367590..95d146a 100644 --- a/tools/llvm-mc/Disassembler.cpp +++ b/tools/llvm-mc/Disassembler.cpp @@ -22,33 +22,14 @@ #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/MemoryObject.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; -typedef std::vector<std::pair<unsigned char, const char*> > ByteArrayTy; - -namespace { -class VectorMemoryObject : public MemoryObject { -private: - const ByteArrayTy &Bytes; -public: - VectorMemoryObject(const ByteArrayTy &bytes) : Bytes(bytes) {} - - uint64_t getBase() const override { return 0; } - uint64_t getExtent() const override { return Bytes.size(); } - - int readByte(uint64_t Addr, uint8_t *Byte) const override { - if (Addr >= getExtent()) - return -1; - *Byte = Bytes[Addr].first; - return 0; - } -}; -} +typedef std::pair<std::vector<unsigned char>, std::vector<const char *>> + ByteArrayTy; static bool PrintInsts(const MCDisassembler &DisAsm, const ByteArrayTy &Bytes, @@ -56,21 +37,21 @@ static bool PrintInsts(const MCDisassembler &DisAsm, MCStreamer &Streamer, bool InAtomicBlock, const MCSubtargetInfo &STI) { // Wrap the vector in a MemoryObject. - VectorMemoryObject memoryObject(Bytes); + ArrayRef<uint8_t> Data(Bytes.first.data(), Bytes.first.size()); // Disassemble it to strings. uint64_t Size; uint64_t Index; - for (Index = 0; Index < Bytes.size(); Index += Size) { + for (Index = 0; Index < Bytes.first.size(); Index += Size) { MCInst Inst; MCDisassembler::DecodeStatus S; - S = DisAsm.getInstruction(Inst, Size, memoryObject, Index, + S = DisAsm.getInstruction(Inst, Size, Data.slice(Index), Index, /*REMOVE*/ nulls(), nulls()); switch (S) { case MCDisassembler::Fail: - SM.PrintMessage(SMLoc::getFromPointer(Bytes[Index].second), + SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]), SourceMgr::DK_Warning, "invalid instruction encoding"); // Don't try to resynchronise the stream in a block @@ -83,7 +64,7 @@ static bool PrintInsts(const MCDisassembler &DisAsm, break; case MCDisassembler::SoftFail: - SM.PrintMessage(SMLoc::getFromPointer(Bytes[Index].second), + SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]), SourceMgr::DK_Warning, "potentially undefined instruction encoding"); // Fall through @@ -98,29 +79,23 @@ static bool PrintInsts(const MCDisassembler &DisAsm, } static bool SkipToToken(StringRef &Str) { - while (!Str.empty() && Str.find_first_not_of(" \t\r\n#,") != 0) { + for (;;) { + if (Str.empty()) + return false; + // Strip horizontal whitespace and commas. - if (size_t Pos = Str.find_first_not_of(" \t\r,")) { + if (size_t Pos = Str.find_first_not_of(" \t\r\n,")) { Str = Str.substr(Pos); + continue; } - // If this is the end of a line or start of a comment, remove the rest of - // the line. - if (Str[0] == '\n' || Str[0] == '#') { - // Strip to the end of line if we already processed any bytes on this - // line. This strips the comment and/or the \n. - if (Str[0] == '\n') { - Str = Str.substr(1); - } else { + // If this is the start of a comment, remove the rest of the line. + if (Str[0] == '#') { Str = Str.substr(Str.find_first_of('\n')); - if (!Str.empty()) - Str = Str.substr(1); - } continue; } + return true; } - - return !Str.empty(); } @@ -143,11 +118,13 @@ static bool ByteArrayFromString(ByteArrayTy &ByteArray, SM.PrintMessage(SMLoc::getFromPointer(Value.data()), SourceMgr::DK_Error, "invalid input token"); Str = Str.substr(Str.find('\n')); - ByteArray.clear(); + ByteArray.first.clear(); + ByteArray.second.clear(); continue; } - ByteArray.push_back(std::make_pair((unsigned char)ByteVal, Value.data())); + ByteArray.first.push_back(ByteVal); + ByteArray.second.push_back(Value.data()); Str = Str.substr(Next); } @@ -185,7 +162,7 @@ int Disassembler::disassemble(const Target &T, } // Set up initial section manually here - Streamer.InitSections(); + Streamer.InitSections(false); bool ErrorOccurred = false; @@ -195,7 +172,8 @@ int Disassembler::disassemble(const Target &T, bool InAtomicBlock = false; while (SkipToToken(Str)) { - ByteArray.clear(); + ByteArray.first.clear(); + ByteArray.second.clear(); if (Str[0] == '[') { if (InAtomicBlock) { @@ -220,7 +198,7 @@ int Disassembler::disassemble(const Target &T, // It's a real token, get the bytes and emit them ErrorOccurred |= ByteArrayFromString(ByteArray, Str, SM); - if (!ByteArray.empty()) + if (!ByteArray.first.empty()) ErrorOccurred |= PrintInsts(*DisAsm, ByteArray, SM, Out, Streamer, InAtomicBlock, STI); } diff --git a/tools/llvm-mc/Disassembler.h b/tools/llvm-mc/Disassembler.h index 5615da8..1f18ac0 100644 --- a/tools/llvm-mc/Disassembler.h +++ b/tools/llvm-mc/Disassembler.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef DISASSEMBLER_H -#define DISASSEMBLER_H +#ifndef LLVM_TOOLS_LLVM_MC_DISASSEMBLER_H +#define LLVM_TOOLS_LLVM_MC_DISASSEMBLER_H #include <string> diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index 4c5b230..5da9e86 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/tools/llvm-mc/llvm-mc.cpp @@ -208,11 +208,11 @@ static tool_output_file *GetOutputStream() { if (OutputFilename == "") OutputFilename = "-"; - std::string Err; + std::error_code EC; tool_output_file *Out = - new tool_output_file(OutputFilename.c_str(), Err, sys::fs::F_None); - if (!Err.empty()) { - errs() << Err << '\n'; + new tool_output_file(OutputFilename, EC, sys::fs::F_None); + if (EC) { + errs() << EC.message() << '\n'; delete Out; return nullptr; } @@ -373,12 +373,12 @@ int main(int argc, char **argv) { errs() << ProgName << ": " << EC.message() << '\n'; return 1; } - MemoryBuffer *Buffer = BufferPtr->release(); + MemoryBuffer *Buffer = BufferPtr->get(); SourceMgr SrcMgr; // Tell SrcMgr about this buffer, which is what the parser will pick up. - SrcMgr.AddNewSourceBuffer(Buffer, SMLoc()); + SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); // Record the location of the include directories so that the lexer can find // it later. @@ -471,9 +471,10 @@ int main(int argc, char **argv) { assert(FileType == OFT_ObjectFile && "Invalid file type!"); MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, *STI, Ctx); MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, MCPU); - Str.reset(TheTarget->createMCObjectStreamer(TripleName, Ctx, *MAB, - FOS, CE, *STI, RelaxAll, - NoExecStack)); + Str.reset(TheTarget->createMCObjectStreamer(TripleName, Ctx, *MAB, FOS, CE, + *STI, RelaxAll)); + if (NoExecStack) + Str->InitSections(true); } int Res = 1; diff --git a/tools/llvm-mcmarkup/llvm-mcmarkup.cpp b/tools/llvm-mcmarkup/llvm-mcmarkup.cpp index a878f11..5654313 100644 --- a/tools/llvm-mcmarkup/llvm-mcmarkup.cpp +++ b/tools/llvm-mcmarkup/llvm-mcmarkup.cpp @@ -141,14 +141,15 @@ static void parseMCMarkup(StringRef Filename) { errs() << ToolName << ": " << EC.message() << '\n'; return; } - MemoryBuffer *Buffer = BufferPtr->release(); + std::unique_ptr<MemoryBuffer> &Buffer = BufferPtr.get(); SourceMgr SrcMgr; + StringRef InputSource = Buffer->getBuffer(); + // Tell SrcMgr about this buffer, which is what the parser will pick up. - SrcMgr.AddNewSourceBuffer(Buffer, SMLoc()); + SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc()); - StringRef InputSource = Buffer->getBuffer(); MarkupLexer Lex(InputSource); MarkupParser Parser(Lex, SrcMgr); diff --git a/tools/llvm-nm/Android.mk b/tools/llvm-nm/Android.mk index 98e7ba9..a17ca4d 100644 --- a/tools/llvm-nm/Android.mk +++ b/tools/llvm-nm/Android.mk @@ -30,19 +30,23 @@ llvm_nm_STATIC_LIBRARIES := \ libLLVMMipsDesc \ libLLVMMipsAsmPrinter \ libLLVMMipsDisassembler \ - libLLVMX86CodeGen \ libLLVMX86Info \ libLLVMX86Desc \ libLLVMX86AsmPrinter \ libLLVMX86AsmParser \ + libLLVMX86CodeGen \ libLLVMX86Utils \ libLLVMX86Disassembler \ + libLLVMCodeGen \ + libLLVMAnalysis \ + libLLVMTarget \ libLLVMObject \ libLLVMBitReader \ libLLVMMC \ libLLVMMCParser \ libLLVMCore \ libLLVMSupport \ + libLLVMMCDisassembler \ include $(CLEAR_VARS) diff --git a/tools/llvm-nm/CMakeLists.txt b/tools/llvm-nm/CMakeLists.txt index 1fe4a2d..20293bb 100644 --- a/tools/llvm-nm/CMakeLists.txt +++ b/tools/llvm-nm/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} + Core Object Support ) diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index 3bd9ef9..be2c4fa 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/tools/llvm-nm/llvm-nm.cpp @@ -87,8 +87,8 @@ cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix")); cl::opt<bool> DarwinFormat("m", cl::desc("Alias for --format=darwin")); static cl::list<std::string> -ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), - cl::ZeroOrMore); + ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), + cl::ZeroOrMore); bool ArchAll = false; cl::opt<bool> PrintFileName( @@ -136,6 +136,22 @@ cl::opt<bool> JustSymbolName("just-symbol-name", cl::desc("Print just the symbol's name")); cl::alias JustSymbolNames("j", cl::desc("Alias for --just-symbol-name"), cl::aliasopt(JustSymbolName)); + +// FIXME: This option takes exactly two strings and should be allowed anywhere +// on the command line. Such that "llvm-nm -s __TEXT __text foo.o" would work. +// But that does not as the CommandLine Library does not have a way to make +// this work. For now the "-s __TEXT __text" has to be last on the command +// line. +cl::list<std::string> SegSect("s", cl::Positional, cl::ZeroOrMore, + cl::desc("Dump only symbols from this segment " + "and section name, Mach-O only")); + +cl::opt<bool> FormatMachOasHex("x", cl::desc("Print symbol entry in hex, " + "Mach-O only")); + +cl::opt<bool> NoLLVMBitcode("no-llvm-bc", + cl::desc("Disable LLVM bitcode reader")); + bool PrintAddress = true; bool MultipleFiles = false; @@ -234,12 +250,12 @@ static bool compareSymbolName(const NMSymbol &A, const NMSymbol &B) { } } -static char isSymbolList64Bit(SymbolicFile *Obj) { +static char isSymbolList64Bit(SymbolicFile &Obj) { if (isa<IRObjectFile>(Obj)) return false; else if (isa<COFFObjectFile>(Obj)) return false; - else if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj)) + else if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) return MachO->is64Bit(); else if (isa<ELF32LEObjectFile>(Obj)) return false; @@ -258,8 +274,10 @@ typedef std::vector<NMSymbol> SymbolListT; static SymbolListT SymbolList; // darwinPrintSymbol() is used to print a symbol from a Mach-O file when the -// the OutputFormat is darwin. It produces the same output as darwin's nm(1) -m -// output. +// the OutputFormat is darwin or we are printing Mach-O symbols in hex. For +// the darwin format it produces the same output as darwin's nm(1) -m output +// and when printing Mach-O symbols in hex it produces the same output as +// darwin's nm(1) -x format. static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I, char *SymbolAddrStr, const char *printBlanks) { MachO::mach_header H; @@ -268,7 +286,9 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I, MachO::nlist_64 STE_64; MachO::nlist STE; uint8_t NType; + uint8_t NSect; uint16_t NDesc; + uint32_t NStrx; uint64_t NValue; if (MachO->is64Bit()) { H_64 = MachO->MachOObjectFile::getHeader64(); @@ -276,7 +296,9 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I, Flags = H_64.flags; STE_64 = MachO->getSymbol64TableEntry(I->Symb); NType = STE_64.n_type; + NSect = STE_64.n_sect; NDesc = STE_64.n_desc; + NStrx = STE_64.n_strx; NValue = STE_64.n_value; } else { H = MachO->MachOObjectFile::getHeader(); @@ -284,10 +306,34 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I, Flags = H.flags; STE = MachO->getSymbolTableEntry(I->Symb); NType = STE.n_type; + NSect = STE.n_sect; NDesc = STE.n_desc; + NStrx = STE.n_strx; NValue = STE.n_value; } + // If we are printing Mach-O symbols in hex do that and return. + if (FormatMachOasHex) { + char Str[18] = ""; + const char *printFormat; + if (MachO->is64Bit()) + printFormat = "%016" PRIx64; + else + printFormat = "%08" PRIx64; + format(printFormat, NValue).print(Str, sizeof(Str)); + outs() << Str << ' '; + format("%02x", NType).print(Str, sizeof(Str)); + outs() << Str << ' '; + format("%02x", NSect).print(Str, sizeof(Str)); + outs() << Str << ' '; + format("%04x", NDesc).print(Str, sizeof(Str)); + outs() << Str << ' '; + format("%08x", NStrx).print(Str, sizeof(Str)); + outs() << Str << ' '; + outs() << I->Name << "\n"; + return; + } + if (PrintAddress) { if ((NType & MachO::N_TYPE) == MachO::N_INDR) strcpy(SymbolAddrStr, printBlanks); @@ -414,7 +460,87 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I, outs() << "\n"; } -static void sortAndPrintSymbolList(SymbolicFile *Obj, bool printName) { +// Table that maps Darwin's Mach-O stab constants to strings to allow printing. +struct DarwinStabName { + uint8_t NType; + const char *Name; +}; +static const struct DarwinStabName DarwinStabNames[] = { + {MachO::N_GSYM, "GSYM"}, + {MachO::N_FNAME, "FNAME"}, + {MachO::N_FUN, "FUN"}, + {MachO::N_STSYM, "STSYM"}, + {MachO::N_LCSYM, "LCSYM"}, + {MachO::N_BNSYM, "BNSYM"}, + {MachO::N_PC, "PC"}, + {MachO::N_AST, "AST"}, + {MachO::N_OPT, "OPT"}, + {MachO::N_RSYM, "RSYM"}, + {MachO::N_SLINE, "SLINE"}, + {MachO::N_ENSYM, "ENSYM"}, + {MachO::N_SSYM, "SSYM"}, + {MachO::N_SO, "SO"}, + {MachO::N_OSO, "OSO"}, + {MachO::N_LSYM, "LSYM"}, + {MachO::N_BINCL, "BINCL"}, + {MachO::N_SOL, "SOL"}, + {MachO::N_PARAMS, "PARAM"}, + {MachO::N_VERSION, "VERS"}, + {MachO::N_OLEVEL, "OLEV"}, + {MachO::N_PSYM, "PSYM"}, + {MachO::N_EINCL, "EINCL"}, + {MachO::N_ENTRY, "ENTRY"}, + {MachO::N_LBRAC, "LBRAC"}, + {MachO::N_EXCL, "EXCL"}, + {MachO::N_RBRAC, "RBRAC"}, + {MachO::N_BCOMM, "BCOMM"}, + {MachO::N_ECOMM, "ECOMM"}, + {MachO::N_ECOML, "ECOML"}, + {MachO::N_LENG, "LENG"}, + {0, 0}}; +static const char *getDarwinStabString(uint8_t NType) { + for (unsigned i = 0; DarwinStabNames[i].Name; i++) { + if (DarwinStabNames[i].NType == NType) + return DarwinStabNames[i].Name; + } + return 0; +} + +// darwinPrintStab() prints the n_sect, n_desc along with a symbolic name of +// a stab n_type value in a Mach-O file. +static void darwinPrintStab(MachOObjectFile *MachO, SymbolListT::iterator I) { + MachO::nlist_64 STE_64; + MachO::nlist STE; + uint8_t NType; + uint8_t NSect; + uint16_t NDesc; + if (MachO->is64Bit()) { + STE_64 = MachO->getSymbol64TableEntry(I->Symb); + NType = STE_64.n_type; + NSect = STE_64.n_sect; + NDesc = STE_64.n_desc; + } else { + STE = MachO->getSymbolTableEntry(I->Symb); + NType = STE.n_type; + NSect = STE.n_sect; + NDesc = STE.n_desc; + } + + char Str[18] = ""; + format("%02x", NSect).print(Str, sizeof(Str)); + outs() << ' ' << Str << ' '; + format("%04x", NDesc).print(Str, sizeof(Str)); + outs() << Str << ' '; + if (const char *stabString = getDarwinStabString(NType)) + format("%5.5s", stabString).print(Str, sizeof(Str)); + else + format(" %02x", NType).print(Str, sizeof(Str)); + outs() << Str; +} + +static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, + std::string ArchiveName, + std::string ArchitectureName) { if (!NoSort) { if (NumericSort) std::sort(SymbolList.begin(), SymbolList.end(), compareSymbolAddress); @@ -424,14 +550,16 @@ static void sortAndPrintSymbolList(SymbolicFile *Obj, bool printName) { std::sort(SymbolList.begin(), SymbolList.end(), compareSymbolName); } - if (OutputFormat == posix && MultipleFiles && printName) { - outs() << '\n' << CurrentFilename << ":\n"; - } else if (OutputFormat == bsd && MultipleFiles && printName) { - outs() << "\n" << CurrentFilename << ":\n"; - } else if (OutputFormat == sysv) { - outs() << "\n\nSymbols from " << CurrentFilename << ":\n\n" - << "Name Value Class Type" - << " Size Line Section\n"; + if (!PrintFileName) { + if (OutputFormat == posix && MultipleFiles && printName) { + outs() << '\n' << CurrentFilename << ":\n"; + } else if (OutputFormat == bsd && MultipleFiles && printName) { + outs() << "\n" << CurrentFilename << ":\n"; + } else if (OutputFormat == sysv) { + outs() << "\n\nSymbols from " << CurrentFilename << ":\n\n" + << "Name Value Class Type" + << " Size Line Section\n"; + } } const char *printBlanks, *printFormat; @@ -451,7 +579,14 @@ static void sortAndPrintSymbolList(SymbolicFile *Obj, bool printName) { continue; if (SizeSort && !PrintAddress && I->Size == UnknownAddressOrSize) continue; - if (JustSymbolName) { + if (PrintFileName) { + if (!ArchitectureName.empty()) + outs() << "(for architecture " << ArchitectureName << "):"; + if (!ArchiveName.empty()) + outs() << ArchiveName << ":"; + outs() << CurrentFilename << ": "; + } + if (JustSymbolName || (UndefinedOnly && isa<MachOObjectFile>(Obj))) { outs() << I->Name << "\n"; continue; } @@ -470,11 +605,13 @@ static void sortAndPrintSymbolList(SymbolicFile *Obj, bool printName) { if (I->Size != UnknownAddressOrSize) format(printFormat, I->Size).print(SymbolSizeStr, sizeof(SymbolSizeStr)); - // If OutputFormat is darwin and we have a MachOObjectFile print as darwin's - // nm(1) -m output, else if OutputFormat is darwin and not a Mach-O object - // fall back to OutputFormat bsd (see below). - MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj); - if (OutputFormat == darwin && MachO) { + // If OutputFormat is darwin or we are printing Mach-O symbols in hex and + // we have a MachOObjectFile, call darwinPrintSymbol to print as darwin's + // nm(1) -m output or hex, else if OutputFormat is darwin or we are + // printing Mach-O symbols in hex and not a Mach-O object fall back to + // OutputFormat bsd (see below). + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj); + if ((OutputFormat == darwin || FormatMachOasHex) && MachO) { darwinPrintSymbol(MachO, I, SymbolAddrStr, printBlanks); } else if (OutputFormat == posix) { outs() << I->Name << " " << I->TypeChar << " " << SymbolAddrStr @@ -487,7 +624,10 @@ static void sortAndPrintSymbolList(SymbolicFile *Obj, bool printName) { if (I->Size != UnknownAddressOrSize) outs() << ' '; } - outs() << I->TypeChar << " " << I->Name << "\n"; + outs() << I->TypeChar; + if (I->TypeChar == '-' && MachO) + darwinPrintStab(MachO, I); + outs() << " " << I->Name << "\n"; } else if (OutputFormat == sysv) { std::string PaddedName(I->Name); while (PaddedName.length() < 20) @@ -549,7 +689,7 @@ static char getSymbolNMTypeChar(ELFObjectFile<ELFT> &Obj, } static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) { - const coff_symbol *Symb = Obj.getCOFFSymbol(*I); + COFFSymbolRef Symb = Obj.getCOFFSymbol(*I); // OK, this is COFF. symbol_iterator SymI(I); @@ -566,7 +706,7 @@ static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) { return Ret; uint32_t Characteristics = 0; - if (!COFF::isReservedSectionNumber(Symb->SectionNumber)) { + if (!COFF::isReservedSectionNumber(Symb.getSectionNumber())) { section_iterator SecI = Obj.section_end(); if (error(SymI->getSection(SecI))) return '?'; @@ -574,25 +714,21 @@ static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) { Characteristics = Section->Characteristics; } - switch (Symb->SectionNumber) { + switch (Symb.getSectionNumber()) { case COFF::IMAGE_SYM_DEBUG: return 'n'; default: // Check section type. if (Characteristics & COFF::IMAGE_SCN_CNT_CODE) return 't'; - else if (Characteristics & COFF::IMAGE_SCN_MEM_READ && - ~Characteristics & COFF::IMAGE_SCN_MEM_WRITE) // Read only. - return 'r'; - else if (Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) - return 'd'; - else if (Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) + if (Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) + return Characteristics & COFF::IMAGE_SCN_MEM_WRITE ? 'd' : 'r'; + if (Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) return 'b'; - else if (Characteristics & COFF::IMAGE_SCN_LNK_INFO) + if (Characteristics & COFF::IMAGE_SCN_LNK_INFO) return 'i'; - // Check for section symbol. - else if (Symb->isSectionDefinition()) + if (Symb.isSectionDefinition()) return 's'; } @@ -612,6 +748,9 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) { DataRefImpl Symb = I->getRawDataRefImpl(); uint8_t NType = getNType(Obj, Symb); + if (NType & MachO::N_STAB) + return '-'; + switch (NType & MachO::N_TYPE) { case MachO::N_ABS: return 's'; @@ -654,7 +793,7 @@ static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I) { } template <class ELFT> -static bool isObject(ELFObjectFile<ELFT> &Obj, symbol_iterator I) { +static bool isELFObject(ELFObjectFile<ELFT> &Obj, symbol_iterator I) { typedef typename ELFObjectFile<ELFT>::Elf_Sym Elf_Sym; DataRefImpl Symb = I->getRawDataRefImpl(); @@ -663,19 +802,19 @@ static bool isObject(ELFObjectFile<ELFT> &Obj, symbol_iterator I) { return ESym->getType() == ELF::STT_OBJECT; } -static bool isObject(SymbolicFile *Obj, basic_symbol_iterator I) { - if (ELF32LEObjectFile *ELF = dyn_cast<ELF32LEObjectFile>(Obj)) - return isObject(*ELF, I); - if (ELF64LEObjectFile *ELF = dyn_cast<ELF64LEObjectFile>(Obj)) - return isObject(*ELF, I); - if (ELF32BEObjectFile *ELF = dyn_cast<ELF32BEObjectFile>(Obj)) - return isObject(*ELF, I); - if (ELF64BEObjectFile *ELF = dyn_cast<ELF64BEObjectFile>(Obj)) - return isObject(*ELF, I); +static bool isObject(SymbolicFile &Obj, basic_symbol_iterator I) { + if (ELF32LEObjectFile *ELF = dyn_cast<ELF32LEObjectFile>(&Obj)) + return isELFObject(*ELF, I); + if (ELF64LEObjectFile *ELF = dyn_cast<ELF64LEObjectFile>(&Obj)) + return isELFObject(*ELF, I); + if (ELF32BEObjectFile *ELF = dyn_cast<ELF32BEObjectFile>(&Obj)) + return isELFObject(*ELF, I); + if (ELF64BEObjectFile *ELF = dyn_cast<ELF64BEObjectFile>(&Obj)) + return isELFObject(*ELF, I); return false; } -static char getNMTypeChar(SymbolicFile *Obj, basic_symbol_iterator I) { +static char getNMTypeChar(SymbolicFile &Obj, basic_symbol_iterator I) { uint32_t Symflags = I->getFlags(); if ((Symflags & object::SymbolRef::SF_Weak) && !isa<MachOObjectFile>(Obj)) { char Ret = isObject(Obj, I) ? 'v' : 'w'; @@ -693,20 +832,20 @@ static char getNMTypeChar(SymbolicFile *Obj, basic_symbol_iterator I) { char Ret = '?'; if (Symflags & object::SymbolRef::SF_Absolute) Ret = 'a'; - else if (IRObjectFile *IR = dyn_cast<IRObjectFile>(Obj)) + else if (IRObjectFile *IR = dyn_cast<IRObjectFile>(&Obj)) Ret = getSymbolNMTypeChar(*IR, I); - else if (COFFObjectFile *COFF = dyn_cast<COFFObjectFile>(Obj)) + else if (COFFObjectFile *COFF = dyn_cast<COFFObjectFile>(&Obj)) Ret = getSymbolNMTypeChar(*COFF, I); - else if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj)) + else if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) Ret = getSymbolNMTypeChar(*MachO, I); - else if (ELF32LEObjectFile *ELF = dyn_cast<ELF32LEObjectFile>(Obj)) + else if (ELF32LEObjectFile *ELF = dyn_cast<ELF32LEObjectFile>(&Obj)) Ret = getSymbolNMTypeChar(*ELF, I); - else if (ELF64LEObjectFile *ELF = dyn_cast<ELF64LEObjectFile>(Obj)) + else if (ELF64LEObjectFile *ELF = dyn_cast<ELF64LEObjectFile>(&Obj)) Ret = getSymbolNMTypeChar(*ELF, I); - else if (ELF32BEObjectFile *ELF = dyn_cast<ELF32BEObjectFile>(Obj)) + else if (ELF32BEObjectFile *ELF = dyn_cast<ELF32BEObjectFile>(&Obj)) Ret = getSymbolNMTypeChar(*ELF, I); else - Ret = getSymbolNMTypeChar(*cast<ELF64BEObjectFile>(Obj), I); + Ret = getSymbolNMTypeChar(cast<ELF64BEObjectFile>(Obj), I); if (Symflags & object::SymbolRef::SF_Global) Ret = toupper(Ret); @@ -714,32 +853,90 @@ static char getNMTypeChar(SymbolicFile *Obj, basic_symbol_iterator I) { return Ret; } -static void dumpSymbolNamesFromObject(SymbolicFile *Obj, bool printName) { - basic_symbol_iterator IBegin = Obj->symbol_begin(); - basic_symbol_iterator IEnd = Obj->symbol_end(); +// getNsectForSegSect() is used to implement the Mach-O "-s segname sectname" +// option to dump only those symbols from that section in a Mach-O file. +// It is called once for each Mach-O file from dumpSymbolNamesFromObject() +// to get the section number for that named section from the command line +// arguments. It returns the section number for that section in the Mach-O +// file or zero it is not present. +static unsigned getNsectForSegSect(MachOObjectFile *Obj) { + unsigned Nsect = 1; + for (section_iterator I = Obj->section_begin(), E = Obj->section_end(); + I != E; ++I) { + DataRefImpl Ref = I->getRawDataRefImpl(); + StringRef SectionName; + Obj->getSectionName(Ref, SectionName); + StringRef SegmentName = Obj->getSectionFinalSegmentName(Ref); + if (SegmentName == SegSect[0] && SectionName == SegSect[1]) + return Nsect; + Nsect++; + } + return 0; +} + +// getNsectInMachO() is used to implement the Mach-O "-s segname sectname" +// option to dump only those symbols from that section in a Mach-O file. +// It is called once for each symbol in a Mach-O file from +// dumpSymbolNamesFromObject() and returns the section number for that symbol +// if it is in a section, else it returns 0. +static unsigned getNsectInMachO(MachOObjectFile &Obj, basic_symbol_iterator I) { + DataRefImpl Symb = I->getRawDataRefImpl(); + if (Obj.is64Bit()) { + MachO::nlist_64 STE = Obj.getSymbol64TableEntry(Symb); + if ((STE.n_type & MachO::N_TYPE) == MachO::N_SECT) + return STE.n_sect; + return 0; + } + MachO::nlist STE = Obj.getSymbolTableEntry(Symb); + if ((STE.n_type & MachO::N_TYPE) == MachO::N_SECT) + return STE.n_sect; + return 0; +} + +static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, + std::string ArchiveName = std::string(), + std::string ArchitectureName = + std::string()) { + basic_symbol_iterator IBegin = Obj.symbol_begin(); + basic_symbol_iterator IEnd = Obj.symbol_end(); if (DynamicSyms) { - if (!Obj->isELF()) { - error("File format has no dynamic symbol table", Obj->getFileName()); + if (!Obj.isELF()) { + error("File format has no dynamic symbol table", Obj.getFileName()); return; } std::pair<symbol_iterator, symbol_iterator> IDyn = - getELFDynamicSymbolIterators(Obj); + getELFDynamicSymbolIterators(&Obj); IBegin = IDyn.first; IEnd = IDyn.second; } std::string NameBuffer; raw_string_ostream OS(NameBuffer); + // If a "-s segname sectname" option was specified and this is a Mach-O + // file get the section number for that section in this object file. + unsigned int Nsect = 0; + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj); + if (SegSect.size() != 0 && MachO) { + Nsect = getNsectForSegSect(MachO); + // If this section is not in the object file no symbols are printed. + if (Nsect == 0) + return; + } for (basic_symbol_iterator I = IBegin; I != IEnd; ++I) { uint32_t SymFlags = I->getFlags(); if (!DebugSyms && (SymFlags & SymbolRef::SF_FormatSpecific)) continue; if (WithoutAliases) { - if (IRObjectFile *IR = dyn_cast<IRObjectFile>(Obj)) { + if (IRObjectFile *IR = dyn_cast<IRObjectFile>(&Obj)) { const GlobalValue *GV = IR->getSymbolGV(I->getRawDataRefImpl()); if (GV && isa<GlobalAlias>(GV)) continue; } } + // If a "-s segname sectname" option was specified and this is a Mach-O + // file and this section appears in this file, Nsect will be non-zero then + // see if this symbol is a symbol from that section and if not skip it. + if (Nsect && Nsect != getNsectInMachO(*MachO, I)) + continue; NMSymbol S; S.Size = UnknownAddressOrSize; S.Address = UnknownAddressOrSize; @@ -766,8 +963,8 @@ static void dumpSymbolNamesFromObject(SymbolicFile *Obj, bool printName) { P += strlen(P) + 1; } - CurrentFilename = Obj->getFileName(); - sortAndPrintSymbolList(Obj, printName); + CurrentFilename = Obj.getFileName(); + sortAndPrintSymbolList(Obj, printName, ArchiveName, ArchitectureName); } // checkMachOAndArchFlags() checks to see if the SymbolicFile is a Mach-O file @@ -809,16 +1006,15 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { MemoryBuffer::getFileOrSTDIN(Filename); if (error(BufferOrErr.getError(), Filename)) return; - std::unique_ptr<MemoryBuffer> Buffer = std::move(BufferOrErr.get()); LLVMContext &Context = getGlobalContext(); - ErrorOr<Binary *> BinaryOrErr = createBinary(Buffer, &Context); + ErrorOr<std::unique_ptr<Binary>> BinaryOrErr = createBinary( + BufferOrErr.get()->getMemBufferRef(), NoLLVMBitcode ? nullptr : &Context); if (error(BinaryOrErr.getError(), Filename)) return; - Buffer.release(); - std::unique_ptr<Binary> Bin(BinaryOrErr.get()); + Binary &Bin = *BinaryOrErr.get(); - if (Archive *A = dyn_cast<Archive>(Bin.get())) { + if (Archive *A = dyn_cast<Archive>(&Bin)) { if (ArchiveMap) { Archive::symbol_iterator I = A->symbol_begin(); Archive::symbol_iterator E = A->symbol_end(); @@ -846,18 +1042,20 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { if (!checkMachOAndArchFlags(O, Filename)) return; - outs() << "\n"; - if (isa<MachOObjectFile>(O)) { - outs() << Filename << "(" << O->getFileName() << ")"; - } else - outs() << O->getFileName(); - outs() << ":\n"; - dumpSymbolNamesFromObject(O, false); + if (!PrintFileName) { + outs() << "\n"; + if (isa<MachOObjectFile>(O)) { + outs() << Filename << "(" << O->getFileName() << ")"; + } else + outs() << O->getFileName(); + outs() << ":\n"; + } + dumpSymbolNamesFromObject(*O, false, Filename); } } return; } - if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(Bin.get())) { + if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) { // If we have a list of architecture flags specified dump only those. if (!ArchAll && ArchFlags.size() != 0) { // Look for a slice in the universal binary that matches each ArchFlag. @@ -872,14 +1070,22 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); std::unique_ptr<Archive> A; + std::string ArchiveName; + std::string ArchitectureName; + ArchiveName.clear(); + ArchitectureName.clear(); if (ObjOrErr) { - std::unique_ptr<ObjectFile> Obj = std::move(ObjOrErr.get()); + ObjectFile &Obj = *ObjOrErr.get(); if (ArchFlags.size() > 1) { - outs() << "\n" << Obj->getFileName() << " (for architecture " - << I->getArchTypeName() << ")" - << ":\n"; + if (PrintFileName) + ArchitectureName = I->getArchTypeName(); + else + outs() << "\n" << Obj.getFileName() << " (for architecture " + << I->getArchTypeName() << ")" + << ":\n"; } - dumpSymbolNamesFromObject(Obj.get(), false); + dumpSymbolNamesFromObject(Obj, false, ArchiveName, + ArchitectureName); } else if (!I->getAsArchive(A)) { for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); @@ -890,14 +1096,21 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { continue; if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { - outs() << "\n" << A->getFileName(); - outs() << "(" << O->getFileName() << ")"; - if (ArchFlags.size() > 1) { - outs() << " (for architecture " << I->getArchTypeName() - << ")"; + if (PrintFileName) { + ArchiveName = A->getFileName(); + if (ArchFlags.size() > 1) + ArchitectureName = I->getArchTypeName(); + } else { + outs() << "\n" << A->getFileName(); + outs() << "(" << O->getFileName() << ")"; + if (ArchFlags.size() > 1) { + outs() << " (for architecture " << I->getArchTypeName() + << ")"; + } + outs() << ":\n"; } - outs() << ":\n"; - dumpSymbolNamesFromObject(O, false); + dumpSymbolNamesFromObject(*O, false, ArchiveName, + ArchitectureName); } } } @@ -921,9 +1134,11 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { if (HostArchName == I->getArchTypeName()) { ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); std::unique_ptr<Archive> A; + std::string ArchiveName; + ArchiveName.clear(); if (ObjOrErr) { - std::unique_ptr<ObjectFile> Obj = std::move(ObjOrErr.get()); - dumpSymbolNamesFromObject(Obj.get(), false); + ObjectFile &Obj = *ObjOrErr.get(); + dumpSymbolNamesFromObject(Obj, false); } else if (!I->getAsArchive(A)) { for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); @@ -934,10 +1149,13 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { continue; if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { - outs() << "\n" << A->getFileName() << "(" << O->getFileName() - << ")" - << ":\n"; - dumpSymbolNamesFromObject(O, false); + if (PrintFileName) + ArchiveName = A->getFileName(); + else + outs() << "\n" << A->getFileName() << "(" << O->getFileName() + << ")" + << ":\n"; + dumpSymbolNamesFromObject(*O, false, ArchiveName); } } } @@ -953,15 +1171,24 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { I != E; ++I) { ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); std::unique_ptr<Archive> A; + std::string ArchiveName; + std::string ArchitectureName; + ArchiveName.clear(); + ArchitectureName.clear(); if (ObjOrErr) { - std::unique_ptr<ObjectFile> Obj = std::move(ObjOrErr.get()); - if (moreThanOneArch) - outs() << "\n"; - outs() << Obj->getFileName(); - if (isa<MachOObjectFile>(Obj.get()) && moreThanOneArch) - outs() << " (for architecture " << I->getArchTypeName() << ")"; - outs() << ":\n"; - dumpSymbolNamesFromObject(Obj.get(), false); + ObjectFile &Obj = *ObjOrErr.get(); + if (PrintFileName) { + if (isa<MachOObjectFile>(Obj) && moreThanOneArch) + ArchitectureName = I->getArchTypeName(); + } else { + if (moreThanOneArch) + outs() << "\n"; + outs() << Obj.getFileName(); + if (isa<MachOObjectFile>(Obj) && moreThanOneArch) + outs() << " (for architecture " << I->getArchTypeName() << ")"; + outs() << ":\n"; + } + dumpSymbolNamesFromObject(Obj, false, ArchiveName, ArchitectureName); } else if (!I->getAsArchive(A)) { for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); AI != AE; ++AI) { @@ -970,25 +1197,32 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { if (ChildOrErr.getError()) continue; if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { - outs() << "\n" << A->getFileName(); - if (isa<MachOObjectFile>(O)) { - outs() << "(" << O->getFileName() << ")"; - if (moreThanOneArch) - outs() << " (for architecture " << I->getArchTypeName() << ")"; - } else - outs() << ":" << O->getFileName(); - outs() << ":\n"; - dumpSymbolNamesFromObject(O, false); + if (PrintFileName) { + ArchiveName = A->getFileName(); + if (isa<MachOObjectFile>(O) && moreThanOneArch) + ArchitectureName = I->getArchTypeName(); + } else { + outs() << "\n" << A->getFileName(); + if (isa<MachOObjectFile>(O)) { + outs() << "(" << O->getFileName() << ")"; + if (moreThanOneArch) + outs() << " (for architecture " << I->getArchTypeName() + << ")"; + } else + outs() << ":" << O->getFileName(); + outs() << ":\n"; + } + dumpSymbolNamesFromObject(*O, false, ArchiveName, ArchitectureName); } } } } return; } - if (SymbolicFile *O = dyn_cast<SymbolicFile>(Bin.get())) { + if (SymbolicFile *O = dyn_cast<SymbolicFile>(&Bin)) { if (!checkMachOAndArchFlags(O, Filename)) return; - dumpSymbolNamesFromObject(O, true); + dumpSymbolNamesFromObject(*O, true); return; } error("unrecognizable file type", Filename); @@ -1040,13 +1274,16 @@ int main(int argc, char **argv) { if (ArchFlags[i] == "all") { ArchAll = true; } else { - Triple T = MachOObjectFile::getArch(ArchFlags[i]); - if (T.getArch() == Triple::UnknownArch) + if (!MachOObjectFile::isValidArch(ArchFlags[i])) error("Unknown architecture named '" + ArchFlags[i] + "'", "for the -arch option"); } } + if (SegSect.size() != 0 && SegSect.size() != 2) + error("bad number of arguments (must be two arguments)", + "for the -s option"); + std::for_each(InputFilenames.begin(), InputFilenames.end(), dumpSymbolNamesFromFile); diff --git a/tools/llvm-objdump/Android.mk b/tools/llvm-objdump/Android.mk index 8105ebf..077e0ee 100644 --- a/tools/llvm-objdump/Android.mk +++ b/tools/llvm-objdump/Android.mk @@ -34,14 +34,16 @@ llvm_objdump_STATIC_LIBRARIES := \ libLLVMX86Info \ libLLVMX86Desc \ libLLVMX86AsmParser \ + libLLVMX86CodeGen \ libLLVMX86AsmPrinter \ libLLVMX86Utils \ libLLVMX86Disassembler \ libLLVMAsmPrinter \ + libLLVMCodeGen \ + libLLVMAnalysis \ libLLVMTarget \ libLLVMObject \ libLLVMMCParser \ - libLLVMMCAnalysis \ libLLVMMC \ libLLVMMCDisassembler \ libLLVMBitReader \ diff --git a/tools/llvm-objdump/CMakeLists.txt b/tools/llvm-objdump/CMakeLists.txt index d63602b..61bf3b3 100644 --- a/tools/llvm-objdump/CMakeLists.txt +++ b/tools/llvm-objdump/CMakeLists.txt @@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} DebugInfo MC - MCAnalysis + MCDisassembler Object Support ) diff --git a/tools/llvm-objdump/COFFDump.cpp b/tools/llvm-objdump/COFFDump.cpp index 39d8e8e..4a20b91 100644 --- a/tools/llvm-objdump/COFFDump.cpp +++ b/tools/llvm-objdump/COFFDump.cpp @@ -260,11 +260,8 @@ static void printLoadConfiguration(const COFFObjectFile *Obj) { if (!PE32Header) return; - const coff_file_header *Header; - if (error(Obj->getCOFFHeader(Header))) - return; // Currently only x86 is supported - if (Header->Machine != COFF::IMAGE_FILE_MACHINE_I386) + if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_I386) return; const data_directory *DataDir; @@ -325,7 +322,7 @@ static void printImportTables(const COFFObjectFile *Obj) { const import_lookup_table_entry32 *entry; if (I->getImportLookupEntry(entry)) return; - for (; entry->data; ++entry) { + for (; entry->Data; ++entry) { if (entry->isOrdinal()) { outs() << format(" % 6d\n", entry->getOrdinal()); continue; @@ -518,11 +515,7 @@ static void printRuntimeFunctionRels(const COFFObjectFile *Obj, } void llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) { - const coff_file_header *Header; - if (error(Obj->getCOFFHeader(Header))) - return; - - if (Header->Machine != COFF::IMAGE_FILE_MACHINE_AMD64) { + if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_AMD64) { errs() << "Unsupported image machine type " "(currently only AMD64 is supported).\n"; return; diff --git a/tools/llvm-objdump/LLVMBuild.txt b/tools/llvm-objdump/LLVMBuild.txt index d9c09b6..d16c501 100644 --- a/tools/llvm-objdump/LLVMBuild.txt +++ b/tools/llvm-objdump/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-objdump parent = Tools -required_libraries = DebugInfo MC MCAnalysis MCDisassembler MCParser Object all-targets +required_libraries = DebugInfo MC MCDisassembler MCParser Object all-targets diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 4b46ac4..3a28703 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -12,16 +12,17 @@ //===----------------------------------------------------------------------===// #include "llvm-objdump.h" +#include "llvm-c/Disassembler.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" +#include "llvm/Config/config.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstPrinter.h" -#include "llvm/MC/MCInstrAnalysis.h" #include "llvm/MC/MCInstrDesc.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" @@ -30,41 +31,70 @@ #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" #include "llvm/Support/GraphWriter.h" #include "llvm/Support/MachO.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/FormattedStream.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cstring> #include <system_error> + +#if HAVE_CXXABI_H +#include <cxxabi.h> +#endif + using namespace llvm; using namespace object; static cl::opt<bool> - UseDbg("g", cl::desc("Print line information from debug info if available")); + UseDbg("g", + cl::desc("Print line information from debug info if available")); -static cl::opt<std::string> - DSYMFile("dsym", cl::desc("Use .dSYM file for debug info")); +static cl::opt<std::string> DSYMFile("dsym", + cl::desc("Use .dSYM file for debug info")); + +static cl::opt<bool> FullLeadingAddr("full-leading-addr", + cl::desc("Print full leading address")); + +static cl::opt<bool> + PrintImmHex("print-imm-hex", + cl::desc("Use hex format for immediate values")); -static const Target *GetTarget(const MachOObjectFile *MachOObj) { +static std::string ThumbTripleName; + +static const Target *GetTarget(const MachOObjectFile *MachOObj, + const char **McpuDefault, + const Target **ThumbTarget) { // Figure out the target triple. if (TripleName.empty()) { llvm::Triple TT("unknown-unknown-unknown"); - TT.setArch(Triple::ArchType(MachOObj->getArch())); + llvm::Triple ThumbTriple = Triple(); + TT = MachOObj->getArch(McpuDefault, &ThumbTriple); TripleName = TT.str(); + ThumbTripleName = ThumbTriple.str(); } // Get the target specific parser. std::string Error; const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error); - if (TheTarget) + if (TheTarget && ThumbTripleName.empty()) + return TheTarget; + + *ThumbTarget = TargetRegistry::lookupTarget(ThumbTripleName, Error); + if (*ThumbTarget) return TheTarget; - errs() << "llvm-objdump: error: unable to get target for '" << TripleName - << "', see --version and --triple.\n"; + errs() << "llvm-objdump: error: unable to get target for '"; + if (!TheTarget) + errs() << TripleName; + else + errs() << ThumbTripleName; + errs() << "', see --version and --triple.\n"; return nullptr; } @@ -93,58 +123,80 @@ typedef std::pair<uint64_t, DiceRef> DiceTableEntry; typedef std::vector<DiceTableEntry> DiceTable; typedef DiceTable::iterator dice_table_iterator; -static bool -compareDiceTableEntries(const DiceTableEntry i, - const DiceTableEntry j) { - return i.first == j.first; +// This is used to search for a data in code table entry for the PC being +// disassembled. The j parameter has the PC in j.first. A single data in code +// table entry can cover many bytes for each of its Kind's. So if the offset, +// aka the i.first value, of the data in code table entry plus its Length +// covers the PC being searched for this will return true. If not it will +// return false. +static bool compareDiceTableEntries(const DiceTableEntry &i, + const DiceTableEntry &j) { + uint16_t Length; + i.second.getLength(Length); + + return j.first >= i.first && j.first < i.first + Length; } -static void DumpDataInCode(const char *bytes, uint64_t Size, - unsigned short Kind) { - uint64_t Value; +static uint64_t DumpDataInCode(const char *bytes, uint64_t Length, + unsigned short Kind) { + uint32_t Value, Size = 1; switch (Kind) { + default: case MachO::DICE_KIND_DATA: - switch (Size) { - case 4: - Value = bytes[3] << 24 | - bytes[2] << 16 | - bytes[1] << 8 | - bytes[0]; + if (Length >= 4) { + if (!NoShowRawInsn) + DumpBytes(StringRef(bytes, 4)); + Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; - break; - case 2: - Value = bytes[1] << 8 | - bytes[0]; + Size = 4; + } else if (Length >= 2) { + if (!NoShowRawInsn) + DumpBytes(StringRef(bytes, 2)); + Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << Value; - break; - case 1: + Size = 2; + } else { + if (!NoShowRawInsn) + DumpBytes(StringRef(bytes, 2)); Value = bytes[0]; outs() << "\t.byte " << Value; - break; + Size = 1; } - outs() << "\t@ KIND_DATA\n"; + if (Kind == MachO::DICE_KIND_DATA) + outs() << "\t@ KIND_DATA\n"; + else + outs() << "\t@ data in code kind = " << Kind << "\n"; break; case MachO::DICE_KIND_JUMP_TABLE8: + if (!NoShowRawInsn) + DumpBytes(StringRef(bytes, 1)); Value = bytes[0]; - outs() << "\t.byte " << Value << "\t@ KIND_JUMP_TABLE8"; + outs() << "\t.byte " << format("%3u", Value) << "\t@ KIND_JUMP_TABLE8\n"; + Size = 1; break; case MachO::DICE_KIND_JUMP_TABLE16: - Value = bytes[1] << 8 | - bytes[0]; - outs() << "\t.short " << Value << "\t@ KIND_JUMP_TABLE16"; + if (!NoShowRawInsn) + DumpBytes(StringRef(bytes, 2)); + Value = bytes[1] << 8 | bytes[0]; + outs() << "\t.short " << format("%5u", Value & 0xffff) + << "\t@ KIND_JUMP_TABLE16\n"; + Size = 2; break; case MachO::DICE_KIND_JUMP_TABLE32: - Value = bytes[3] << 24 | - bytes[2] << 16 | - bytes[1] << 8 | - bytes[0]; - outs() << "\t.long " << Value << "\t@ KIND_JUMP_TABLE32"; - break; - default: - outs() << "\t@ data in code kind = " << Kind << "\n"; + case MachO::DICE_KIND_ABS_JUMP_TABLE32: + if (!NoShowRawInsn) + DumpBytes(StringRef(bytes, 4)); + Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; + outs() << "\t.long " << Value; + if (Kind == MachO::DICE_KIND_JUMP_TABLE32) + outs() << "\t@ KIND_JUMP_TABLE32\n"; + else + outs() << "\t@ KIND_ABS_JUMP_TABLE32\n"; + Size = 4; break; } + return Size; } static void getSectionsAndSymbols(const MachO::mach_header Header, @@ -165,20 +217,18 @@ static void getSectionsAndSymbols(const MachO::mach_header Header, MachOObjectFile::LoadCommandInfo Command = MachOObj->getFirstLoadCommandInfo(); bool BaseSegmentAddressSet = false; - for (unsigned i = 0; ; ++i) { + for (unsigned i = 0;; ++i) { if (Command.C.cmd == MachO::LC_FUNCTION_STARTS) { // We found a function starts segment, parse the addresses for later // consumption. MachO::linkedit_data_command LLC = - MachOObj->getLinkeditDataLoadCommand(Command); + MachOObj->getLinkeditDataLoadCommand(Command); MachOObj->ReadULEB128s(LLC.dataoff, FoundFns); - } - else if (Command.C.cmd == MachO::LC_SEGMENT) { - MachO::segment_command SLC = - MachOObj->getSegmentLoadCommand(Command); + } else if (Command.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command SLC = MachOObj->getSegmentLoadCommand(Command); StringRef SegName = SLC.segname; - if(!BaseSegmentAddressSet && SegName != "__PAGEZERO") { + if (!BaseSegmentAddressSet && SegName != "__PAGEZERO") { BaseSegmentAddressSet = true; BaseSegmentAddress = SLC.vmaddr; } @@ -195,29 +245,1371 @@ static void DisassembleInputMachO2(StringRef Filename, MachOObjectFile *MachOOF); void llvm::DisassembleInputMachO(StringRef Filename) { - ErrorOr<std::unique_ptr<MemoryBuffer>> Buff = + ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = MemoryBuffer::getFileOrSTDIN(Filename); - if (std::error_code EC = Buff.getError()) { + if (std::error_code EC = BuffOrErr.getError()) { errs() << "llvm-objdump: " << Filename << ": " << EC.message() << "\n"; return; } + std::unique_ptr<MemoryBuffer> Buff = std::move(BuffOrErr.get()); - std::unique_ptr<MachOObjectFile> MachOOF(static_cast<MachOObjectFile *>( - ObjectFile::createMachOObjectFile(Buff.get()).get())); + std::unique_ptr<MachOObjectFile> MachOOF = std::move( + ObjectFile::createMachOObjectFile(Buff.get()->getMemBufferRef()).get()); DisassembleInputMachO2(Filename, MachOOF.get()); } +typedef DenseMap<uint64_t, StringRef> SymbolAddressMap; +typedef std::pair<uint64_t, const char *> BindInfoEntry; +typedef std::vector<BindInfoEntry> BindTable; +typedef BindTable::iterator bind_table_iterator; + +// The block of info used by the Symbolizer call backs. +struct DisassembleInfo { + bool verbose; + MachOObjectFile *O; + SectionRef S; + SymbolAddressMap *AddrMap; + std::vector<SectionRef> *Sections; + const char *class_name; + const char *selector_name; + char *method; + char *demangled_name; + uint64_t adrp_addr; + uint32_t adrp_inst; + BindTable *bindtable; +}; + +// GuessSymbolName is passed the address of what might be a symbol and a +// pointer to the DisassembleInfo struct. It returns the name of a symbol +// with that address or nullptr if no symbol is found with that address. +static const char *GuessSymbolName(uint64_t value, + struct DisassembleInfo *info) { + const char *SymbolName = nullptr; + // A DenseMap can't lookup up some values. + if (value != 0xffffffffffffffffULL && value != 0xfffffffffffffffeULL) { + StringRef name = info->AddrMap->lookup(value); + if (!name.empty()) + SymbolName = name.data(); + } + return SymbolName; +} + +// SymbolizerGetOpInfo() is the operand information call back function. +// This is called to get the symbolic information for operand(s) of an +// instruction when it is being done. This routine does this from +// the relocation information, symbol table, etc. That block of information +// is a pointer to the struct DisassembleInfo that was passed when the +// disassembler context was created and passed to back to here when +// called back by the disassembler for instruction operands that could have +// relocation information. The address of the instruction containing operand is +// at the Pc parameter. The immediate value the operand has is passed in +// op_info->Value and is at Offset past the start of the instruction and has a +// byte Size of 1, 2 or 4. The symbolc information is returned in TagBuf is the +// LLVMOpInfo1 struct defined in the header "llvm-c/Disassembler.h" as symbol +// names and addends of the symbolic expression to add for the operand. The +// value of TagType is currently 1 (for the LLVMOpInfo1 struct). If symbolic +// information is returned then this function returns 1 else it returns 0. +int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, + uint64_t Size, int TagType, void *TagBuf) { + struct DisassembleInfo *info = (struct DisassembleInfo *)DisInfo; + struct LLVMOpInfo1 *op_info = (struct LLVMOpInfo1 *)TagBuf; + uint64_t value = op_info->Value; + + // Make sure all fields returned are zero if we don't set them. + memset((void *)op_info, '\0', sizeof(struct LLVMOpInfo1)); + op_info->Value = value; + + // If the TagType is not the value 1 which it code knows about or if no + // verbose symbolic information is wanted then just return 0, indicating no + // information is being returned. + if (TagType != 1 || info->verbose == false) + return 0; + + unsigned int Arch = info->O->getArch(); + if (Arch == Triple::x86) { + if (Size != 1 && Size != 2 && Size != 4 && Size != 0) + return 0; + // First search the section's relocation entries (if any) for an entry + // for this section offset. + uint32_t sect_addr = info->S.getAddress(); + uint32_t sect_offset = (Pc + Offset) - sect_addr; + bool reloc_found = false; + DataRefImpl Rel; + MachO::any_relocation_info RE; + bool isExtern = false; + SymbolRef Symbol; + bool r_scattered = false; + uint32_t r_value, pair_r_value, r_type; + for (const RelocationRef &Reloc : info->S.relocations()) { + uint64_t RelocOffset; + Reloc.getOffset(RelocOffset); + if (RelocOffset == sect_offset) { + Rel = Reloc.getRawDataRefImpl(); + RE = info->O->getRelocation(Rel); + r_type = info->O->getAnyRelocationType(RE); + r_scattered = info->O->isRelocationScattered(RE); + if (r_scattered) { + r_value = info->O->getScatteredRelocationValue(RE); + if (r_type == MachO::GENERIC_RELOC_SECTDIFF || + r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF) { + DataRefImpl RelNext = Rel; + info->O->moveRelocationNext(RelNext); + MachO::any_relocation_info RENext; + RENext = info->O->getRelocation(RelNext); + if (info->O->isRelocationScattered(RENext)) + pair_r_value = info->O->getScatteredRelocationValue(RENext); + else + return 0; + } + } else { + isExtern = info->O->getPlainRelocationExternal(RE); + if (isExtern) { + symbol_iterator RelocSym = Reloc.getSymbol(); + Symbol = *RelocSym; + } + } + reloc_found = true; + break; + } + } + if (reloc_found && isExtern) { + StringRef SymName; + Symbol.getName(SymName); + const char *name = SymName.data(); + op_info->AddSymbol.Present = 1; + op_info->AddSymbol.Name = name; + // For i386 extern relocation entries the value in the instruction is + // the offset from the symbol, and value is already set in op_info->Value. + return 1; + } + if (reloc_found && (r_type == MachO::GENERIC_RELOC_SECTDIFF || + r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) { + const char *add = GuessSymbolName(r_value, info); + const char *sub = GuessSymbolName(pair_r_value, info); + uint32_t offset = value - (r_value - pair_r_value); + op_info->AddSymbol.Present = 1; + if (add != nullptr) + op_info->AddSymbol.Name = add; + else + op_info->AddSymbol.Value = r_value; + op_info->SubtractSymbol.Present = 1; + if (sub != nullptr) + op_info->SubtractSymbol.Name = sub; + else + op_info->SubtractSymbol.Value = pair_r_value; + op_info->Value = offset; + return 1; + } + // TODO: + // Second search the external relocation entries of a fully linked image + // (if any) for an entry that matches this segment offset. + // uint32_t seg_offset = (Pc + Offset); + return 0; + } else if (Arch == Triple::x86_64) { + if (Size != 1 && Size != 2 && Size != 4 && Size != 0) + return 0; + // First search the section's relocation entries (if any) for an entry + // for this section offset. + uint64_t sect_addr = info->S.getAddress(); + uint64_t sect_offset = (Pc + Offset) - sect_addr; + bool reloc_found = false; + DataRefImpl Rel; + MachO::any_relocation_info RE; + bool isExtern = false; + SymbolRef Symbol; + for (const RelocationRef &Reloc : info->S.relocations()) { + uint64_t RelocOffset; + Reloc.getOffset(RelocOffset); + if (RelocOffset == sect_offset) { + Rel = Reloc.getRawDataRefImpl(); + RE = info->O->getRelocation(Rel); + // NOTE: Scattered relocations don't exist on x86_64. + isExtern = info->O->getPlainRelocationExternal(RE); + if (isExtern) { + symbol_iterator RelocSym = Reloc.getSymbol(); + Symbol = *RelocSym; + } + reloc_found = true; + break; + } + } + if (reloc_found && isExtern) { + // The Value passed in will be adjusted by the Pc if the instruction + // adds the Pc. But for x86_64 external relocation entries the Value + // is the offset from the external symbol. + if (info->O->getAnyRelocationPCRel(RE)) + op_info->Value -= Pc + Offset + Size; + StringRef SymName; + Symbol.getName(SymName); + const char *name = SymName.data(); + unsigned Type = info->O->getAnyRelocationType(RE); + if (Type == MachO::X86_64_RELOC_SUBTRACTOR) { + DataRefImpl RelNext = Rel; + info->O->moveRelocationNext(RelNext); + MachO::any_relocation_info RENext = info->O->getRelocation(RelNext); + unsigned TypeNext = info->O->getAnyRelocationType(RENext); + bool isExternNext = info->O->getPlainRelocationExternal(RENext); + unsigned SymbolNum = info->O->getPlainRelocationSymbolNum(RENext); + if (TypeNext == MachO::X86_64_RELOC_UNSIGNED && isExternNext) { + op_info->SubtractSymbol.Present = 1; + op_info->SubtractSymbol.Name = name; + symbol_iterator RelocSymNext = info->O->getSymbolByIndex(SymbolNum); + Symbol = *RelocSymNext; + StringRef SymNameNext; + Symbol.getName(SymNameNext); + name = SymNameNext.data(); + } + } + // TODO: add the VariantKinds to op_info->VariantKind for relocation types + // like: X86_64_RELOC_TLV, X86_64_RELOC_GOT_LOAD and X86_64_RELOC_GOT. + op_info->AddSymbol.Present = 1; + op_info->AddSymbol.Name = name; + return 1; + } + // TODO: + // Second search the external relocation entries of a fully linked image + // (if any) for an entry that matches this segment offset. + // uint64_t seg_offset = (Pc + Offset); + return 0; + } else if (Arch == Triple::arm) { + if (Offset != 0 || (Size != 4 && Size != 2)) + return 0; + // First search the section's relocation entries (if any) for an entry + // for this section offset. + uint32_t sect_addr = info->S.getAddress(); + uint32_t sect_offset = (Pc + Offset) - sect_addr; + bool reloc_found = false; + DataRefImpl Rel; + MachO::any_relocation_info RE; + bool isExtern = false; + SymbolRef Symbol; + bool r_scattered = false; + uint32_t r_value, pair_r_value, r_type, r_length, other_half; + for (const RelocationRef &Reloc : info->S.relocations()) { + uint64_t RelocOffset; + Reloc.getOffset(RelocOffset); + if (RelocOffset == sect_offset) { + Rel = Reloc.getRawDataRefImpl(); + RE = info->O->getRelocation(Rel); + r_length = info->O->getAnyRelocationLength(RE); + r_scattered = info->O->isRelocationScattered(RE); + if (r_scattered) { + r_value = info->O->getScatteredRelocationValue(RE); + r_type = info->O->getScatteredRelocationType(RE); + } else { + r_type = info->O->getAnyRelocationType(RE); + isExtern = info->O->getPlainRelocationExternal(RE); + if (isExtern) { + symbol_iterator RelocSym = Reloc.getSymbol(); + Symbol = *RelocSym; + } + } + if (r_type == MachO::ARM_RELOC_HALF || + r_type == MachO::ARM_RELOC_SECTDIFF || + r_type == MachO::ARM_RELOC_LOCAL_SECTDIFF || + r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { + DataRefImpl RelNext = Rel; + info->O->moveRelocationNext(RelNext); + MachO::any_relocation_info RENext; + RENext = info->O->getRelocation(RelNext); + other_half = info->O->getAnyRelocationAddress(RENext) & 0xffff; + if (info->O->isRelocationScattered(RENext)) + pair_r_value = info->O->getScatteredRelocationValue(RENext); + } + reloc_found = true; + break; + } + } + if (reloc_found && isExtern) { + StringRef SymName; + Symbol.getName(SymName); + const char *name = SymName.data(); + op_info->AddSymbol.Present = 1; + op_info->AddSymbol.Name = name; + if (value != 0) { + switch (r_type) { + case MachO::ARM_RELOC_HALF: + if ((r_length & 0x1) == 1) { + op_info->Value = value << 16 | other_half; + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; + } else { + op_info->Value = other_half << 16 | value; + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; + } + break; + default: + break; + } + } else { + switch (r_type) { + case MachO::ARM_RELOC_HALF: + if ((r_length & 0x1) == 1) { + op_info->Value = value << 16 | other_half; + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; + } else { + op_info->Value = other_half << 16 | value; + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; + } + break; + default: + break; + } + } + return 1; + } + // If we have a branch that is not an external relocation entry then + // return 0 so the code in tryAddingSymbolicOperand() can use the + // SymbolLookUp call back with the branch target address to look up the + // symbol and possiblity add an annotation for a symbol stub. + if (reloc_found && isExtern == 0 && (r_type == MachO::ARM_RELOC_BR24 || + r_type == MachO::ARM_THUMB_RELOC_BR22)) + return 0; + + uint32_t offset = 0; + if (reloc_found) { + if (r_type == MachO::ARM_RELOC_HALF || + r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { + if ((r_length & 0x1) == 1) + value = value << 16 | other_half; + else + value = other_half << 16 | value; + } + if (r_scattered && (r_type != MachO::ARM_RELOC_HALF && + r_type != MachO::ARM_RELOC_HALF_SECTDIFF)) { + offset = value - r_value; + value = r_value; + } + } + + if (reloc_found && r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { + if ((r_length & 0x1) == 1) + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; + else + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; + const char *add = GuessSymbolName(r_value, info); + const char *sub = GuessSymbolName(pair_r_value, info); + int32_t offset = value - (r_value - pair_r_value); + op_info->AddSymbol.Present = 1; + if (add != nullptr) + op_info->AddSymbol.Name = add; + else + op_info->AddSymbol.Value = r_value; + op_info->SubtractSymbol.Present = 1; + if (sub != nullptr) + op_info->SubtractSymbol.Name = sub; + else + op_info->SubtractSymbol.Value = pair_r_value; + op_info->Value = offset; + return 1; + } + + if (reloc_found == false) + return 0; + + op_info->AddSymbol.Present = 1; + op_info->Value = offset; + if (reloc_found) { + if (r_type == MachO::ARM_RELOC_HALF) { + if ((r_length & 0x1) == 1) + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; + else + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; + } + } + const char *add = GuessSymbolName(value, info); + if (add != nullptr) { + op_info->AddSymbol.Name = add; + return 1; + } + op_info->AddSymbol.Value = value; + return 1; + } else if (Arch == Triple::aarch64) { + if (Offset != 0 || Size != 4) + return 0; + // First search the section's relocation entries (if any) for an entry + // for this section offset. + uint64_t sect_addr = info->S.getAddress(); + uint64_t sect_offset = (Pc + Offset) - sect_addr; + bool reloc_found = false; + DataRefImpl Rel; + MachO::any_relocation_info RE; + bool isExtern = false; + SymbolRef Symbol; + uint32_t r_type = 0; + for (const RelocationRef &Reloc : info->S.relocations()) { + uint64_t RelocOffset; + Reloc.getOffset(RelocOffset); + if (RelocOffset == sect_offset) { + Rel = Reloc.getRawDataRefImpl(); + RE = info->O->getRelocation(Rel); + r_type = info->O->getAnyRelocationType(RE); + if (r_type == MachO::ARM64_RELOC_ADDEND) { + DataRefImpl RelNext = Rel; + info->O->moveRelocationNext(RelNext); + MachO::any_relocation_info RENext = info->O->getRelocation(RelNext); + if (value == 0) { + value = info->O->getPlainRelocationSymbolNum(RENext); + op_info->Value = value; + } + } + // NOTE: Scattered relocations don't exist on arm64. + isExtern = info->O->getPlainRelocationExternal(RE); + if (isExtern) { + symbol_iterator RelocSym = Reloc.getSymbol(); + Symbol = *RelocSym; + } + reloc_found = true; + break; + } + } + if (reloc_found && isExtern) { + StringRef SymName; + Symbol.getName(SymName); + const char *name = SymName.data(); + op_info->AddSymbol.Present = 1; + op_info->AddSymbol.Name = name; + + switch (r_type) { + case MachO::ARM64_RELOC_PAGE21: + /* @page */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_PAGE; + break; + case MachO::ARM64_RELOC_PAGEOFF12: + /* @pageoff */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_PAGEOFF; + break; + case MachO::ARM64_RELOC_GOT_LOAD_PAGE21: + /* @gotpage */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_GOTPAGE; + break; + case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: + /* @gotpageoff */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_GOTPAGEOFF; + break; + case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21: + /* @tvlppage is not implemented in llvm-mc */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_TLVP; + break; + case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12: + /* @tvlppageoff is not implemented in llvm-mc */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_TLVOFF; + break; + default: + case MachO::ARM64_RELOC_BRANCH26: + op_info->VariantKind = LLVMDisassembler_VariantKind_None; + break; + } + return 1; + } + return 0; + } else { + return 0; + } +} + +// GuessCstringPointer is passed the address of what might be a pointer to a +// literal string in a cstring section. If that address is in a cstring section +// it returns a pointer to that string. Else it returns nullptr. +const char *GuessCstringPointer(uint64_t ReferenceValue, + struct DisassembleInfo *info) { + uint32_t LoadCommandCount = info->O->getHeader().ncmds; + MachOObjectFile::LoadCommandInfo Load = info->O->getFirstLoadCommandInfo(); + for (unsigned I = 0;; ++I) { + if (Load.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 Seg = info->O->getSegment64LoadCommand(Load); + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section_64 Sec = info->O->getSection64(Load, J); + uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; + if (section_type == MachO::S_CSTRING_LITERALS && + ReferenceValue >= Sec.addr && + ReferenceValue < Sec.addr + Sec.size) { + uint64_t sect_offset = ReferenceValue - Sec.addr; + uint64_t object_offset = Sec.offset + sect_offset; + StringRef MachOContents = info->O->getData(); + uint64_t object_size = MachOContents.size(); + const char *object_addr = (const char *)MachOContents.data(); + if (object_offset < object_size) { + const char *name = object_addr + object_offset; + return name; + } else { + return nullptr; + } + } + } + } else if (Load.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command Seg = info->O->getSegmentLoadCommand(Load); + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section Sec = info->O->getSection(Load, J); + uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; + if (section_type == MachO::S_CSTRING_LITERALS && + ReferenceValue >= Sec.addr && + ReferenceValue < Sec.addr + Sec.size) { + uint64_t sect_offset = ReferenceValue - Sec.addr; + uint64_t object_offset = Sec.offset + sect_offset; + StringRef MachOContents = info->O->getData(); + uint64_t object_size = MachOContents.size(); + const char *object_addr = (const char *)MachOContents.data(); + if (object_offset < object_size) { + const char *name = object_addr + object_offset; + return name; + } else { + return nullptr; + } + } + } + } + if (I == LoadCommandCount - 1) + break; + else + Load = info->O->getNextLoadCommandInfo(Load); + } + return nullptr; +} + +// GuessIndirectSymbol returns the name of the indirect symbol for the +// ReferenceValue passed in or nullptr. This is used when ReferenceValue maybe +// an address of a symbol stub or a lazy or non-lazy pointer to associate the +// symbol name being referenced by the stub or pointer. +static const char *GuessIndirectSymbol(uint64_t ReferenceValue, + struct DisassembleInfo *info) { + uint32_t LoadCommandCount = info->O->getHeader().ncmds; + MachOObjectFile::LoadCommandInfo Load = info->O->getFirstLoadCommandInfo(); + MachO::dysymtab_command Dysymtab = info->O->getDysymtabLoadCommand(); + MachO::symtab_command Symtab = info->O->getSymtabLoadCommand(); + for (unsigned I = 0;; ++I) { + if (Load.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 Seg = info->O->getSegment64LoadCommand(Load); + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section_64 Sec = info->O->getSection64(Load, J); + uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; + if ((section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS || + section_type == MachO::S_LAZY_SYMBOL_POINTERS || + section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS || + section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS || + section_type == MachO::S_SYMBOL_STUBS) && + ReferenceValue >= Sec.addr && + ReferenceValue < Sec.addr + Sec.size) { + uint32_t stride; + if (section_type == MachO::S_SYMBOL_STUBS) + stride = Sec.reserved2; + else + stride = 8; + if (stride == 0) + return nullptr; + uint32_t index = Sec.reserved1 + (ReferenceValue - Sec.addr) / stride; + if (index < Dysymtab.nindirectsyms) { + uint32_t indirect_symbol = + info->O->getIndirectSymbolTableEntry(Dysymtab, index); + if (indirect_symbol < Symtab.nsyms) { + symbol_iterator Sym = info->O->getSymbolByIndex(indirect_symbol); + SymbolRef Symbol = *Sym; + StringRef SymName; + Symbol.getName(SymName); + const char *name = SymName.data(); + return name; + } + } + } + } + } else if (Load.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command Seg = info->O->getSegmentLoadCommand(Load); + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section Sec = info->O->getSection(Load, J); + uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; + if ((section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS || + section_type == MachO::S_LAZY_SYMBOL_POINTERS || + section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS || + section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS || + section_type == MachO::S_SYMBOL_STUBS) && + ReferenceValue >= Sec.addr && + ReferenceValue < Sec.addr + Sec.size) { + uint32_t stride; + if (section_type == MachO::S_SYMBOL_STUBS) + stride = Sec.reserved2; + else + stride = 4; + if (stride == 0) + return nullptr; + uint32_t index = Sec.reserved1 + (ReferenceValue - Sec.addr) / stride; + if (index < Dysymtab.nindirectsyms) { + uint32_t indirect_symbol = + info->O->getIndirectSymbolTableEntry(Dysymtab, index); + if (indirect_symbol < Symtab.nsyms) { + symbol_iterator Sym = info->O->getSymbolByIndex(indirect_symbol); + SymbolRef Symbol = *Sym; + StringRef SymName; + Symbol.getName(SymName); + const char *name = SymName.data(); + return name; + } + } + } + } + } + if (I == LoadCommandCount - 1) + break; + else + Load = info->O->getNextLoadCommandInfo(Load); + } + return nullptr; +} + +// method_reference() is called passing it the ReferenceName that might be +// a reference it to an Objective-C method call. If so then it allocates and +// assembles a method call string with the values last seen and saved in +// the DisassembleInfo's class_name and selector_name fields. This is saved +// into the method field of the info and any previous string is free'ed. +// Then the class_name field in the info is set to nullptr. The method call +// string is set into ReferenceName and ReferenceType is set to +// LLVMDisassembler_ReferenceType_Out_Objc_Message. If this not a method call +// then both ReferenceType and ReferenceName are left unchanged. +static void method_reference(struct DisassembleInfo *info, + uint64_t *ReferenceType, + const char **ReferenceName) { + unsigned int Arch = info->O->getArch(); + if (*ReferenceName != nullptr) { + if (strcmp(*ReferenceName, "_objc_msgSend") == 0) { + if (info->selector_name != nullptr) { + if (info->method != nullptr) + free(info->method); + if (info->class_name != nullptr) { + info->method = (char *)malloc(5 + strlen(info->class_name) + + strlen(info->selector_name)); + if (info->method != nullptr) { + strcpy(info->method, "+["); + strcat(info->method, info->class_name); + strcat(info->method, " "); + strcat(info->method, info->selector_name); + strcat(info->method, "]"); + *ReferenceName = info->method; + *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message; + } + } else { + info->method = (char *)malloc(9 + strlen(info->selector_name)); + if (info->method != nullptr) { + if (Arch == Triple::x86_64) + strcpy(info->method, "-[%rdi "); + else if (Arch == Triple::aarch64) + strcpy(info->method, "-[x0 "); + else + strcpy(info->method, "-[r? "); + strcat(info->method, info->selector_name); + strcat(info->method, "]"); + *ReferenceName = info->method; + *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message; + } + } + info->class_name = nullptr; + } + } else if (strcmp(*ReferenceName, "_objc_msgSendSuper2") == 0) { + if (info->selector_name != nullptr) { + if (info->method != nullptr) + free(info->method); + info->method = (char *)malloc(17 + strlen(info->selector_name)); + if (info->method != nullptr) { + if (Arch == Triple::x86_64) + strcpy(info->method, "-[[%rdi super] "); + else if (Arch == Triple::aarch64) + strcpy(info->method, "-[[x0 super] "); + else + strcpy(info->method, "-[[r? super] "); + strcat(info->method, info->selector_name); + strcat(info->method, "]"); + *ReferenceName = info->method; + *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message; + } + info->class_name = nullptr; + } + } + } +} + +// GuessPointerPointer() is passed the address of what might be a pointer to +// a reference to an Objective-C class, selector, message ref or cfstring. +// If so the value of the pointer is returned and one of the booleans are set +// to true. If not zero is returned and all the booleans are set to false. +static uint64_t GuessPointerPointer(uint64_t ReferenceValue, + struct DisassembleInfo *info, + bool &classref, bool &selref, bool &msgref, + bool &cfstring) { + classref = false; + selref = false; + msgref = false; + cfstring = false; + uint32_t LoadCommandCount = info->O->getHeader().ncmds; + MachOObjectFile::LoadCommandInfo Load = info->O->getFirstLoadCommandInfo(); + for (unsigned I = 0;; ++I) { + if (Load.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 Seg = info->O->getSegment64LoadCommand(Load); + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section_64 Sec = info->O->getSection64(Load, J); + if ((strncmp(Sec.sectname, "__objc_selrefs", 16) == 0 || + strncmp(Sec.sectname, "__objc_classrefs", 16) == 0 || + strncmp(Sec.sectname, "__objc_superrefs", 16) == 0 || + strncmp(Sec.sectname, "__objc_msgrefs", 16) == 0 || + strncmp(Sec.sectname, "__cfstring", 16) == 0) && + ReferenceValue >= Sec.addr && + ReferenceValue < Sec.addr + Sec.size) { + uint64_t sect_offset = ReferenceValue - Sec.addr; + uint64_t object_offset = Sec.offset + sect_offset; + StringRef MachOContents = info->O->getData(); + uint64_t object_size = MachOContents.size(); + const char *object_addr = (const char *)MachOContents.data(); + if (object_offset < object_size) { + uint64_t pointer_value; + memcpy(&pointer_value, object_addr + object_offset, + sizeof(uint64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(pointer_value); + if (strncmp(Sec.sectname, "__objc_selrefs", 16) == 0) + selref = true; + else if (strncmp(Sec.sectname, "__objc_classrefs", 16) == 0 || + strncmp(Sec.sectname, "__objc_superrefs", 16) == 0) + classref = true; + else if (strncmp(Sec.sectname, "__objc_msgrefs", 16) == 0 && + ReferenceValue + 8 < Sec.addr + Sec.size) { + msgref = true; + memcpy(&pointer_value, object_addr + object_offset + 8, + sizeof(uint64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(pointer_value); + } else if (strncmp(Sec.sectname, "__cfstring", 16) == 0) + cfstring = true; + return pointer_value; + } else { + return 0; + } + } + } + } + // TODO: Look for LC_SEGMENT for 32-bit Mach-O files. + if (I == LoadCommandCount - 1) + break; + else + Load = info->O->getNextLoadCommandInfo(Load); + } + return 0; +} + +// get_pointer_64 returns a pointer to the bytes in the object file at the +// Address from a section in the Mach-O file. And indirectly returns the +// offset into the section, number of bytes left in the section past the offset +// and which section is was being referenced. If the Address is not in a +// section nullptr is returned. +const char *get_pointer_64(uint64_t Address, uint32_t &offset, uint32_t &left, + SectionRef &S, DisassembleInfo *info) { + offset = 0; + left = 0; + S = SectionRef(); + for (unsigned SectIdx = 0; SectIdx != info->Sections->size(); SectIdx++) { + uint64_t SectAddress = ((*(info->Sections))[SectIdx]).getAddress(); + uint64_t SectSize = ((*(info->Sections))[SectIdx]).getSize(); + if (Address >= SectAddress && Address < SectAddress + SectSize) { + S = (*(info->Sections))[SectIdx]; + offset = Address - SectAddress; + left = SectSize - offset; + StringRef SectContents; + ((*(info->Sections))[SectIdx]).getContents(SectContents); + return SectContents.data() + offset; + } + } + return nullptr; +} + +// get_symbol_64() returns the name of a symbol (or nullptr) and the address of +// the symbol indirectly through n_value. Based on the relocation information +// for the specified section offset in the specified section reference. +const char *get_symbol_64(uint32_t sect_offset, SectionRef S, + DisassembleInfo *info, uint64_t &n_value) { + n_value = 0; + if (info->verbose == false) + return nullptr; + + // See if there is an external relocation entry at the sect_offset. + bool reloc_found = false; + DataRefImpl Rel; + MachO::any_relocation_info RE; + bool isExtern = false; + SymbolRef Symbol; + for (const RelocationRef &Reloc : S.relocations()) { + uint64_t RelocOffset; + Reloc.getOffset(RelocOffset); + if (RelocOffset == sect_offset) { + Rel = Reloc.getRawDataRefImpl(); + RE = info->O->getRelocation(Rel); + if (info->O->isRelocationScattered(RE)) + continue; + isExtern = info->O->getPlainRelocationExternal(RE); + if (isExtern) { + symbol_iterator RelocSym = Reloc.getSymbol(); + Symbol = *RelocSym; + } + reloc_found = true; + break; + } + } + // If there is an external relocation entry for a symbol in this section + // at this section_offset then use that symbol's value for the n_value + // and return its name. + const char *SymbolName = nullptr; + if (reloc_found && isExtern) { + Symbol.getAddress(n_value); + StringRef name; + Symbol.getName(name); + if (!name.empty()) { + SymbolName = name.data(); + return SymbolName; + } + } + + // TODO: For fully linked images, look through the external relocation + // entries off the dynamic symtab command. For these the r_offset is from the + // start of the first writeable segment in the Mach-O file. So the offset + // to this section from that segment is passed to this routine by the caller, + // as the database_offset. Which is the difference of the section's starting + // address and the first writable segment. + // + // NOTE: need add passing the database_offset to this routine. + + // TODO: We did not find an external relocation entry so look up the + // ReferenceValue as an address of a symbol and if found return that symbol's + // name. + // + // NOTE: need add passing the ReferenceValue to this routine. Then that code + // would simply be this: + // SymbolName = GuessSymbolName(ReferenceValue, info); + + return SymbolName; +} + +// These are structs in the Objective-C meta data and read to produce the +// comments for disassembly. While these are part of the ABI they are no +// public defintions. So the are here not in include/llvm/Support/MachO.h . + +// The cfstring object in a 64-bit Mach-O file. +struct cfstring64_t { + uint64_t isa; // class64_t * (64-bit pointer) + uint64_t flags; // flag bits + uint64_t characters; // char * (64-bit pointer) + uint64_t length; // number of non-NULL characters in above +}; + +// The class object in a 64-bit Mach-O file. +struct class64_t { + uint64_t isa; // class64_t * (64-bit pointer) + uint64_t superclass; // class64_t * (64-bit pointer) + uint64_t cache; // Cache (64-bit pointer) + uint64_t vtable; // IMP * (64-bit pointer) + uint64_t data; // class_ro64_t * (64-bit pointer) +}; + +struct class_ro64_t { + uint32_t flags; + uint32_t instanceStart; + uint32_t instanceSize; + uint32_t reserved; + uint64_t ivarLayout; // const uint8_t * (64-bit pointer) + uint64_t name; // const char * (64-bit pointer) + uint64_t baseMethods; // const method_list_t * (64-bit pointer) + uint64_t baseProtocols; // const protocol_list_t * (64-bit pointer) + uint64_t ivars; // const ivar_list_t * (64-bit pointer) + uint64_t weakIvarLayout; // const uint8_t * (64-bit pointer) + uint64_t baseProperties; // const struct objc_property_list (64-bit pointer) +}; + +inline void swapStruct(struct cfstring64_t &cfs) { + sys::swapByteOrder(cfs.isa); + sys::swapByteOrder(cfs.flags); + sys::swapByteOrder(cfs.characters); + sys::swapByteOrder(cfs.length); +} + +inline void swapStruct(struct class64_t &c) { + sys::swapByteOrder(c.isa); + sys::swapByteOrder(c.superclass); + sys::swapByteOrder(c.cache); + sys::swapByteOrder(c.vtable); + sys::swapByteOrder(c.data); +} + +inline void swapStruct(struct class_ro64_t &cro) { + sys::swapByteOrder(cro.flags); + sys::swapByteOrder(cro.instanceStart); + sys::swapByteOrder(cro.instanceSize); + sys::swapByteOrder(cro.reserved); + sys::swapByteOrder(cro.ivarLayout); + sys::swapByteOrder(cro.name); + sys::swapByteOrder(cro.baseMethods); + sys::swapByteOrder(cro.baseProtocols); + sys::swapByteOrder(cro.ivars); + sys::swapByteOrder(cro.weakIvarLayout); + sys::swapByteOrder(cro.baseProperties); +} + +static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, + struct DisassembleInfo *info); + +// get_objc2_64bit_class_name() is used for disassembly and is passed a pointer +// to an Objective-C class and returns the class name. It is also passed the +// address of the pointer, so when the pointer is zero as it can be in an .o +// file, that is used to look for an external relocation entry with a symbol +// name. +const char *get_objc2_64bit_class_name(uint64_t pointer_value, + uint64_t ReferenceValue, + struct DisassembleInfo *info) { + const char *r; + uint32_t offset, left; + SectionRef S; + + // The pointer_value can be 0 in an object file and have a relocation + // entry for the class symbol at the ReferenceValue (the address of the + // pointer). + if (pointer_value == 0) { + r = get_pointer_64(ReferenceValue, offset, left, S, info); + if (r == nullptr || left < sizeof(uint64_t)) + return nullptr; + uint64_t n_value; + const char *symbol_name = get_symbol_64(offset, S, info, n_value); + if (symbol_name == nullptr) + return nullptr; + const char *class_name = strrchr(symbol_name, '$'); + if (class_name != nullptr && class_name[1] == '_' && class_name[2] != '\0') + return class_name + 2; + else + return nullptr; + } + + // The case were the pointer_value is non-zero and points to a class defined + // in this Mach-O file. + r = get_pointer_64(pointer_value, offset, left, S, info); + if (r == nullptr || left < sizeof(struct class64_t)) + return nullptr; + struct class64_t c; + memcpy(&c, r, sizeof(struct class64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(c); + if (c.data == 0) + return nullptr; + r = get_pointer_64(c.data, offset, left, S, info); + if (r == nullptr || left < sizeof(struct class_ro64_t)) + return nullptr; + struct class_ro64_t cro; + memcpy(&cro, r, sizeof(struct class_ro64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(cro); + if (cro.name == 0) + return nullptr; + const char *name = get_pointer_64(cro.name, offset, left, S, info); + return name; +} + +// get_objc2_64bit_cfstring_name is used for disassembly and is passed a +// pointer to a cfstring and returns its name or nullptr. +const char *get_objc2_64bit_cfstring_name(uint64_t ReferenceValue, + struct DisassembleInfo *info) { + const char *r, *name; + uint32_t offset, left; + SectionRef S; + struct cfstring64_t cfs; + uint64_t cfs_characters; + + r = get_pointer_64(ReferenceValue, offset, left, S, info); + if (r == nullptr || left < sizeof(struct cfstring64_t)) + return nullptr; + memcpy(&cfs, r, sizeof(struct cfstring64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(cfs); + if (cfs.characters == 0) { + uint64_t n_value; + const char *symbol_name = get_symbol_64( + offset + offsetof(struct cfstring64_t, characters), S, info, n_value); + if (symbol_name == nullptr) + return nullptr; + cfs_characters = n_value; + } else + cfs_characters = cfs.characters; + name = get_pointer_64(cfs_characters, offset, left, S, info); + + return name; +} + +// get_objc2_64bit_selref() is used for disassembly and is passed a the address +// of a pointer to an Objective-C selector reference when the pointer value is +// zero as in a .o file and is likely to have a external relocation entry with +// who's symbol's n_value is the real pointer to the selector name. If that is +// the case the real pointer to the selector name is returned else 0 is +// returned +uint64_t get_objc2_64bit_selref(uint64_t ReferenceValue, + struct DisassembleInfo *info) { + uint32_t offset, left; + SectionRef S; + + const char *r = get_pointer_64(ReferenceValue, offset, left, S, info); + if (r == nullptr || left < sizeof(uint64_t)) + return 0; + uint64_t n_value; + const char *symbol_name = get_symbol_64(offset, S, info, n_value); + if (symbol_name == nullptr) + return 0; + return n_value; +} + +// GuessLiteralPointer returns a string which for the item in the Mach-O file +// for the address passed in as ReferenceValue for printing as a comment with +// the instruction and also returns the corresponding type of that item +// indirectly through ReferenceType. +// +// If ReferenceValue is an address of literal cstring then a pointer to the +// cstring is returned and ReferenceType is set to +// LLVMDisassembler_ReferenceType_Out_LitPool_CstrAddr . +// +// If ReferenceValue is an address of an Objective-C CFString, Selector ref or +// Class ref that name is returned and the ReferenceType is set accordingly. +// +// Lastly, literals which are Symbol address in a literal pool are looked for +// and if found the symbol name is returned and ReferenceType is set to +// LLVMDisassembler_ReferenceType_Out_LitPool_SymAddr . +// +// If there is no item in the Mach-O file for the address passed in as +// ReferenceValue nullptr is returned and ReferenceType is unchanged. +const char *GuessLiteralPointer(uint64_t ReferenceValue, uint64_t ReferencePC, + uint64_t *ReferenceType, + struct DisassembleInfo *info) { + // First see if there is an external relocation entry at the ReferencePC. + uint64_t sect_addr = info->S.getAddress(); + uint64_t sect_offset = ReferencePC - sect_addr; + bool reloc_found = false; + DataRefImpl Rel; + MachO::any_relocation_info RE; + bool isExtern = false; + SymbolRef Symbol; + for (const RelocationRef &Reloc : info->S.relocations()) { + uint64_t RelocOffset; + Reloc.getOffset(RelocOffset); + if (RelocOffset == sect_offset) { + Rel = Reloc.getRawDataRefImpl(); + RE = info->O->getRelocation(Rel); + if (info->O->isRelocationScattered(RE)) + continue; + isExtern = info->O->getPlainRelocationExternal(RE); + if (isExtern) { + symbol_iterator RelocSym = Reloc.getSymbol(); + Symbol = *RelocSym; + } + reloc_found = true; + break; + } + } + // If there is an external relocation entry for a symbol in a section + // then used that symbol's value for the value of the reference. + if (reloc_found && isExtern) { + if (info->O->getAnyRelocationPCRel(RE)) { + unsigned Type = info->O->getAnyRelocationType(RE); + if (Type == MachO::X86_64_RELOC_SIGNED) { + Symbol.getAddress(ReferenceValue); + } + } + } + + // Look for literals such as Objective-C CFStrings refs, Selector refs, + // Message refs and Class refs. + bool classref, selref, msgref, cfstring; + uint64_t pointer_value = GuessPointerPointer(ReferenceValue, info, classref, + selref, msgref, cfstring); + if (classref == true && pointer_value == 0) { + // Note the ReferenceValue is a pointer into the __objc_classrefs section. + // And the pointer_value in that section is typically zero as it will be + // set by dyld as part of the "bind information". + const char *name = get_dyld_bind_info_symbolname(ReferenceValue, info); + if (name != nullptr) { + *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Class_Ref; + const char *class_name = strrchr(name, '$'); + if (class_name != nullptr && class_name[1] == '_' && + class_name[2] != '\0') { + info->class_name = class_name + 2; + return name; + } + } + } + + if (classref == true) { + *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Class_Ref; + const char *name = + get_objc2_64bit_class_name(pointer_value, ReferenceValue, info); + if (name != nullptr) + info->class_name = name; + else + name = "bad class ref"; + return name; + } + + if (cfstring == true) { + *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_CFString_Ref; + const char *name = get_objc2_64bit_cfstring_name(ReferenceValue, info); + return name; + } + + if (selref == true && pointer_value == 0) + pointer_value = get_objc2_64bit_selref(ReferenceValue, info); + + if (pointer_value != 0) + ReferenceValue = pointer_value; + + const char *name = GuessCstringPointer(ReferenceValue, info); + if (name) { + if (pointer_value != 0 && selref == true) { + *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Selector_Ref; + info->selector_name = name; + } else if (pointer_value != 0 && msgref == true) { + info->class_name = nullptr; + *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message_Ref; + info->selector_name = name; + } else + *ReferenceType = LLVMDisassembler_ReferenceType_Out_LitPool_CstrAddr; + return name; + } + + // Lastly look for an indirect symbol with this ReferenceValue which is in + // a literal pool. If found return that symbol name. + name = GuessIndirectSymbol(ReferenceValue, info); + if (name) { + *ReferenceType = LLVMDisassembler_ReferenceType_Out_LitPool_SymAddr; + return name; + } + + return nullptr; +} + +// SymbolizerSymbolLookUp is the symbol lookup function passed when creating +// the Symbolizer. It looks up the ReferenceValue using the info passed via the +// pointer to the struct DisassembleInfo that was passed when MCSymbolizer +// is created and returns the symbol name that matches the ReferenceValue or +// nullptr if none. The ReferenceType is passed in for the IN type of +// reference the instruction is making from the values in defined in the header +// "llvm-c/Disassembler.h". On return the ReferenceType can set to a specific +// Out type and the ReferenceName will also be set which is added as a comment +// to the disassembled instruction. +// +#if HAVE_CXXABI_H +// If the symbol name is a C++ mangled name then the demangled name is +// returned through ReferenceName and ReferenceType is set to +// LLVMDisassembler_ReferenceType_DeMangled_Name . +#endif +// +// When this is called to get a symbol name for a branch target then the +// ReferenceType will be LLVMDisassembler_ReferenceType_In_Branch and then +// SymbolValue will be looked for in the indirect symbol table to determine if +// it is an address for a symbol stub. If so then the symbol name for that +// stub is returned indirectly through ReferenceName and then ReferenceType is +// set to LLVMDisassembler_ReferenceType_Out_SymbolStub. +// +// When this is called with an value loaded via a PC relative load then +// ReferenceType will be LLVMDisassembler_ReferenceType_In_PCrel_Load then the +// SymbolValue is checked to be an address of literal pointer, symbol pointer, +// or an Objective-C meta data reference. If so the output ReferenceType is +// set to correspond to that as well as setting the ReferenceName. +const char *SymbolizerSymbolLookUp(void *DisInfo, uint64_t ReferenceValue, + uint64_t *ReferenceType, + uint64_t ReferencePC, + const char **ReferenceName) { + struct DisassembleInfo *info = (struct DisassembleInfo *)DisInfo; + // If no verbose symbolic information is wanted then just return nullptr. + if (info->verbose == false) { + *ReferenceName = nullptr; + *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; + return nullptr; + } + + const char *SymbolName = GuessSymbolName(ReferenceValue, info); + + if (*ReferenceType == LLVMDisassembler_ReferenceType_In_Branch) { + *ReferenceName = GuessIndirectSymbol(ReferenceValue, info); + if (*ReferenceName != nullptr) { + method_reference(info, ReferenceType, ReferenceName); + if (*ReferenceType != LLVMDisassembler_ReferenceType_Out_Objc_Message) + *ReferenceType = LLVMDisassembler_ReferenceType_Out_SymbolStub; + } else +#if HAVE_CXXABI_H + if (SymbolName != nullptr && strncmp(SymbolName, "__Z", 3) == 0) { + if (info->demangled_name != nullptr) + free(info->demangled_name); + int status; + info->demangled_name = + abi::__cxa_demangle(SymbolName + 1, nullptr, nullptr, &status); + if (info->demangled_name != nullptr) { + *ReferenceName = info->demangled_name; + *ReferenceType = LLVMDisassembler_ReferenceType_DeMangled_Name; + } else + *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; + } else +#endif + *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; + } else if (*ReferenceType == LLVMDisassembler_ReferenceType_In_PCrel_Load) { + *ReferenceName = + GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info); + if (*ReferenceName) + method_reference(info, ReferenceType, ReferenceName); + else + *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; + // If this is arm64 and the reference is an adrp instruction save the + // instruction, passed in ReferenceValue and the address of the instruction + // for use later if we see and add immediate instruction. + } else if (info->O->getArch() == Triple::aarch64 && + *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_ADRP) { + info->adrp_inst = ReferenceValue; + info->adrp_addr = ReferencePC; + SymbolName = nullptr; + *ReferenceName = nullptr; + *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; + // If this is arm64 and reference is an add immediate instruction and we + // have + // seen an adrp instruction just before it and the adrp's Xd register + // matches + // this add's Xn register reconstruct the value being referenced and look to + // see if it is a literal pointer. Note the add immediate instruction is + // passed in ReferenceValue. + } else if (info->O->getArch() == Triple::aarch64 && + *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_ADDXri && + ReferencePC - 4 == info->adrp_addr && + (info->adrp_inst & 0x9f000000) == 0x90000000 && + (info->adrp_inst & 0x1f) == ((ReferenceValue >> 5) & 0x1f)) { + uint32_t addxri_inst; + uint64_t adrp_imm, addxri_imm; + + adrp_imm = + ((info->adrp_inst & 0x00ffffe0) >> 3) | ((info->adrp_inst >> 29) & 0x3); + if (info->adrp_inst & 0x0200000) + adrp_imm |= 0xfffffffffc000000LL; + + addxri_inst = ReferenceValue; + addxri_imm = (addxri_inst >> 10) & 0xfff; + if (((addxri_inst >> 22) & 0x3) == 1) + addxri_imm <<= 12; + + ReferenceValue = (info->adrp_addr & 0xfffffffffffff000LL) + + (adrp_imm << 12) + addxri_imm; + + *ReferenceName = + GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info); + if (*ReferenceName == nullptr) + *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; + // If this is arm64 and the reference is a load register instruction and we + // have seen an adrp instruction just before it and the adrp's Xd register + // matches this add's Xn register reconstruct the value being referenced and + // look to see if it is a literal pointer. Note the load register + // instruction is passed in ReferenceValue. + } else if (info->O->getArch() == Triple::aarch64 && + *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_LDRXui && + ReferencePC - 4 == info->adrp_addr && + (info->adrp_inst & 0x9f000000) == 0x90000000 && + (info->adrp_inst & 0x1f) == ((ReferenceValue >> 5) & 0x1f)) { + uint32_t ldrxui_inst; + uint64_t adrp_imm, ldrxui_imm; + + adrp_imm = + ((info->adrp_inst & 0x00ffffe0) >> 3) | ((info->adrp_inst >> 29) & 0x3); + if (info->adrp_inst & 0x0200000) + adrp_imm |= 0xfffffffffc000000LL; + + ldrxui_inst = ReferenceValue; + ldrxui_imm = (ldrxui_inst >> 10) & 0xfff; + + ReferenceValue = (info->adrp_addr & 0xfffffffffffff000LL) + + (adrp_imm << 12) + (ldrxui_imm << 3); + + *ReferenceName = + GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info); + if (*ReferenceName == nullptr) + *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; + } + // If this arm64 and is an load register (PC-relative) instruction the + // ReferenceValue is the PC plus the immediate value. + else if (info->O->getArch() == Triple::aarch64 && + (*ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_LDRXl || + *ReferenceType == LLVMDisassembler_ReferenceType_In_ARM64_ADR)) { + *ReferenceName = + GuessLiteralPointer(ReferenceValue, ReferencePC, ReferenceType, info); + if (*ReferenceName == nullptr) + *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; + } +#if HAVE_CXXABI_H + else if (SymbolName != nullptr && strncmp(SymbolName, "__Z", 3) == 0) { + if (info->demangled_name != nullptr) + free(info->demangled_name); + int status; + info->demangled_name = + abi::__cxa_demangle(SymbolName + 1, nullptr, nullptr, &status); + if (info->demangled_name != nullptr) { + *ReferenceName = info->demangled_name; + *ReferenceType = LLVMDisassembler_ReferenceType_DeMangled_Name; + } + } +#endif + else { + *ReferenceName = nullptr; + *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; + } + + return SymbolName; +} + +/// \brief Emits the comments that are stored in the CommentStream. +/// Each comment in the CommentStream must end with a newline. +static void emitComments(raw_svector_ostream &CommentStream, + SmallString<128> &CommentsToEmit, + formatted_raw_ostream &FormattedOS, + const MCAsmInfo &MAI) { + // Flush the stream before taking its content. + CommentStream.flush(); + StringRef Comments = CommentsToEmit.str(); + // Get the default information for printing a comment. + const char *CommentBegin = MAI.getCommentString(); + unsigned CommentColumn = MAI.getCommentColumn(); + bool IsFirst = true; + while (!Comments.empty()) { + if (!IsFirst) + FormattedOS << '\n'; + // Emit a line of comments. + FormattedOS.PadToColumn(CommentColumn); + size_t Position = Comments.find('\n'); + FormattedOS << CommentBegin << ' ' << Comments.substr(0, Position); + // Move after the newline character. + Comments = Comments.substr(Position + 1); + IsFirst = false; + } + FormattedOS.flush(); + + // Tell the comment stream that the vector changed underneath it. + CommentsToEmit.clear(); + CommentStream.resync(); +} + static void DisassembleInputMachO2(StringRef Filename, MachOObjectFile *MachOOF) { - const Target *TheTarget = GetTarget(MachOOF); + const char *McpuDefault = nullptr; + const Target *ThumbTarget = nullptr; + const Target *TheTarget = GetTarget(MachOOF, &McpuDefault, &ThumbTarget); if (!TheTarget) { // GetTarget prints out stuff. return; } + if (MCPU.empty() && McpuDefault) + MCPU = McpuDefault; + std::unique_ptr<const MCInstrInfo> InstrInfo(TheTarget->createMCInstrInfo()); - std::unique_ptr<MCInstrAnalysis> InstrAnalysis( - TheTarget->createMCInstrAnalysis(InstrInfo.get())); + std::unique_ptr<const MCInstrInfo> ThumbInstrInfo; + if (ThumbTarget) + ThumbInstrInfo.reset(ThumbTarget->createMCInstrInfo()); + + // Package up features to be passed to target/subtarget + std::string FeaturesStr; + if (MAttrs.size()) { + SubtargetFeatures Features; + for (unsigned i = 0; i != MAttrs.size(); ++i) + Features.AddFeature(MAttrs[i]); + FeaturesStr = Features.getString(); + } // Set up disassembler. std::unique_ptr<const MCRegisterInfo> MRI( @@ -225,26 +1617,80 @@ static void DisassembleInputMachO2(StringRef Filename, std::unique_ptr<const MCAsmInfo> AsmInfo( TheTarget->createMCAsmInfo(*MRI, TripleName)); std::unique_ptr<const MCSubtargetInfo> STI( - TheTarget->createMCSubtargetInfo(TripleName, "", "")); + TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); MCContext Ctx(AsmInfo.get(), MRI.get(), nullptr); - std::unique_ptr<const MCDisassembler> DisAsm( - TheTarget->createMCDisassembler(*STI, Ctx)); + std::unique_ptr<MCDisassembler> DisAsm( + TheTarget->createMCDisassembler(*STI, Ctx)); + std::unique_ptr<MCSymbolizer> Symbolizer; + struct DisassembleInfo SymbolizerInfo; + std::unique_ptr<MCRelocationInfo> RelInfo( + TheTarget->createMCRelocationInfo(TripleName, Ctx)); + if (RelInfo) { + Symbolizer.reset(TheTarget->createMCSymbolizer( + TripleName, SymbolizerGetOpInfo, SymbolizerSymbolLookUp, + &SymbolizerInfo, &Ctx, RelInfo.release())); + DisAsm->setSymbolizer(std::move(Symbolizer)); + } int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( AsmPrinterVariant, *AsmInfo, *InstrInfo, *MRI, *STI)); + // Set the display preference for hex vs. decimal immediates. + IP->setPrintImmHex(PrintImmHex); + // Comment stream and backing vector. + SmallString<128> CommentsToEmit; + raw_svector_ostream CommentStream(CommentsToEmit); - if (!InstrAnalysis || !AsmInfo || !STI || !DisAsm || !IP) { + if (!AsmInfo || !STI || !DisAsm || !IP) { errs() << "error: couldn't initialize disassembler for target " << TripleName << '\n'; return; } + // Set up thumb disassembler. + std::unique_ptr<const MCRegisterInfo> ThumbMRI; + std::unique_ptr<const MCAsmInfo> ThumbAsmInfo; + std::unique_ptr<const MCSubtargetInfo> ThumbSTI; + std::unique_ptr<MCDisassembler> ThumbDisAsm; + std::unique_ptr<MCInstPrinter> ThumbIP; + std::unique_ptr<MCContext> ThumbCtx; + std::unique_ptr<MCSymbolizer> ThumbSymbolizer; + struct DisassembleInfo ThumbSymbolizerInfo; + std::unique_ptr<MCRelocationInfo> ThumbRelInfo; + if (ThumbTarget) { + ThumbMRI.reset(ThumbTarget->createMCRegInfo(ThumbTripleName)); + ThumbAsmInfo.reset( + ThumbTarget->createMCAsmInfo(*ThumbMRI, ThumbTripleName)); + ThumbSTI.reset( + ThumbTarget->createMCSubtargetInfo(ThumbTripleName, MCPU, FeaturesStr)); + ThumbCtx.reset(new MCContext(ThumbAsmInfo.get(), ThumbMRI.get(), nullptr)); + ThumbDisAsm.reset(ThumbTarget->createMCDisassembler(*ThumbSTI, *ThumbCtx)); + MCContext *PtrThumbCtx = ThumbCtx.get(); + ThumbRelInfo.reset( + ThumbTarget->createMCRelocationInfo(ThumbTripleName, *PtrThumbCtx)); + if (ThumbRelInfo) { + ThumbSymbolizer.reset(ThumbTarget->createMCSymbolizer( + ThumbTripleName, SymbolizerGetOpInfo, SymbolizerSymbolLookUp, + &ThumbSymbolizerInfo, PtrThumbCtx, ThumbRelInfo.release())); + ThumbDisAsm->setSymbolizer(std::move(ThumbSymbolizer)); + } + int ThumbAsmPrinterVariant = ThumbAsmInfo->getAssemblerDialect(); + ThumbIP.reset(ThumbTarget->createMCInstPrinter( + ThumbAsmPrinterVariant, *ThumbAsmInfo, *ThumbInstrInfo, *ThumbMRI, + *ThumbSTI)); + // Set the display preference for hex vs. decimal immediates. + ThumbIP->setPrintImmHex(PrintImmHex); + } + + if (ThumbTarget && (!ThumbAsmInfo || !ThumbSTI || !ThumbDisAsm || !ThumbIP)) { + errs() << "error: couldn't initialize disassembler for target " + << ThumbTripleName << '\n'; + return; + } + outs() << '\n' << Filename << ":\n\n"; MachO::mach_header Header = MachOOF->getHeader(); - // FIXME: FoundFns isn't used anymore. Using symbols/LC_FUNCTION_STARTS to - // determine function locations will eventually go in MCObjectDisassembler. // FIXME: Using the -cfg command line option, this code used to be able to // annotate relocations with the referenced symbol's name, and if this was // inside a __[cf]string section, the data it points to. This is now replaced @@ -263,7 +1709,7 @@ static void DisassembleInputMachO2(StringRef Filename, // Build a data in code table that is sorted on by the address of each entry. uint64_t BaseAddress = 0; if (Header.filetype == MachO::MH_OBJECT) - Sections[0].getAddress(BaseAddress); + BaseAddress = Sections[0].getAddress(); else BaseAddress = BaseSegmentAddress; DiceTable Dices; @@ -288,29 +1734,30 @@ static void DisassembleInputMachO2(StringRef Filename, // A separate DSym file path was specified, parse it as a macho file, // get the sections and supply it to the section name parsing machinery. if (!DSYMFile.empty()) { - ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = MemoryBuffer::getFileOrSTDIN(DSYMFile); - if (std::error_code EC = Buf.getError()) { + if (std::error_code EC = BufOrErr.getError()) { errs() << "llvm-objdump: " << Filename << ": " << EC.message() << '\n'; return; } - DbgObj = ObjectFile::createMachOObjectFile(Buf.get()).get(); + DbgObj = + ObjectFile::createMachOObjectFile(BufOrErr.get()->getMemBufferRef()) + .get() + .release(); } // Setup the DIContext - diContext.reset(DIContext::getDWARFContext(DbgObj)); + diContext.reset(DIContext::getDWARFContext(*DbgObj)); } for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) { - bool SectIsText = false; - Sections[SectIdx].isText(SectIsText); + bool SectIsText = Sections[SectIdx].isText(); if (SectIsText == false) continue; StringRef SectName; - if (Sections[SectIdx].getName(SectName) || - SectName != "__text") + if (Sections[SectIdx].getName(SectName) || SectName != "__text") continue; // Skip non-text sections DataRefImpl DR = Sections[SectIdx].getRawDataRefImpl(); @@ -319,17 +1766,20 @@ static void DisassembleInputMachO2(StringRef Filename, if (SegmentName != "__TEXT") continue; - StringRef Bytes; - Sections[SectIdx].getContents(Bytes); - StringRefMemoryObject memoryObject(Bytes); + StringRef BytesStr; + Sections[SectIdx].getContents(BytesStr); + ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()), + BytesStr.size()); + uint64_t SectAddress = Sections[SectIdx].getAddress(); + bool symbolTableWorked = false; // Parse relocations. std::vector<std::pair<uint64_t, SymbolRef>> Relocs; for (const RelocationRef &Reloc : Sections[SectIdx].relocations()) { - uint64_t RelocOffset, SectionAddress; + uint64_t RelocOffset; Reloc.getOffset(RelocOffset); - Sections[SectIdx].getAddress(SectionAddress); + uint64_t SectionAddress = Sections[SectIdx].getAddress(); RelocOffset -= SectionAddress; symbol_iterator RelocSym = Reloc.getSymbol(); @@ -338,6 +1788,48 @@ static void DisassembleInputMachO2(StringRef Filename, } array_pod_sort(Relocs.begin(), Relocs.end()); + // Create a map of symbol addresses to symbol names for use by + // the SymbolizerSymbolLookUp() routine. + SymbolAddressMap AddrMap; + for (const SymbolRef &Symbol : MachOOF->symbols()) { + SymbolRef::Type ST; + Symbol.getType(ST); + if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data || + ST == SymbolRef::ST_Other) { + uint64_t Address; + Symbol.getAddress(Address); + StringRef SymName; + Symbol.getName(SymName); + AddrMap[Address] = SymName; + } + } + // Set up the block of info used by the Symbolizer call backs. + SymbolizerInfo.verbose = true; + SymbolizerInfo.O = MachOOF; + SymbolizerInfo.S = Sections[SectIdx]; + SymbolizerInfo.AddrMap = &AddrMap; + SymbolizerInfo.Sections = &Sections; + SymbolizerInfo.class_name = nullptr; + SymbolizerInfo.selector_name = nullptr; + SymbolizerInfo.method = nullptr; + SymbolizerInfo.demangled_name = nullptr; + SymbolizerInfo.bindtable = nullptr; + SymbolizerInfo.adrp_addr = 0; + SymbolizerInfo.adrp_inst = 0; + // Same for the ThumbSymbolizer + ThumbSymbolizerInfo.verbose = true; + ThumbSymbolizerInfo.O = MachOOF; + ThumbSymbolizerInfo.S = Sections[SectIdx]; + ThumbSymbolizerInfo.AddrMap = &AddrMap; + ThumbSymbolizerInfo.Sections = &Sections; + ThumbSymbolizerInfo.class_name = nullptr; + ThumbSymbolizerInfo.selector_name = nullptr; + ThumbSymbolizerInfo.method = nullptr; + ThumbSymbolizerInfo.demangled_name = nullptr; + ThumbSymbolizerInfo.bindtable = nullptr; + ThumbSymbolizerInfo.adrp_addr = 0; + ThumbSymbolizerInfo.adrp_inst = 0; + // Disassemble symbol by symbol. for (unsigned SymIdx = 0; SymIdx != Symbols.size(); SymIdx++) { StringRef SymName; @@ -349,15 +1841,13 @@ static void DisassembleInputMachO2(StringRef Filename, continue; // Make sure the symbol is defined in this section. - bool containsSym = false; - Sections[SectIdx].containsSymbol(Symbols[SymIdx], containsSym); + bool containsSym = Sections[SectIdx].containsSymbol(Symbols[SymIdx]); if (!containsSym) continue; // Start at the address of the symbol relative to the section's address. - uint64_t SectionAddress = 0; uint64_t Start = 0; - Sections[SectIdx].getAddress(SectionAddress); + uint64_t SectionAddress = Sections[SectIdx].getAddress(); Symbols[SymIdx].getAddress(Start); Start -= SectionAddress; @@ -365,13 +1855,13 @@ static void DisassembleInputMachO2(StringRef Filename, // the end of the section. bool containsNextSym = false; uint64_t NextSym = 0; - uint64_t NextSymIdx = SymIdx+1; + uint64_t NextSymIdx = SymIdx + 1; while (Symbols.size() > NextSymIdx) { SymbolRef::Type NextSymType; Symbols[NextSymIdx].getType(NextSymType); if (NextSymType == SymbolRef::ST_Function) { - Sections[SectIdx].containsSymbol(Symbols[NextSymIdx], - containsNextSym); + containsNextSym = + Sections[SectIdx].containsSymbol(Symbols[NextSymIdx]); Symbols[NextSymIdx].getAddress(NextSym); NextSym -= SectionAddress; break; @@ -379,48 +1869,81 @@ static void DisassembleInputMachO2(StringRef Filename, ++NextSymIdx; } - uint64_t SectSize; - Sections[SectIdx].getSize(SectSize); - uint64_t End = containsNextSym ? NextSym : SectSize; + uint64_t SectSize = Sections[SectIdx].getSize(); + uint64_t End = containsNextSym ? NextSym : SectSize; uint64_t Size; symbolTableWorked = true; + DataRefImpl Symb = Symbols[SymIdx].getRawDataRefImpl(); + bool isThumb = + (MachOOF->getSymbolFlags(Symb) & SymbolRef::SF_Thumb) && ThumbTarget; + outs() << SymName << ":\n"; DILineInfo lastLine; for (uint64_t Index = Start; Index < End; Index += Size) { MCInst Inst; - uint64_t SectAddress = 0; - Sections[SectIdx].getAddress(SectAddress); - outs() << format("%8" PRIx64 ":\t", SectAddress + Index); + uint64_t PC = SectAddress + Index; + if (FullLeadingAddr) { + if (MachOOF->is64Bit()) + outs() << format("%016" PRIx64, PC); + else + outs() << format("%08" PRIx64, PC); + } else { + outs() << format("%8" PRIx64 ":", PC); + } + if (!NoShowRawInsn) + 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(SectAddress + Index, DiceRef())); - dice_table_iterator DTI = std::search(Dices.begin(), Dices.end(), - Dice.begin(), Dice.end(), - compareDiceTableEntries); - if (DTI != Dices.end()){ + 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); - DumpBytes(StringRef(Bytes.data() + Index, Length)); uint16_t Kind; DTI->second.getKind(Kind); - DumpDataInCode(Bytes.data() + Index, Length, Kind); + Size = DumpDataInCode(reinterpret_cast<const char *>(Bytes.data()) + + Index, + Length, Kind); + if ((Kind == MachO::DICE_KIND_JUMP_TABLE8) && + (PC == (DTI->first + Length - 1)) && (Length & 1)) + Size++; continue; } - if (DisAsm->getInstruction(Inst, Size, memoryObject, Index, - DebugOut, nulls())) { - DumpBytes(StringRef(Bytes.data() + Index, Size)); - IP->printInst(&Inst, outs(), ""); + SmallVector<char, 64> AnnotationsBytes; + raw_svector_ostream Annotations(AnnotationsBytes); + + bool gotInst; + if (isThumb) + gotInst = ThumbDisAsm->getInstruction(Inst, Size, Bytes.slice(Index), + PC, DebugOut, Annotations); + else + gotInst = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), PC, + DebugOut, Annotations); + if (gotInst) { + if (!NoShowRawInsn) { + DumpBytes(StringRef( + reinterpret_cast<const char *>(Bytes.data()) + Index, Size)); + } + formatted_raw_ostream FormattedOS(outs()); + Annotations.flush(); + StringRef AnnotationsStr = Annotations.str(); + if (isThumb) + ThumbIP->printInst(&Inst, FormattedOS, AnnotationsStr); + else + IP->printInst(&Inst, FormattedOS, AnnotationsStr); + emitComments(CommentStream, CommentsToEmit, FormattedOS, *AsmInfo); // Print debug info. if (diContext) { - DILineInfo dli = - diContext->getLineInfoForAddress(SectAddress + Index); + DILineInfo dli = diContext->getLineInfoForAddress(PC); // Print valid line info if it changed. if (dli != lastLine && dli.Line != 0) outs() << "\t## " << dli.FileName << ':' << dli.Line << ':' @@ -429,34 +1952,1924 @@ static void DisassembleInputMachO2(StringRef Filename, } outs() << "\n"; } else { - errs() << "llvm-objdump: warning: invalid instruction encoding\n"; - if (Size == 0) - Size = 1; // skip illegible bytes + unsigned int Arch = MachOOF->getArch(); + if (Arch == Triple::x86_64 || Arch == Triple::x86) { + outs() << format("\t.byte 0x%02x #bad opcode\n", + *(Bytes.data() + Index) & 0xff); + Size = 1; // skip exactly one illegible byte and move on. + } else if (Arch == Triple::aarch64) { + uint32_t opcode = (*(Bytes.data() + Index) & 0xff) | + (*(Bytes.data() + Index + 1) & 0xff) << 8 | + (*(Bytes.data() + Index + 2) & 0xff) << 16 | + (*(Bytes.data() + Index + 3) & 0xff) << 24; + outs() << format("\t.long\t0x%08x\n", opcode); + Size = 4; + } else { + errs() << "llvm-objdump: warning: invalid instruction encoding\n"; + if (Size == 0) + Size = 1; // skip illegible bytes + } } } } if (!symbolTableWorked) { - // Reading the symbol table didn't work, disassemble the whole section. - uint64_t SectAddress; - Sections[SectIdx].getAddress(SectAddress); - uint64_t SectSize; - Sections[SectIdx].getSize(SectSize); + // Reading the symbol table didn't work, disassemble the whole section. + uint64_t SectAddress = Sections[SectIdx].getAddress(); + uint64_t SectSize = Sections[SectIdx].getSize(); uint64_t InstSize; for (uint64_t Index = 0; Index < SectSize; Index += InstSize) { MCInst Inst; - if (DisAsm->getInstruction(Inst, InstSize, memoryObject, Index, + uint64_t PC = SectAddress + Index; + if (DisAsm->getInstruction(Inst, InstSize, Bytes.slice(Index), PC, DebugOut, nulls())) { - outs() << format("%8" PRIx64 ":\t", SectAddress + Index); - DumpBytes(StringRef(Bytes.data() + Index, InstSize)); + if (FullLeadingAddr) { + if (MachOOF->is64Bit()) + outs() << format("%016" PRIx64, PC); + else + outs() << format("%08" PRIx64, PC); + } else { + outs() << format("%8" PRIx64 ":", PC); + } + if (!NoShowRawInsn) { + outs() << "\t"; + DumpBytes( + StringRef(reinterpret_cast<const char *>(Bytes.data()) + Index, + InstSize)); + } IP->printInst(&Inst, outs(), ""); outs() << "\n"; } else { - errs() << "llvm-objdump: warning: invalid instruction encoding\n"; - if (InstSize == 0) - InstSize = 1; // skip illegible bytes + unsigned int Arch = MachOOF->getArch(); + if (Arch == Triple::x86_64 || Arch == Triple::x86) { + outs() << format("\t.byte 0x%02x #bad opcode\n", + *(Bytes.data() + Index) & 0xff); + InstSize = 1; // skip exactly one illegible byte and move on. + } else { + errs() << "llvm-objdump: warning: invalid instruction encoding\n"; + if (InstSize == 0) + InstSize = 1; // skip illegible bytes + } } } } + if (SymbolizerInfo.method != nullptr) + free(SymbolizerInfo.method); + if (SymbolizerInfo.demangled_name != nullptr) + free(SymbolizerInfo.demangled_name); + if (SymbolizerInfo.bindtable != nullptr) + delete SymbolizerInfo.bindtable; + if (ThumbSymbolizerInfo.method != nullptr) + free(ThumbSymbolizerInfo.method); + if (ThumbSymbolizerInfo.demangled_name != nullptr) + free(ThumbSymbolizerInfo.demangled_name); + if (ThumbSymbolizerInfo.bindtable != nullptr) + delete ThumbSymbolizerInfo.bindtable; + } +} + +//===----------------------------------------------------------------------===// +// __compact_unwind section dumping +//===----------------------------------------------------------------------===// + +namespace { + +template <typename T> static uint64_t readNext(const char *&Buf) { + using llvm::support::little; + using llvm::support::unaligned; + + uint64_t Val = support::endian::read<T, little, unaligned>(Buf); + Buf += sizeof(T); + return Val; +} + +struct CompactUnwindEntry { + uint32_t OffsetInSection; + + uint64_t FunctionAddr; + uint32_t Length; + uint32_t CompactEncoding; + uint64_t PersonalityAddr; + uint64_t LSDAAddr; + + RelocationRef FunctionReloc; + RelocationRef PersonalityReloc; + RelocationRef LSDAReloc; + + CompactUnwindEntry(StringRef Contents, unsigned Offset, bool Is64) + : OffsetInSection(Offset) { + if (Is64) + read<uint64_t>(Contents.data() + Offset); + else + read<uint32_t>(Contents.data() + Offset); + } + +private: + template <typename UIntPtr> void read(const char *Buf) { + FunctionAddr = readNext<UIntPtr>(Buf); + Length = readNext<uint32_t>(Buf); + CompactEncoding = readNext<uint32_t>(Buf); + PersonalityAddr = readNext<UIntPtr>(Buf); + LSDAAddr = readNext<UIntPtr>(Buf); + } +}; +} + +/// Given a relocation from __compact_unwind, consisting of the RelocationRef +/// and data being relocated, determine the best base Name and Addend to use for +/// display purposes. +/// +/// 1. An Extern relocation will directly reference a symbol (and the data is +/// then already an addend), so use that. +/// 2. Otherwise the data is an offset in the object file's layout; try to find +// a symbol before it in the same section, and use the offset from there. +/// 3. Finally, if all that fails, fall back to an offset from the start of the +/// referenced section. +static void findUnwindRelocNameAddend(const MachOObjectFile *Obj, + std::map<uint64_t, SymbolRef> &Symbols, + const RelocationRef &Reloc, uint64_t Addr, + StringRef &Name, uint64_t &Addend) { + if (Reloc.getSymbol() != Obj->symbol_end()) { + Reloc.getSymbol()->getName(Name); + Addend = Addr; + return; + } + + auto RE = Obj->getRelocation(Reloc.getRawDataRefImpl()); + SectionRef RelocSection = Obj->getRelocationSection(RE); + + uint64_t SectionAddr = RelocSection.getAddress(); + + 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); + Addend = Addr - SectionAddr; + return; + } + + // Go back one so that SymbolAddress <= Addr. + --Sym; + + section_iterator SymSection = Obj->section_end(); + Sym->second.getSection(SymSection); + if (RelocSection == *SymSection) { + // There's a valid symbol in the same section before this reference. + Sym->second.getName(Name); + Addend = Addr - Sym->first; + return; + } + + // 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); + Addend = Addr - SectionAddr; +} + +static void printUnwindRelocDest(const MachOObjectFile *Obj, + std::map<uint64_t, SymbolRef> &Symbols, + const RelocationRef &Reloc, uint64_t Addr) { + StringRef Name; + uint64_t Addend; + + if (!Reloc.getObjectFile()) + return; + + findUnwindRelocNameAddend(Obj, Symbols, Reloc, Addr, Name, Addend); + + outs() << Name; + if (Addend) + outs() << " + " << format("0x%" PRIx64, Addend); +} + +static void +printMachOCompactUnwindSection(const MachOObjectFile *Obj, + std::map<uint64_t, SymbolRef> &Symbols, + const SectionRef &CompactUnwind) { + + assert(Obj->isLittleEndian() && + "There should not be a big-endian .o with __compact_unwind"); + + bool Is64 = Obj->is64Bit(); + uint32_t PointerSize = Is64 ? sizeof(uint64_t) : sizeof(uint32_t); + uint32_t EntrySize = 3 * PointerSize + 2 * sizeof(uint32_t); + + StringRef Contents; + CompactUnwind.getContents(Contents); + + SmallVector<CompactUnwindEntry, 4> CompactUnwinds; + + // First populate the initial raw offsets, encodings and so on from the entry. + for (unsigned Offset = 0; Offset < Contents.size(); Offset += EntrySize) { + CompactUnwindEntry Entry(Contents.data(), Offset, Is64); + CompactUnwinds.push_back(Entry); + } + + // Next we need to look at the relocations to find out what objects are + // actually being referred to. + for (const RelocationRef &Reloc : CompactUnwind.relocations()) { + uint64_t RelocAddress; + Reloc.getOffset(RelocAddress); + + uint32_t EntryIdx = RelocAddress / EntrySize; + uint32_t OffsetInEntry = RelocAddress - EntryIdx * EntrySize; + CompactUnwindEntry &Entry = CompactUnwinds[EntryIdx]; + + if (OffsetInEntry == 0) + Entry.FunctionReloc = Reloc; + else if (OffsetInEntry == PointerSize + 2 * sizeof(uint32_t)) + Entry.PersonalityReloc = Reloc; + else if (OffsetInEntry == 2 * PointerSize + 2 * sizeof(uint32_t)) + Entry.LSDAReloc = Reloc; + else + llvm_unreachable("Unexpected relocation in __compact_unwind section"); + } + + // Finally, we're ready to print the data we've gathered. + outs() << "Contents of __compact_unwind section:\n"; + for (auto &Entry : CompactUnwinds) { + outs() << " Entry at offset " + << format("0x%" PRIx32, Entry.OffsetInSection) << ":\n"; + + // 1. Start of the region this entry applies to. + outs() << " start: " << format("0x%" PRIx64, + Entry.FunctionAddr) << ' '; + printUnwindRelocDest(Obj, Symbols, Entry.FunctionReloc, Entry.FunctionAddr); + outs() << '\n'; + + // 2. Length of the region this entry applies to. + outs() << " length: " << format("0x%" PRIx32, Entry.Length) + << '\n'; + // 3. The 32-bit compact encoding. + outs() << " compact encoding: " + << format("0x%08" PRIx32, Entry.CompactEncoding) << '\n'; + + // 4. The personality function, if present. + if (Entry.PersonalityReloc.getObjectFile()) { + outs() << " personality function: " + << format("0x%" PRIx64, Entry.PersonalityAddr) << ' '; + printUnwindRelocDest(Obj, Symbols, Entry.PersonalityReloc, + Entry.PersonalityAddr); + outs() << '\n'; + } + + // 5. This entry's language-specific data area. + if (Entry.LSDAReloc.getObjectFile()) { + outs() << " LSDA: " << format("0x%" PRIx64, + Entry.LSDAAddr) << ' '; + printUnwindRelocDest(Obj, Symbols, Entry.LSDAReloc, Entry.LSDAAddr); + outs() << '\n'; + } + } +} + +//===----------------------------------------------------------------------===// +// __unwind_info section dumping +//===----------------------------------------------------------------------===// + +static void printRegularSecondLevelUnwindPage(const char *PageStart) { + const char *Pos = PageStart; + uint32_t Kind = readNext<uint32_t>(Pos); + (void)Kind; + assert(Kind == 2 && "kind for a regular 2nd level index should be 2"); + + uint16_t EntriesStart = readNext<uint16_t>(Pos); + uint16_t NumEntries = readNext<uint16_t>(Pos); + + Pos = PageStart + EntriesStart; + for (unsigned i = 0; i < NumEntries; ++i) { + uint32_t FunctionOffset = readNext<uint32_t>(Pos); + uint32_t Encoding = readNext<uint32_t>(Pos); + + outs() << " [" << i << "]: " + << "function offset=" << format("0x%08" PRIx32, FunctionOffset) + << ", " + << "encoding=" << format("0x%08" PRIx32, Encoding) << '\n'; + } +} + +static void printCompressedSecondLevelUnwindPage( + const char *PageStart, uint32_t FunctionBase, + const SmallVectorImpl<uint32_t> &CommonEncodings) { + const char *Pos = PageStart; + uint32_t Kind = readNext<uint32_t>(Pos); + (void)Kind; + assert(Kind == 3 && "kind for a compressed 2nd level index should be 3"); + + uint16_t EntriesStart = readNext<uint16_t>(Pos); + uint16_t NumEntries = readNext<uint16_t>(Pos); + + uint16_t EncodingsStart = readNext<uint16_t>(Pos); + readNext<uint16_t>(Pos); + const auto *PageEncodings = reinterpret_cast<const support::ulittle32_t *>( + PageStart + EncodingsStart); + + Pos = PageStart + EntriesStart; + for (unsigned i = 0; i < NumEntries; ++i) { + uint32_t Entry = readNext<uint32_t>(Pos); + uint32_t FunctionOffset = FunctionBase + (Entry & 0xffffff); + uint32_t EncodingIdx = Entry >> 24; + + uint32_t Encoding; + if (EncodingIdx < CommonEncodings.size()) + Encoding = CommonEncodings[EncodingIdx]; + else + Encoding = PageEncodings[EncodingIdx - CommonEncodings.size()]; + + outs() << " [" << i << "]: " + << "function offset=" << format("0x%08" PRIx32, FunctionOffset) + << ", " + << "encoding[" << EncodingIdx + << "]=" << format("0x%08" PRIx32, Encoding) << '\n'; + } +} + +static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, + std::map<uint64_t, SymbolRef> &Symbols, + const SectionRef &UnwindInfo) { + + assert(Obj->isLittleEndian() && + "There should not be a big-endian .o with __unwind_info"); + + outs() << "Contents of __unwind_info section:\n"; + + StringRef Contents; + UnwindInfo.getContents(Contents); + const char *Pos = Contents.data(); + + //===---------------------------------- + // Section header + //===---------------------------------- + + uint32_t Version = readNext<uint32_t>(Pos); + outs() << " Version: " + << format("0x%" PRIx32, Version) << '\n'; + assert(Version == 1 && "only understand version 1"); + + uint32_t CommonEncodingsStart = readNext<uint32_t>(Pos); + outs() << " Common encodings array section offset: " + << format("0x%" PRIx32, CommonEncodingsStart) << '\n'; + uint32_t NumCommonEncodings = readNext<uint32_t>(Pos); + outs() << " Number of common encodings in array: " + << format("0x%" PRIx32, NumCommonEncodings) << '\n'; + + uint32_t PersonalitiesStart = readNext<uint32_t>(Pos); + outs() << " Personality function array section offset: " + << format("0x%" PRIx32, PersonalitiesStart) << '\n'; + uint32_t NumPersonalities = readNext<uint32_t>(Pos); + outs() << " Number of personality functions in array: " + << format("0x%" PRIx32, NumPersonalities) << '\n'; + + uint32_t IndicesStart = readNext<uint32_t>(Pos); + outs() << " Index array section offset: " + << format("0x%" PRIx32, IndicesStart) << '\n'; + uint32_t NumIndices = readNext<uint32_t>(Pos); + outs() << " Number of indices in array: " + << format("0x%" PRIx32, NumIndices) << '\n'; + + //===---------------------------------- + // A shared list of common encodings + //===---------------------------------- + + // These occupy indices in the range [0, N] whenever an encoding is referenced + // from a compressed 2nd level index table. In practice the linker only + // creates ~128 of these, so that indices are available to embed encodings in + // the 2nd level index. + + SmallVector<uint32_t, 64> CommonEncodings; + outs() << " Common encodings: (count = " << NumCommonEncodings << ")\n"; + Pos = Contents.data() + CommonEncodingsStart; + for (unsigned i = 0; i < NumCommonEncodings; ++i) { + uint32_t Encoding = readNext<uint32_t>(Pos); + CommonEncodings.push_back(Encoding); + + outs() << " encoding[" << i << "]: " << format("0x%08" PRIx32, Encoding) + << '\n'; + } + + //===---------------------------------- + // Personality functions used in this executable + //===---------------------------------- + + // There should be only a handful of these (one per source language, + // roughly). Particularly since they only get 2 bits in the compact encoding. + + outs() << " Personality functions: (count = " << NumPersonalities << ")\n"; + Pos = Contents.data() + PersonalitiesStart; + for (unsigned i = 0; i < NumPersonalities; ++i) { + uint32_t PersonalityFn = readNext<uint32_t>(Pos); + outs() << " personality[" << i + 1 + << "]: " << format("0x%08" PRIx32, PersonalityFn) << '\n'; + } + + //===---------------------------------- + // The level 1 index entries + //===---------------------------------- + + // These specify an approximate place to start searching for the more detailed + // information, sorted by PC. + + struct IndexEntry { + uint32_t FunctionOffset; + uint32_t SecondLevelPageStart; + uint32_t LSDAStart; + }; + + SmallVector<IndexEntry, 4> IndexEntries; + + outs() << " Top level indices: (count = " << NumIndices << ")\n"; + Pos = Contents.data() + IndicesStart; + for (unsigned i = 0; i < NumIndices; ++i) { + IndexEntry Entry; + + Entry.FunctionOffset = readNext<uint32_t>(Pos); + Entry.SecondLevelPageStart = readNext<uint32_t>(Pos); + Entry.LSDAStart = readNext<uint32_t>(Pos); + IndexEntries.push_back(Entry); + + outs() << " [" << i << "]: " + << "function offset=" << format("0x%08" PRIx32, Entry.FunctionOffset) + << ", " + << "2nd level page offset=" + << format("0x%08" PRIx32, Entry.SecondLevelPageStart) << ", " + << "LSDA offset=" << format("0x%08" PRIx32, Entry.LSDAStart) << '\n'; + } + + //===---------------------------------- + // Next come the LSDA tables + //===---------------------------------- + + // The LSDA layout is rather implicit: it's a contiguous array of entries from + // the first top-level index's LSDAOffset to the last (sentinel). + + outs() << " LSDA descriptors:\n"; + Pos = Contents.data() + IndexEntries[0].LSDAStart; + int NumLSDAs = (IndexEntries.back().LSDAStart - IndexEntries[0].LSDAStart) / + (2 * sizeof(uint32_t)); + for (int i = 0; i < NumLSDAs; ++i) { + uint32_t FunctionOffset = readNext<uint32_t>(Pos); + uint32_t LSDAOffset = readNext<uint32_t>(Pos); + outs() << " [" << i << "]: " + << "function offset=" << format("0x%08" PRIx32, FunctionOffset) + << ", " + << "LSDA offset=" << format("0x%08" PRIx32, LSDAOffset) << '\n'; + } + + //===---------------------------------- + // Finally, the 2nd level indices + //===---------------------------------- + + // Generally these are 4K in size, and have 2 possible forms: + // + Regular stores up to 511 entries with disparate encodings + // + Compressed stores up to 1021 entries if few enough compact encoding + // values are used. + outs() << " Second level indices:\n"; + for (unsigned i = 0; i < IndexEntries.size() - 1; ++i) { + // The final sentinel top-level index has no associated 2nd level page + if (IndexEntries[i].SecondLevelPageStart == 0) + break; + + outs() << " Second level index[" << i << "]: " + << "offset in section=" + << format("0x%08" PRIx32, IndexEntries[i].SecondLevelPageStart) + << ", " + << "base function offset=" + << format("0x%08" PRIx32, IndexEntries[i].FunctionOffset) << '\n'; + + Pos = Contents.data() + IndexEntries[i].SecondLevelPageStart; + uint32_t Kind = *reinterpret_cast<const support::ulittle32_t *>(Pos); + if (Kind == 2) + printRegularSecondLevelUnwindPage(Pos); + else if (Kind == 3) + printCompressedSecondLevelUnwindPage(Pos, IndexEntries[i].FunctionOffset, + CommonEncodings); + else + llvm_unreachable("Do not know how to print this kind of 2nd level page"); + } +} + +void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) { + std::map<uint64_t, SymbolRef> Symbols; + for (const SymbolRef &SymRef : Obj->symbols()) { + // Discard any undefined or absolute symbols. They're not going to take part + // in the convenience lookup for unwind info and just take up resources. + section_iterator Section = Obj->section_end(); + SymRef.getSection(Section); + if (Section == Obj->section_end()) + continue; + + uint64_t Addr; + SymRef.getAddress(Addr); + Symbols.insert(std::make_pair(Addr, SymRef)); + } + + for (const SectionRef &Section : Obj->sections()) { + StringRef SectName; + Section.getName(SectName); + if (SectName == "__compact_unwind") + printMachOCompactUnwindSection(Obj, Symbols, Section); + else if (SectName == "__unwind_info") + printMachOUnwindInfoSection(Obj, Symbols, Section); + else if (SectName == "__eh_frame") + outs() << "llvm-objdump: warning: unhandled __eh_frame section\n"; + } +} + +static void PrintMachHeader(uint32_t magic, uint32_t cputype, + uint32_t cpusubtype, uint32_t filetype, + uint32_t ncmds, uint32_t sizeofcmds, uint32_t flags, + bool verbose) { + outs() << "Mach header\n"; + outs() << " magic cputype cpusubtype caps filetype ncmds " + "sizeofcmds flags\n"; + if (verbose) { + if (magic == MachO::MH_MAGIC) + outs() << " MH_MAGIC"; + else if (magic == MachO::MH_MAGIC_64) + outs() << "MH_MAGIC_64"; + else + outs() << format(" 0x%08" PRIx32, magic); + switch (cputype) { + case MachO::CPU_TYPE_I386: + outs() << " I386"; + switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_I386_ALL: + outs() << " ALL"; + break; + default: + outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); + break; + } + break; + case MachO::CPU_TYPE_X86_64: + outs() << " X86_64"; + case MachO::CPU_SUBTYPE_X86_64_ALL: + outs() << " ALL"; + break; + case MachO::CPU_SUBTYPE_X86_64_H: + outs() << " Haswell"; + outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); + break; + case MachO::CPU_TYPE_ARM: + outs() << " ARM"; + switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_ARM_ALL: + outs() << " ALL"; + break; + case MachO::CPU_SUBTYPE_ARM_V4T: + outs() << " V4T"; + break; + case MachO::CPU_SUBTYPE_ARM_V5TEJ: + outs() << " V5TEJ"; + break; + case MachO::CPU_SUBTYPE_ARM_XSCALE: + outs() << " XSCALE"; + break; + case MachO::CPU_SUBTYPE_ARM_V6: + outs() << " V6"; + break; + case MachO::CPU_SUBTYPE_ARM_V6M: + outs() << " V6M"; + break; + case MachO::CPU_SUBTYPE_ARM_V7: + outs() << " V7"; + break; + case MachO::CPU_SUBTYPE_ARM_V7EM: + outs() << " V7EM"; + break; + case MachO::CPU_SUBTYPE_ARM_V7K: + outs() << " V7K"; + break; + case MachO::CPU_SUBTYPE_ARM_V7M: + outs() << " V7M"; + break; + case MachO::CPU_SUBTYPE_ARM_V7S: + outs() << " V7S"; + break; + default: + outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); + break; + } + break; + case MachO::CPU_TYPE_ARM64: + outs() << " ARM64"; + switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_ARM64_ALL: + outs() << " ALL"; + break; + default: + outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); + break; + } + break; + case MachO::CPU_TYPE_POWERPC: + outs() << " PPC"; + switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_POWERPC_ALL: + outs() << " ALL"; + break; + default: + outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); + break; + } + break; + case MachO::CPU_TYPE_POWERPC64: + outs() << " PPC64"; + switch (cpusubtype & ~MachO::CPU_SUBTYPE_MASK) { + case MachO::CPU_SUBTYPE_POWERPC_ALL: + outs() << " ALL"; + break; + default: + outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); + break; + } + break; + } + if ((cpusubtype & MachO::CPU_SUBTYPE_MASK) == MachO::CPU_SUBTYPE_LIB64) { + outs() << " LIB64"; + } else { + outs() << format(" 0x%02" PRIx32, + (cpusubtype & MachO::CPU_SUBTYPE_MASK) >> 24); + } + switch (filetype) { + case MachO::MH_OBJECT: + outs() << " OBJECT"; + break; + case MachO::MH_EXECUTE: + outs() << " EXECUTE"; + break; + case MachO::MH_FVMLIB: + outs() << " FVMLIB"; + break; + case MachO::MH_CORE: + outs() << " CORE"; + break; + case MachO::MH_PRELOAD: + outs() << " PRELOAD"; + break; + case MachO::MH_DYLIB: + outs() << " DYLIB"; + break; + case MachO::MH_DYLIB_STUB: + outs() << " DYLIB_STUB"; + break; + case MachO::MH_DYLINKER: + outs() << " DYLINKER"; + break; + case MachO::MH_BUNDLE: + outs() << " BUNDLE"; + break; + case MachO::MH_DSYM: + outs() << " DSYM"; + break; + case MachO::MH_KEXT_BUNDLE: + outs() << " KEXTBUNDLE"; + break; + default: + outs() << format(" %10u", filetype); + break; + } + outs() << format(" %5u", ncmds); + outs() << format(" %10u", sizeofcmds); + uint32_t f = flags; + if (f & MachO::MH_NOUNDEFS) { + outs() << " NOUNDEFS"; + f &= ~MachO::MH_NOUNDEFS; + } + if (f & MachO::MH_INCRLINK) { + outs() << " INCRLINK"; + f &= ~MachO::MH_INCRLINK; + } + if (f & MachO::MH_DYLDLINK) { + outs() << " DYLDLINK"; + f &= ~MachO::MH_DYLDLINK; + } + if (f & MachO::MH_BINDATLOAD) { + outs() << " BINDATLOAD"; + f &= ~MachO::MH_BINDATLOAD; + } + if (f & MachO::MH_PREBOUND) { + outs() << " PREBOUND"; + f &= ~MachO::MH_PREBOUND; + } + if (f & MachO::MH_SPLIT_SEGS) { + outs() << " SPLIT_SEGS"; + f &= ~MachO::MH_SPLIT_SEGS; + } + if (f & MachO::MH_LAZY_INIT) { + outs() << " LAZY_INIT"; + f &= ~MachO::MH_LAZY_INIT; + } + if (f & MachO::MH_TWOLEVEL) { + outs() << " TWOLEVEL"; + f &= ~MachO::MH_TWOLEVEL; + } + if (f & MachO::MH_FORCE_FLAT) { + outs() << " FORCE_FLAT"; + f &= ~MachO::MH_FORCE_FLAT; + } + if (f & MachO::MH_NOMULTIDEFS) { + outs() << " NOMULTIDEFS"; + f &= ~MachO::MH_NOMULTIDEFS; + } + if (f & MachO::MH_NOFIXPREBINDING) { + outs() << " NOFIXPREBINDING"; + f &= ~MachO::MH_NOFIXPREBINDING; + } + if (f & MachO::MH_PREBINDABLE) { + outs() << " PREBINDABLE"; + f &= ~MachO::MH_PREBINDABLE; + } + if (f & MachO::MH_ALLMODSBOUND) { + outs() << " ALLMODSBOUND"; + f &= ~MachO::MH_ALLMODSBOUND; + } + if (f & MachO::MH_SUBSECTIONS_VIA_SYMBOLS) { + outs() << " SUBSECTIONS_VIA_SYMBOLS"; + f &= ~MachO::MH_SUBSECTIONS_VIA_SYMBOLS; + } + if (f & MachO::MH_CANONICAL) { + outs() << " CANONICAL"; + f &= ~MachO::MH_CANONICAL; + } + if (f & MachO::MH_WEAK_DEFINES) { + outs() << " WEAK_DEFINES"; + f &= ~MachO::MH_WEAK_DEFINES; + } + if (f & MachO::MH_BINDS_TO_WEAK) { + outs() << " BINDS_TO_WEAK"; + f &= ~MachO::MH_BINDS_TO_WEAK; + } + if (f & MachO::MH_ALLOW_STACK_EXECUTION) { + outs() << " ALLOW_STACK_EXECUTION"; + f &= ~MachO::MH_ALLOW_STACK_EXECUTION; + } + if (f & MachO::MH_DEAD_STRIPPABLE_DYLIB) { + outs() << " DEAD_STRIPPABLE_DYLIB"; + f &= ~MachO::MH_DEAD_STRIPPABLE_DYLIB; + } + if (f & MachO::MH_PIE) { + outs() << " PIE"; + f &= ~MachO::MH_PIE; + } + if (f & MachO::MH_NO_REEXPORTED_DYLIBS) { + outs() << " NO_REEXPORTED_DYLIBS"; + f &= ~MachO::MH_NO_REEXPORTED_DYLIBS; + } + if (f & MachO::MH_HAS_TLV_DESCRIPTORS) { + outs() << " MH_HAS_TLV_DESCRIPTORS"; + f &= ~MachO::MH_HAS_TLV_DESCRIPTORS; + } + if (f & MachO::MH_NO_HEAP_EXECUTION) { + outs() << " MH_NO_HEAP_EXECUTION"; + f &= ~MachO::MH_NO_HEAP_EXECUTION; + } + if (f & MachO::MH_APP_EXTENSION_SAFE) { + outs() << " APP_EXTENSION_SAFE"; + f &= ~MachO::MH_APP_EXTENSION_SAFE; + } + if (f != 0 || flags == 0) + outs() << format(" 0x%08" PRIx32, f); + } else { + outs() << format(" 0x%08" PRIx32, magic); + outs() << format(" %7d", cputype); + outs() << format(" %10d", cpusubtype & ~MachO::CPU_SUBTYPE_MASK); + outs() << format(" 0x%02" PRIx32, + (cpusubtype & MachO::CPU_SUBTYPE_MASK) >> 24); + outs() << format(" %10u", filetype); + outs() << format(" %5u", ncmds); + outs() << format(" %10u", sizeofcmds); + outs() << format(" 0x%08" PRIx32, flags); + } + outs() << "\n"; +} + +static void PrintSegmentCommand(uint32_t cmd, uint32_t cmdsize, + StringRef SegName, uint64_t vmaddr, + uint64_t vmsize, uint64_t fileoff, + uint64_t filesize, uint32_t maxprot, + uint32_t initprot, uint32_t nsects, + uint32_t flags, uint32_t object_size, + bool verbose) { + uint64_t expected_cmdsize; + if (cmd == MachO::LC_SEGMENT) { + outs() << " cmd LC_SEGMENT\n"; + expected_cmdsize = nsects; + expected_cmdsize *= sizeof(struct MachO::section); + expected_cmdsize += sizeof(struct MachO::segment_command); + } else { + outs() << " cmd LC_SEGMENT_64\n"; + expected_cmdsize = nsects; + expected_cmdsize *= sizeof(struct MachO::section_64); + expected_cmdsize += sizeof(struct MachO::segment_command_64); + } + outs() << " cmdsize " << cmdsize; + if (cmdsize != expected_cmdsize) + outs() << " Inconsistent size\n"; + else + outs() << "\n"; + outs() << " segname " << SegName << "\n"; + if (cmd == MachO::LC_SEGMENT_64) { + outs() << " vmaddr " << format("0x%016" PRIx64, vmaddr) << "\n"; + outs() << " vmsize " << format("0x%016" PRIx64, vmsize) << "\n"; + } else { + outs() << " vmaddr " << format("0x%08" PRIx32, vmaddr) << "\n"; + outs() << " vmsize " << format("0x%08" PRIx32, vmsize) << "\n"; + } + outs() << " fileoff " << fileoff; + if (fileoff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " filesize " << filesize; + if (fileoff + filesize > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + if (verbose) { + if ((maxprot & + ~(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | + MachO::VM_PROT_EXECUTE)) != 0) + outs() << " maxprot ?" << format("0x%08" PRIx32, maxprot) << "\n"; + else { + if (maxprot & MachO::VM_PROT_READ) + outs() << " maxprot r"; + else + outs() << " maxprot -"; + if (maxprot & MachO::VM_PROT_WRITE) + outs() << "w"; + else + outs() << "-"; + if (maxprot & MachO::VM_PROT_EXECUTE) + outs() << "x\n"; + else + outs() << "-\n"; + } + if ((initprot & + ~(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | + MachO::VM_PROT_EXECUTE)) != 0) + outs() << " initprot ?" << format("0x%08" PRIx32, initprot) << "\n"; + else { + if (initprot & MachO::VM_PROT_READ) + outs() << " initprot r"; + else + outs() << " initprot -"; + if (initprot & MachO::VM_PROT_WRITE) + outs() << "w"; + else + outs() << "-"; + if (initprot & MachO::VM_PROT_EXECUTE) + outs() << "x\n"; + else + outs() << "-\n"; + } + } else { + outs() << " maxprot " << format("0x%08" PRIx32, maxprot) << "\n"; + outs() << " initprot " << format("0x%08" PRIx32, initprot) << "\n"; + } + outs() << " nsects " << nsects << "\n"; + if (verbose) { + outs() << " flags"; + if (flags == 0) + outs() << " (none)\n"; + else { + if (flags & MachO::SG_HIGHVM) { + outs() << " HIGHVM"; + flags &= ~MachO::SG_HIGHVM; + } + if (flags & MachO::SG_FVMLIB) { + outs() << " FVMLIB"; + flags &= ~MachO::SG_FVMLIB; + } + if (flags & MachO::SG_NORELOC) { + outs() << " NORELOC"; + flags &= ~MachO::SG_NORELOC; + } + if (flags & MachO::SG_PROTECTED_VERSION_1) { + outs() << " PROTECTED_VERSION_1"; + flags &= ~MachO::SG_PROTECTED_VERSION_1; + } + if (flags) + outs() << format(" 0x%08" PRIx32, flags) << " (unknown flags)\n"; + else + outs() << "\n"; + } + } else { + outs() << " flags " << format("0x%" PRIx32, flags) << "\n"; + } +} + +static void PrintSection(const char *sectname, const char *segname, + uint64_t addr, uint64_t size, uint32_t offset, + uint32_t align, uint32_t reloff, uint32_t nreloc, + uint32_t flags, uint32_t reserved1, uint32_t reserved2, + uint32_t cmd, const char *sg_segname, + uint32_t filetype, uint32_t object_size, + bool verbose) { + outs() << "Section\n"; + outs() << " sectname " << format("%.16s\n", sectname); + outs() << " segname " << format("%.16s", segname); + if (filetype != MachO::MH_OBJECT && strncmp(sg_segname, segname, 16) != 0) + outs() << " (does not match segment)\n"; + else + outs() << "\n"; + if (cmd == MachO::LC_SEGMENT_64) { + outs() << " addr " << format("0x%016" PRIx64, addr) << "\n"; + outs() << " size " << format("0x%016" PRIx64, size); + } else { + outs() << " addr " << format("0x%08" PRIx32, addr) << "\n"; + outs() << " size " << format("0x%08" PRIx32, size); + } + if ((flags & MachO::S_ZEROFILL) != 0 && offset + size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " offset " << offset; + if (offset > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + uint32_t align_shifted = 1 << align; + outs() << " align 2^" << align << " (" << align_shifted << ")\n"; + outs() << " reloff " << reloff; + if (reloff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " nreloc " << nreloc; + if (reloff + nreloc * sizeof(struct MachO::relocation_info) > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + uint32_t section_type = flags & MachO::SECTION_TYPE; + if (verbose) { + outs() << " type"; + if (section_type == MachO::S_REGULAR) + outs() << " S_REGULAR\n"; + else if (section_type == MachO::S_ZEROFILL) + outs() << " S_ZEROFILL\n"; + else if (section_type == MachO::S_CSTRING_LITERALS) + outs() << " S_CSTRING_LITERALS\n"; + else if (section_type == MachO::S_4BYTE_LITERALS) + outs() << " S_4BYTE_LITERALS\n"; + else if (section_type == MachO::S_8BYTE_LITERALS) + outs() << " S_8BYTE_LITERALS\n"; + else if (section_type == MachO::S_16BYTE_LITERALS) + outs() << " S_16BYTE_LITERALS\n"; + else if (section_type == MachO::S_LITERAL_POINTERS) + outs() << " S_LITERAL_POINTERS\n"; + else if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS) + outs() << " S_NON_LAZY_SYMBOL_POINTERS\n"; + else if (section_type == MachO::S_LAZY_SYMBOL_POINTERS) + outs() << " S_LAZY_SYMBOL_POINTERS\n"; + else if (section_type == MachO::S_SYMBOL_STUBS) + outs() << " S_SYMBOL_STUBS\n"; + else if (section_type == MachO::S_MOD_INIT_FUNC_POINTERS) + outs() << " S_MOD_INIT_FUNC_POINTERS\n"; + else if (section_type == MachO::S_MOD_TERM_FUNC_POINTERS) + outs() << " S_MOD_TERM_FUNC_POINTERS\n"; + else if (section_type == MachO::S_COALESCED) + outs() << " S_COALESCED\n"; + else if (section_type == MachO::S_INTERPOSING) + outs() << " S_INTERPOSING\n"; + else if (section_type == MachO::S_DTRACE_DOF) + outs() << " S_DTRACE_DOF\n"; + else if (section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS) + outs() << " S_LAZY_DYLIB_SYMBOL_POINTERS\n"; + else if (section_type == MachO::S_THREAD_LOCAL_REGULAR) + outs() << " S_THREAD_LOCAL_REGULAR\n"; + else if (section_type == MachO::S_THREAD_LOCAL_ZEROFILL) + outs() << " S_THREAD_LOCAL_ZEROFILL\n"; + else if (section_type == MachO::S_THREAD_LOCAL_VARIABLES) + outs() << " S_THREAD_LOCAL_VARIABLES\n"; + else if (section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS) + outs() << " S_THREAD_LOCAL_VARIABLE_POINTERS\n"; + else if (section_type == MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS) + outs() << " S_THREAD_LOCAL_INIT_FUNCTION_POINTERS\n"; + else + outs() << format("0x%08" PRIx32, section_type) << "\n"; + outs() << "attributes"; + uint32_t section_attributes = flags & MachO::SECTION_ATTRIBUTES; + if (section_attributes & MachO::S_ATTR_PURE_INSTRUCTIONS) + outs() << " PURE_INSTRUCTIONS"; + if (section_attributes & MachO::S_ATTR_NO_TOC) + outs() << " NO_TOC"; + if (section_attributes & MachO::S_ATTR_STRIP_STATIC_SYMS) + outs() << " STRIP_STATIC_SYMS"; + if (section_attributes & MachO::S_ATTR_NO_DEAD_STRIP) + outs() << " NO_DEAD_STRIP"; + if (section_attributes & MachO::S_ATTR_LIVE_SUPPORT) + outs() << " LIVE_SUPPORT"; + if (section_attributes & MachO::S_ATTR_SELF_MODIFYING_CODE) + outs() << " SELF_MODIFYING_CODE"; + if (section_attributes & MachO::S_ATTR_DEBUG) + outs() << " DEBUG"; + if (section_attributes & MachO::S_ATTR_SOME_INSTRUCTIONS) + outs() << " SOME_INSTRUCTIONS"; + if (section_attributes & MachO::S_ATTR_EXT_RELOC) + outs() << " EXT_RELOC"; + if (section_attributes & MachO::S_ATTR_LOC_RELOC) + outs() << " LOC_RELOC"; + if (section_attributes == 0) + outs() << " (none)"; + outs() << "\n"; + } else + outs() << " flags " << format("0x%08" PRIx32, flags) << "\n"; + outs() << " reserved1 " << reserved1; + if (section_type == MachO::S_SYMBOL_STUBS || + section_type == MachO::S_LAZY_SYMBOL_POINTERS || + section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS || + section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS || + section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS) + outs() << " (index into indirect symbol table)\n"; + else + outs() << "\n"; + outs() << " reserved2 " << reserved2; + if (section_type == MachO::S_SYMBOL_STUBS) + outs() << " (size of stubs)\n"; + else + outs() << "\n"; +} + +static void PrintSymtabLoadCommand(MachO::symtab_command st, bool Is64Bit, + uint32_t object_size) { + outs() << " cmd LC_SYMTAB\n"; + outs() << " cmdsize " << st.cmdsize; + if (st.cmdsize != sizeof(struct MachO::symtab_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + outs() << " symoff " << st.symoff; + if (st.symoff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " nsyms " << st.nsyms; + uint64_t big_size; + if (Is64Bit) { + big_size = st.nsyms; + big_size *= sizeof(struct MachO::nlist_64); + big_size += st.symoff; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + } else { + big_size = st.nsyms; + big_size *= sizeof(struct MachO::nlist); + big_size += st.symoff; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + } + outs() << " stroff " << st.stroff; + if (st.stroff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " strsize " << st.strsize; + big_size = st.stroff; + big_size += st.strsize; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; +} + +static void PrintDysymtabLoadCommand(MachO::dysymtab_command dyst, + uint32_t nsyms, uint32_t object_size, + bool Is64Bit) { + outs() << " cmd LC_DYSYMTAB\n"; + outs() << " cmdsize " << dyst.cmdsize; + if (dyst.cmdsize != sizeof(struct MachO::dysymtab_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + outs() << " ilocalsym " << dyst.ilocalsym; + if (dyst.ilocalsym > nsyms) + outs() << " (greater than the number of symbols)\n"; + else + outs() << "\n"; + outs() << " nlocalsym " << dyst.nlocalsym; + uint64_t big_size; + big_size = dyst.ilocalsym; + big_size += dyst.nlocalsym; + if (big_size > nsyms) + outs() << " (past the end of the symbol table)\n"; + else + outs() << "\n"; + outs() << " iextdefsym " << dyst.iextdefsym; + if (dyst.iextdefsym > nsyms) + outs() << " (greater than the number of symbols)\n"; + else + outs() << "\n"; + outs() << " nextdefsym " << dyst.nextdefsym; + big_size = dyst.iextdefsym; + big_size += dyst.nextdefsym; + if (big_size > nsyms) + outs() << " (past the end of the symbol table)\n"; + else + outs() << "\n"; + outs() << " iundefsym " << dyst.iundefsym; + if (dyst.iundefsym > nsyms) + outs() << " (greater than the number of symbols)\n"; + else + outs() << "\n"; + outs() << " nundefsym " << dyst.nundefsym; + big_size = dyst.iundefsym; + big_size += dyst.nundefsym; + if (big_size > nsyms) + outs() << " (past the end of the symbol table)\n"; + else + outs() << "\n"; + outs() << " tocoff " << dyst.tocoff; + if (dyst.tocoff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " ntoc " << dyst.ntoc; + big_size = dyst.ntoc; + big_size *= sizeof(struct MachO::dylib_table_of_contents); + big_size += dyst.tocoff; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " modtaboff " << dyst.modtaboff; + if (dyst.modtaboff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " nmodtab " << dyst.nmodtab; + uint64_t modtabend; + if (Is64Bit) { + modtabend = dyst.nmodtab; + modtabend *= sizeof(struct MachO::dylib_module_64); + modtabend += dyst.modtaboff; + } else { + modtabend = dyst.nmodtab; + modtabend *= sizeof(struct MachO::dylib_module); + modtabend += dyst.modtaboff; + } + if (modtabend > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " extrefsymoff " << dyst.extrefsymoff; + if (dyst.extrefsymoff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " nextrefsyms " << dyst.nextrefsyms; + big_size = dyst.nextrefsyms; + big_size *= sizeof(struct MachO::dylib_reference); + big_size += dyst.extrefsymoff; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " indirectsymoff " << dyst.indirectsymoff; + if (dyst.indirectsymoff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " nindirectsyms " << dyst.nindirectsyms; + big_size = dyst.nindirectsyms; + big_size *= sizeof(uint32_t); + big_size += dyst.indirectsymoff; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " extreloff " << dyst.extreloff; + if (dyst.extreloff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " nextrel " << dyst.nextrel; + big_size = dyst.nextrel; + big_size *= sizeof(struct MachO::relocation_info); + big_size += dyst.extreloff; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " locreloff " << dyst.locreloff; + if (dyst.locreloff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " nlocrel " << dyst.nlocrel; + big_size = dyst.nlocrel; + big_size *= sizeof(struct MachO::relocation_info); + big_size += dyst.locreloff; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; +} + +static void PrintDyldInfoLoadCommand(MachO::dyld_info_command dc, + uint32_t object_size) { + if (dc.cmd == MachO::LC_DYLD_INFO) + outs() << " cmd LC_DYLD_INFO\n"; + else + outs() << " cmd LC_DYLD_INFO_ONLY\n"; + outs() << " cmdsize " << dc.cmdsize; + if (dc.cmdsize != sizeof(struct MachO::dyld_info_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + outs() << " rebase_off " << dc.rebase_off; + if (dc.rebase_off > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " rebase_size " << dc.rebase_size; + uint64_t big_size; + big_size = dc.rebase_off; + big_size += dc.rebase_size; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " bind_off " << dc.bind_off; + if (dc.bind_off > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " bind_size " << dc.bind_size; + big_size = dc.bind_off; + big_size += dc.bind_size; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " weak_bind_off " << dc.weak_bind_off; + if (dc.weak_bind_off > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " weak_bind_size " << dc.weak_bind_size; + big_size = dc.weak_bind_off; + big_size += dc.weak_bind_size; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " lazy_bind_off " << dc.lazy_bind_off; + if (dc.lazy_bind_off > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " lazy_bind_size " << dc.lazy_bind_size; + big_size = dc.lazy_bind_off; + big_size += dc.lazy_bind_size; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " export_off " << dc.export_off; + if (dc.export_off > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " export_size " << dc.export_size; + big_size = dc.export_off; + big_size += dc.export_size; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; +} + +static void PrintDyldLoadCommand(MachO::dylinker_command dyld, + const char *Ptr) { + if (dyld.cmd == MachO::LC_ID_DYLINKER) + outs() << " cmd LC_ID_DYLINKER\n"; + else if (dyld.cmd == MachO::LC_LOAD_DYLINKER) + outs() << " cmd LC_LOAD_DYLINKER\n"; + else if (dyld.cmd == MachO::LC_DYLD_ENVIRONMENT) + outs() << " cmd LC_DYLD_ENVIRONMENT\n"; + else + outs() << " cmd ?(" << dyld.cmd << ")\n"; + outs() << " cmdsize " << dyld.cmdsize; + if (dyld.cmdsize < sizeof(struct MachO::dylinker_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + if (dyld.name >= dyld.cmdsize) + outs() << " name ?(bad offset " << dyld.name << ")\n"; + else { + const char *P = (const char *)(Ptr) + dyld.name; + outs() << " name " << P << " (offset " << dyld.name << ")\n"; + } +} + +static void PrintUuidLoadCommand(MachO::uuid_command uuid) { + outs() << " cmd LC_UUID\n"; + outs() << " cmdsize " << uuid.cmdsize; + if (uuid.cmdsize != sizeof(struct MachO::uuid_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + outs() << " uuid "; + outs() << format("%02" PRIX32, uuid.uuid[0]); + outs() << format("%02" PRIX32, uuid.uuid[1]); + outs() << format("%02" PRIX32, uuid.uuid[2]); + outs() << format("%02" PRIX32, uuid.uuid[3]); + outs() << "-"; + outs() << format("%02" PRIX32, uuid.uuid[4]); + outs() << format("%02" PRIX32, uuid.uuid[5]); + outs() << "-"; + outs() << format("%02" PRIX32, uuid.uuid[6]); + outs() << format("%02" PRIX32, uuid.uuid[7]); + outs() << "-"; + outs() << format("%02" PRIX32, uuid.uuid[8]); + outs() << format("%02" PRIX32, uuid.uuid[9]); + outs() << "-"; + outs() << format("%02" PRIX32, uuid.uuid[10]); + outs() << format("%02" PRIX32, uuid.uuid[11]); + outs() << format("%02" PRIX32, uuid.uuid[12]); + outs() << format("%02" PRIX32, uuid.uuid[13]); + outs() << format("%02" PRIX32, uuid.uuid[14]); + outs() << format("%02" PRIX32, uuid.uuid[15]); + outs() << "\n"; +} + +static void PrintVersionMinLoadCommand(MachO::version_min_command vd) { + if (vd.cmd == MachO::LC_VERSION_MIN_MACOSX) + outs() << " cmd LC_VERSION_MIN_MACOSX\n"; + else if (vd.cmd == MachO::LC_VERSION_MIN_IPHONEOS) + outs() << " cmd LC_VERSION_MIN_IPHONEOS\n"; + else + outs() << " cmd " << vd.cmd << " (?)\n"; + outs() << " cmdsize " << vd.cmdsize; + if (vd.cmdsize != sizeof(struct MachO::version_min_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + outs() << " version " << ((vd.version >> 16) & 0xffff) << "." + << ((vd.version >> 8) & 0xff); + if ((vd.version & 0xff) != 0) + outs() << "." << (vd.version & 0xff); + outs() << "\n"; + if (vd.sdk == 0) + outs() << " sdk n/a\n"; + else { + outs() << " sdk " << ((vd.sdk >> 16) & 0xffff) << "." + << ((vd.sdk >> 8) & 0xff); + } + if ((vd.sdk & 0xff) != 0) + outs() << "." << (vd.sdk & 0xff); + outs() << "\n"; +} + +static void PrintSourceVersionCommand(MachO::source_version_command sd) { + outs() << " cmd LC_SOURCE_VERSION\n"; + outs() << " cmdsize " << sd.cmdsize; + if (sd.cmdsize != sizeof(struct MachO::source_version_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + uint64_t a = (sd.version >> 40) & 0xffffff; + uint64_t b = (sd.version >> 30) & 0x3ff; + uint64_t c = (sd.version >> 20) & 0x3ff; + uint64_t d = (sd.version >> 10) & 0x3ff; + uint64_t e = sd.version & 0x3ff; + outs() << " version " << a << "." << b; + if (e != 0) + outs() << "." << c << "." << d << "." << e; + else if (d != 0) + outs() << "." << c << "." << d; + else if (c != 0) + outs() << "." << c; + outs() << "\n"; +} + +static void PrintEntryPointCommand(MachO::entry_point_command ep) { + outs() << " cmd LC_MAIN\n"; + outs() << " cmdsize " << ep.cmdsize; + if (ep.cmdsize != sizeof(struct MachO::entry_point_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + outs() << " entryoff " << ep.entryoff << "\n"; + outs() << " stacksize " << ep.stacksize << "\n"; +} + +static void PrintDylibCommand(MachO::dylib_command dl, const char *Ptr) { + if (dl.cmd == MachO::LC_ID_DYLIB) + outs() << " cmd LC_ID_DYLIB\n"; + else if (dl.cmd == MachO::LC_LOAD_DYLIB) + outs() << " cmd LC_LOAD_DYLIB\n"; + else if (dl.cmd == MachO::LC_LOAD_WEAK_DYLIB) + outs() << " cmd LC_LOAD_WEAK_DYLIB\n"; + else if (dl.cmd == MachO::LC_REEXPORT_DYLIB) + outs() << " cmd LC_REEXPORT_DYLIB\n"; + else if (dl.cmd == MachO::LC_LAZY_LOAD_DYLIB) + outs() << " cmd LC_LAZY_LOAD_DYLIB\n"; + else if (dl.cmd == MachO::LC_LOAD_UPWARD_DYLIB) + outs() << " cmd LC_LOAD_UPWARD_DYLIB\n"; + else + outs() << " cmd " << dl.cmd << " (unknown)\n"; + outs() << " cmdsize " << dl.cmdsize; + if (dl.cmdsize < sizeof(struct MachO::dylib_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + if (dl.dylib.name < dl.cmdsize) { + const char *P = (const char *)(Ptr) + dl.dylib.name; + outs() << " name " << P << " (offset " << dl.dylib.name << ")\n"; + } else { + outs() << " name ?(bad offset " << dl.dylib.name << ")\n"; + } + outs() << " time stamp " << dl.dylib.timestamp << " "; + time_t t = dl.dylib.timestamp; + outs() << ctime(&t); + outs() << " current version "; + if (dl.dylib.current_version == 0xffffffff) + outs() << "n/a\n"; + else + outs() << ((dl.dylib.current_version >> 16) & 0xffff) << "." + << ((dl.dylib.current_version >> 8) & 0xff) << "." + << (dl.dylib.current_version & 0xff) << "\n"; + outs() << "compatibility version "; + if (dl.dylib.compatibility_version == 0xffffffff) + outs() << "n/a\n"; + else + outs() << ((dl.dylib.compatibility_version >> 16) & 0xffff) << "." + << ((dl.dylib.compatibility_version >> 8) & 0xff) << "." + << (dl.dylib.compatibility_version & 0xff) << "\n"; +} + +static void PrintLinkEditDataCommand(MachO::linkedit_data_command ld, + uint32_t object_size) { + if (ld.cmd == MachO::LC_CODE_SIGNATURE) + outs() << " cmd LC_FUNCTION_STARTS\n"; + else if (ld.cmd == MachO::LC_SEGMENT_SPLIT_INFO) + outs() << " cmd LC_SEGMENT_SPLIT_INFO\n"; + else if (ld.cmd == MachO::LC_FUNCTION_STARTS) + outs() << " cmd LC_FUNCTION_STARTS\n"; + else if (ld.cmd == MachO::LC_DATA_IN_CODE) + outs() << " cmd LC_DATA_IN_CODE\n"; + else if (ld.cmd == MachO::LC_DYLIB_CODE_SIGN_DRS) + outs() << " cmd LC_DYLIB_CODE_SIGN_DRS\n"; + else if (ld.cmd == MachO::LC_LINKER_OPTIMIZATION_HINT) + outs() << " cmd LC_LINKER_OPTIMIZATION_HINT\n"; + else + outs() << " cmd " << ld.cmd << " (?)\n"; + outs() << " cmdsize " << ld.cmdsize; + if (ld.cmdsize != sizeof(struct MachO::linkedit_data_command)) + outs() << " Incorrect size\n"; + else + outs() << "\n"; + outs() << " dataoff " << ld.dataoff; + if (ld.dataoff > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; + outs() << " datasize " << ld.datasize; + uint64_t big_size = ld.dataoff; + big_size += ld.datasize; + if (big_size > object_size) + outs() << " (past end of file)\n"; + else + outs() << "\n"; +} + +static void PrintLoadCommands(const MachOObjectFile *Obj, uint32_t ncmds, + uint32_t filetype, uint32_t cputype, + bool verbose) { + StringRef Buf = Obj->getData(); + MachOObjectFile::LoadCommandInfo Command = Obj->getFirstLoadCommandInfo(); + for (unsigned i = 0;; ++i) { + outs() << "Load command " << i << "\n"; + if (Command.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command SLC = Obj->getSegmentLoadCommand(Command); + const char *sg_segname = SLC.segname; + PrintSegmentCommand(SLC.cmd, SLC.cmdsize, SLC.segname, SLC.vmaddr, + SLC.vmsize, SLC.fileoff, SLC.filesize, SLC.maxprot, + SLC.initprot, SLC.nsects, SLC.flags, Buf.size(), + verbose); + for (unsigned j = 0; j < SLC.nsects; j++) { + MachO::section_64 S = Obj->getSection64(Command, j); + PrintSection(S.sectname, S.segname, S.addr, S.size, S.offset, S.align, + S.reloff, S.nreloc, S.flags, S.reserved1, S.reserved2, + SLC.cmd, sg_segname, filetype, Buf.size(), verbose); + } + } else if (Command.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 SLC_64 = Obj->getSegment64LoadCommand(Command); + const char *sg_segname = SLC_64.segname; + PrintSegmentCommand(SLC_64.cmd, SLC_64.cmdsize, SLC_64.segname, + SLC_64.vmaddr, SLC_64.vmsize, SLC_64.fileoff, + SLC_64.filesize, SLC_64.maxprot, SLC_64.initprot, + SLC_64.nsects, SLC_64.flags, Buf.size(), verbose); + for (unsigned j = 0; j < SLC_64.nsects; j++) { + MachO::section_64 S_64 = Obj->getSection64(Command, j); + PrintSection(S_64.sectname, S_64.segname, S_64.addr, S_64.size, + S_64.offset, S_64.align, S_64.reloff, S_64.nreloc, + S_64.flags, S_64.reserved1, S_64.reserved2, SLC_64.cmd, + sg_segname, filetype, Buf.size(), verbose); + } + } else if (Command.C.cmd == MachO::LC_SYMTAB) { + MachO::symtab_command Symtab = Obj->getSymtabLoadCommand(); + PrintSymtabLoadCommand(Symtab, Obj->is64Bit(), Buf.size()); + } else if (Command.C.cmd == MachO::LC_DYSYMTAB) { + MachO::dysymtab_command Dysymtab = Obj->getDysymtabLoadCommand(); + MachO::symtab_command Symtab = Obj->getSymtabLoadCommand(); + PrintDysymtabLoadCommand(Dysymtab, Symtab.nsyms, Buf.size(), + Obj->is64Bit()); + } else if (Command.C.cmd == MachO::LC_DYLD_INFO || + Command.C.cmd == MachO::LC_DYLD_INFO_ONLY) { + MachO::dyld_info_command DyldInfo = Obj->getDyldInfoLoadCommand(Command); + PrintDyldInfoLoadCommand(DyldInfo, Buf.size()); + } else if (Command.C.cmd == MachO::LC_LOAD_DYLINKER || + Command.C.cmd == MachO::LC_ID_DYLINKER || + Command.C.cmd == MachO::LC_DYLD_ENVIRONMENT) { + MachO::dylinker_command Dyld = Obj->getDylinkerCommand(Command); + PrintDyldLoadCommand(Dyld, Command.Ptr); + } else if (Command.C.cmd == MachO::LC_UUID) { + MachO::uuid_command Uuid = Obj->getUuidCommand(Command); + PrintUuidLoadCommand(Uuid); + } else if (Command.C.cmd == MachO::LC_VERSION_MIN_MACOSX) { + MachO::version_min_command Vd = Obj->getVersionMinLoadCommand(Command); + PrintVersionMinLoadCommand(Vd); + } else if (Command.C.cmd == MachO::LC_SOURCE_VERSION) { + MachO::source_version_command Sd = Obj->getSourceVersionCommand(Command); + PrintSourceVersionCommand(Sd); + } else if (Command.C.cmd == MachO::LC_MAIN) { + MachO::entry_point_command Ep = Obj->getEntryPointCommand(Command); + PrintEntryPointCommand(Ep); + } else if (Command.C.cmd == MachO::LC_LOAD_DYLIB || + Command.C.cmd == MachO::LC_ID_DYLIB || + Command.C.cmd == MachO::LC_LOAD_WEAK_DYLIB || + Command.C.cmd == MachO::LC_REEXPORT_DYLIB || + Command.C.cmd == MachO::LC_LAZY_LOAD_DYLIB || + Command.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) { + MachO::dylib_command Dl = Obj->getDylibIDLoadCommand(Command); + PrintDylibCommand(Dl, Command.Ptr); + } else if (Command.C.cmd == MachO::LC_CODE_SIGNATURE || + Command.C.cmd == MachO::LC_SEGMENT_SPLIT_INFO || + Command.C.cmd == MachO::LC_FUNCTION_STARTS || + Command.C.cmd == MachO::LC_DATA_IN_CODE || + Command.C.cmd == MachO::LC_DYLIB_CODE_SIGN_DRS || + Command.C.cmd == MachO::LC_LINKER_OPTIMIZATION_HINT) { + MachO::linkedit_data_command Ld = + Obj->getLinkeditDataLoadCommand(Command); + PrintLinkEditDataCommand(Ld, Buf.size()); + } else { + outs() << " cmd ?(" << format("0x%08" PRIx32, Command.C.cmd) + << ")\n"; + outs() << " cmdsize " << Command.C.cmdsize << "\n"; + // TODO: get and print the raw bytes of the load command. + } + // TODO: print all the other kinds of load commands. + if (i == ncmds - 1) + break; + else + Command = Obj->getNextLoadCommandInfo(Command); + } +} + +static void getAndPrintMachHeader(const MachOObjectFile *Obj, uint32_t &ncmds, + uint32_t &filetype, uint32_t &cputype, + bool verbose) { + if (Obj->is64Bit()) { + MachO::mach_header_64 H_64; + H_64 = Obj->getHeader64(); + PrintMachHeader(H_64.magic, H_64.cputype, H_64.cpusubtype, H_64.filetype, + H_64.ncmds, H_64.sizeofcmds, H_64.flags, verbose); + ncmds = H_64.ncmds; + filetype = H_64.filetype; + cputype = H_64.cputype; + } else { + MachO::mach_header H; + H = Obj->getHeader(); + PrintMachHeader(H.magic, H.cputype, H.cpusubtype, H.filetype, H.ncmds, + H.sizeofcmds, H.flags, verbose); + ncmds = H.ncmds; + filetype = H.filetype; + cputype = H.cputype; } } + +void llvm::printMachOFileHeader(const object::ObjectFile *Obj) { + const MachOObjectFile *file = dyn_cast<const MachOObjectFile>(Obj); + uint32_t ncmds = 0; + uint32_t filetype = 0; + uint32_t cputype = 0; + getAndPrintMachHeader(file, ncmds, filetype, cputype, true); + PrintLoadCommands(file, ncmds, filetype, cputype, true); +} + +//===----------------------------------------------------------------------===// +// export trie dumping +//===----------------------------------------------------------------------===// + +void llvm::printMachOExportsTrie(const object::MachOObjectFile *Obj) { + for (const llvm::object::ExportEntry &Entry : Obj->exports()) { + uint64_t Flags = Entry.flags(); + bool ReExport = (Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT); + bool WeakDef = (Flags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); + bool ThreadLocal = ((Flags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) == + MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL); + bool Abs = ((Flags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) == + MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE); + bool Resolver = (Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER); + if (ReExport) + outs() << "[re-export] "; + else + outs() << format("0x%08llX ", + Entry.address()); // FIXME:add in base address + outs() << Entry.name(); + if (WeakDef || ThreadLocal || Resolver || Abs) { + bool NeedsComma = false; + outs() << " ["; + if (WeakDef) { + outs() << "weak_def"; + NeedsComma = true; + } + if (ThreadLocal) { + if (NeedsComma) + outs() << ", "; + outs() << "per-thread"; + NeedsComma = true; + } + if (Abs) { + if (NeedsComma) + outs() << ", "; + outs() << "absolute"; + NeedsComma = true; + } + if (Resolver) { + if (NeedsComma) + outs() << ", "; + outs() << format("resolver=0x%08llX", Entry.other()); + NeedsComma = true; + } + outs() << "]"; + } + if (ReExport) { + StringRef DylibName = "unknown"; + int Ordinal = Entry.other() - 1; + Obj->getLibraryShortNameByIndex(Ordinal, DylibName); + if (Entry.otherName().empty()) + outs() << " (from " << DylibName << ")"; + else + outs() << " (" << Entry.otherName() << " from " << DylibName << ")"; + } + outs() << "\n"; + } +} + +//===----------------------------------------------------------------------===// +// rebase table dumping +//===----------------------------------------------------------------------===// + +namespace { +class SegInfo { +public: + SegInfo(const object::MachOObjectFile *Obj); + + StringRef segmentName(uint32_t SegIndex); + StringRef sectionName(uint32_t SegIndex, uint64_t SegOffset); + uint64_t address(uint32_t SegIndex, uint64_t SegOffset); + +private: + struct SectionInfo { + uint64_t Address; + uint64_t Size; + StringRef SectionName; + StringRef SegmentName; + uint64_t OffsetInSegment; + uint64_t SegmentStartAddress; + uint32_t SegmentIndex; + }; + const SectionInfo &findSection(uint32_t SegIndex, uint64_t SegOffset); + SmallVector<SectionInfo, 32> Sections; +}; +} + +SegInfo::SegInfo(const object::MachOObjectFile *Obj) { + // Build table of sections so segIndex/offset pairs can be translated. + uint32_t CurSegIndex = Obj->hasPageZeroSegment() ? 1 : 0; + StringRef CurSegName; + uint64_t CurSegAddress; + for (const SectionRef &Section : Obj->sections()) { + SectionInfo Info; + if (error(Section.getName(Info.SectionName))) + return; + Info.Address = Section.getAddress(); + Info.Size = Section.getSize(); + Info.SegmentName = + Obj->getSectionFinalSegmentName(Section.getRawDataRefImpl()); + if (!Info.SegmentName.equals(CurSegName)) { + ++CurSegIndex; + CurSegName = Info.SegmentName; + CurSegAddress = Info.Address; + } + Info.SegmentIndex = CurSegIndex - 1; + Info.OffsetInSegment = Info.Address - CurSegAddress; + Info.SegmentStartAddress = CurSegAddress; + Sections.push_back(Info); + } +} + +StringRef SegInfo::segmentName(uint32_t SegIndex) { + for (const SectionInfo &SI : Sections) { + if (SI.SegmentIndex == SegIndex) + return SI.SegmentName; + } + llvm_unreachable("invalid segIndex"); +} + +const SegInfo::SectionInfo &SegInfo::findSection(uint32_t SegIndex, + uint64_t OffsetInSeg) { + for (const SectionInfo &SI : Sections) { + if (SI.SegmentIndex != SegIndex) + continue; + if (SI.OffsetInSegment > OffsetInSeg) + continue; + if (OffsetInSeg >= (SI.OffsetInSegment + SI.Size)) + continue; + return SI; + } + llvm_unreachable("segIndex and offset not in any section"); +} + +StringRef SegInfo::sectionName(uint32_t SegIndex, uint64_t OffsetInSeg) { + return findSection(SegIndex, OffsetInSeg).SectionName; +} + +uint64_t SegInfo::address(uint32_t SegIndex, uint64_t OffsetInSeg) { + const SectionInfo &SI = findSection(SegIndex, OffsetInSeg); + return SI.SegmentStartAddress + OffsetInSeg; +} + +void llvm::printMachORebaseTable(const object::MachOObjectFile *Obj) { + // Build table of sections so names can used in final output. + SegInfo sectionTable(Obj); + + outs() << "segment section address type\n"; + for (const llvm::object::MachORebaseEntry &Entry : Obj->rebaseTable()) { + uint32_t SegIndex = Entry.segmentIndex(); + uint64_t OffsetInSeg = Entry.segmentOffset(); + StringRef SegmentName = sectionTable.segmentName(SegIndex); + StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); + uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + + // Table lines look like: __DATA __nl_symbol_ptr 0x0000F00C pointer + outs() << format("%-8s %-18s 0x%08" PRIX64 " %s\n", + SegmentName.str().c_str(), SectionName.str().c_str(), + Address, Entry.typeName().str().c_str()); + } +} + +static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { + StringRef DylibName; + switch (Ordinal) { + case MachO::BIND_SPECIAL_DYLIB_SELF: + return "this-image"; + case MachO::BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: + return "main-executable"; + case MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP: + return "flat-namespace"; + default: + if (Ordinal > 0) { + std::error_code EC = + Obj->getLibraryShortNameByIndex(Ordinal - 1, DylibName); + if (EC) + return "<<bad library ordinal>>"; + return DylibName; + } + } + return "<<unknown special ordinal>>"; +} + +//===----------------------------------------------------------------------===// +// bind table dumping +//===----------------------------------------------------------------------===// + +void llvm::printMachOBindTable(const object::MachOObjectFile *Obj) { + // Build table of sections so names can used in final output. + SegInfo sectionTable(Obj); + + outs() << "segment section address type " + "addend dylib symbol\n"; + for (const llvm::object::MachOBindEntry &Entry : Obj->bindTable()) { + uint32_t SegIndex = Entry.segmentIndex(); + uint64_t OffsetInSeg = Entry.segmentOffset(); + StringRef SegmentName = sectionTable.segmentName(SegIndex); + StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); + uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + + // Table lines look like: + // __DATA __got 0x00012010 pointer 0 libSystem ___stack_chk_guard + StringRef Attr; + if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_WEAK_IMPORT) + Attr = " (weak_import)"; + outs() << left_justify(SegmentName, 8) << " " + << left_justify(SectionName, 18) << " " + << format_hex(Address, 10, true) << " " + << left_justify(Entry.typeName(), 8) << " " + << format_decimal(Entry.addend(), 8) << " " + << left_justify(ordinalName(Obj, Entry.ordinal()), 16) << " " + << Entry.symbolName() << Attr << "\n"; + } +} + +//===----------------------------------------------------------------------===// +// lazy bind table dumping +//===----------------------------------------------------------------------===// + +void llvm::printMachOLazyBindTable(const object::MachOObjectFile *Obj) { + // Build table of sections so names can used in final output. + SegInfo sectionTable(Obj); + + outs() << "segment section address " + "dylib symbol\n"; + for (const llvm::object::MachOBindEntry &Entry : Obj->lazyBindTable()) { + uint32_t SegIndex = Entry.segmentIndex(); + uint64_t OffsetInSeg = Entry.segmentOffset(); + StringRef SegmentName = sectionTable.segmentName(SegIndex); + StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); + uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + + // Table lines look like: + // __DATA __got 0x00012010 libSystem ___stack_chk_guard + outs() << left_justify(SegmentName, 8) << " " + << left_justify(SectionName, 18) << " " + << format_hex(Address, 10, true) << " " + << left_justify(ordinalName(Obj, Entry.ordinal()), 16) << " " + << Entry.symbolName() << "\n"; + } +} + +//===----------------------------------------------------------------------===// +// weak bind table dumping +//===----------------------------------------------------------------------===// + +void llvm::printMachOWeakBindTable(const object::MachOObjectFile *Obj) { + // Build table of sections so names can used in final output. + SegInfo sectionTable(Obj); + + outs() << "segment section address " + "type addend symbol\n"; + for (const llvm::object::MachOBindEntry &Entry : Obj->weakBindTable()) { + // Strong symbols don't have a location to update. + if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) { + outs() << " strong " + << Entry.symbolName() << "\n"; + continue; + } + uint32_t SegIndex = Entry.segmentIndex(); + uint64_t OffsetInSeg = Entry.segmentOffset(); + StringRef SegmentName = sectionTable.segmentName(SegIndex); + StringRef SectionName = sectionTable.sectionName(SegIndex, OffsetInSeg); + uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + + // Table lines look like: + // __DATA __data 0x00001000 pointer 0 _foo + outs() << left_justify(SegmentName, 8) << " " + << left_justify(SectionName, 18) << " " + << format_hex(Address, 10, true) << " " + << left_justify(Entry.typeName(), 8) << " " + << format_decimal(Entry.addend(), 8) << " " << Entry.symbolName() + << "\n"; + } +} + +// get_dyld_bind_info_symbolname() is used for disassembly and passed an +// address, ReferenceValue, in the Mach-O file and looks in the dyld bind +// information for that address. If the address is found its binding symbol +// name is returned. If not nullptr is returned. +static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, + struct DisassembleInfo *info) { + if (info->bindtable == nullptr) { + info->bindtable = new (BindTable); + SegInfo sectionTable(info->O); + for (const llvm::object::MachOBindEntry &Entry : info->O->bindTable()) { + uint32_t SegIndex = Entry.segmentIndex(); + uint64_t OffsetInSeg = Entry.segmentOffset(); + uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); + const char *SymbolName = nullptr; + StringRef name = Entry.symbolName(); + if (!name.empty()) + SymbolName = name.data(); + info->bindtable->push_back(std::make_pair(Address, SymbolName)); + } + } + for (bind_table_iterator BI = info->bindtable->begin(), + BE = info->bindtable->end(); + BI != BE; ++BI) { + uint64_t Address = BI->first; + if (ReferenceValue == Address) { + const char *SymbolName = BI->second; + return SymbolName; + } + } + return nullptr; +} diff --git a/tools/llvm-objdump/Makefile b/tools/llvm-objdump/Makefile index c3601eb..4616b78 100644 --- a/tools/llvm-objdump/Makefile +++ b/tools/llvm-objdump/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-objdump -LINK_COMPONENTS := all-targets DebugInfo MC MCAnalysis MCParser MCDisassembler Object +LINK_COMPONENTS := all-targets DebugInfo MC MCParser MCDisassembler Object # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 309bf23..8903bff 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -20,10 +20,6 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" -#include "llvm/MC/MCAnalysis/MCAtom.h" -#include "llvm/MC/MCAnalysis/MCFunction.h" -#include "llvm/MC/MCAnalysis/MCModule.h" -#include "llvm/MC/MCAnalysis/MCModuleYAML.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler.h" @@ -31,9 +27,7 @@ #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrAnalysis.h" #include "llvm/MC/MCInstrInfo.h" -#include "llvm/MC/MCObjectDisassembler.h" #include "llvm/MC/MCObjectFileInfo.h" -#include "llvm/MC/MCObjectSymbolizer.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCRelocationInfo.h" #include "llvm/MC/MCSubtargetInfo.h" @@ -85,6 +79,21 @@ static cl::opt<bool> SymbolTable("t", cl::desc("Display the symbol table")); static cl::opt<bool> +ExportsTrie("exports-trie", cl::desc("Display mach-o exported symbols")); + +static cl::opt<bool> +Rebase("rebase", cl::desc("Display mach-o rebasing info")); + +static cl::opt<bool> +Bind("bind", cl::desc("Display mach-o binding info")); + +static cl::opt<bool> +LazyBind("lazy-bind", cl::desc("Display mach-o lazy binding info")); + +static cl::opt<bool> +WeakBind("weak-bind", cl::desc("Display mach-o weak binding info")); + +static cl::opt<bool> MachOOpt("macho", cl::desc("Use MachO specific object file parser")); static cl::alias MachOm("m", cl::desc("Alias for --macho"), cl::aliasopt(MachOOpt)); @@ -94,6 +103,12 @@ llvm::TripleName("triple", cl::desc("Target triple to disassemble for, " "see -version for available targets")); cl::opt<std::string> +llvm::MCPU("mcpu", + cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), + cl::init("")); + +cl::opt<std::string> llvm::ArchName("arch", cl::desc("Target arch to disassemble for, " "see -version for available targets")); @@ -107,15 +122,16 @@ static cl::alias SectionHeadersShorter("h", cl::desc("Alias for --section-headers"), cl::aliasopt(SectionHeaders)); -static cl::list<std::string> -MAttrs("mattr", +cl::list<std::string> +llvm::MAttrs("mattr", cl::CommaSeparated, cl::desc("Target specific attributes"), cl::value_desc("a1,+a2,-a3,...")); -static cl::opt<bool> -NoShowRawInsn("no-show-raw-insn", cl::desc("When disassembling instructions, " - "do not print the instruction bytes.")); +cl::opt<bool> +llvm::NoShowRawInsn("no-show-raw-insn", cl::desc("When disassembling " + "instructions, do not print " + "the instruction bytes.")); static cl::opt<bool> UnwindInfo("unwind-info", cl::desc("Display unwind information")); @@ -132,20 +148,6 @@ static cl::alias PrivateHeadersShort("p", cl::desc("Alias for --private-headers"), cl::aliasopt(PrivateHeaders)); -static cl::opt<bool> -Symbolize("symbolize", cl::desc("When disassembling instructions, " - "try to symbolize operands.")); - -static cl::opt<bool> -CFG("cfg", cl::desc("Create a CFG for every function found in the object" - " and write it to a graphviz file")); - -// FIXME: Does it make sense to have a dedicated tool for yaml cfg output? -static cl::opt<std::string> -YAMLCFG("yaml-cfg", - cl::desc("Create a CFG and write it as a YAML MCModule."), - cl::value_desc("yaml output file")); - static StringRef ToolName; bool llvm::error(std::error_code EC) { @@ -191,53 +193,6 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) { return TheTarget; } -// Write a graphviz file for the CFG inside an MCFunction. -// FIXME: Use GraphWriter -static void emitDOTFile(const char *FileName, const MCFunction &f, - MCInstPrinter *IP) { - // Start a new dot file. - std::string Error; - raw_fd_ostream Out(FileName, Error, sys::fs::F_Text); - if (!Error.empty()) { - errs() << "llvm-objdump: warning: " << Error << '\n'; - return; - } - - Out << "digraph \"" << f.getName() << "\" {\n"; - Out << "graph [ rankdir = \"LR\" ];\n"; - for (MCFunction::const_iterator i = f.begin(), e = f.end(); i != e; ++i) { - // Only print blocks that have predecessors. - bool hasPreds = (*i)->pred_begin() != (*i)->pred_end(); - - if (!hasPreds && i != f.begin()) - continue; - - Out << '"' << (*i)->getInsts()->getBeginAddr() << "\" [ label=\"<a>"; - // Print instructions. - for (unsigned ii = 0, ie = (*i)->getInsts()->size(); ii != ie; - ++ii) { - if (ii != 0) // Not the first line, start a new row. - Out << '|'; - if (ii + 1 == ie) // Last line, add an end id. - Out << "<o>"; - - // Escape special chars and print the instruction in mnemonic form. - std::string Str; - raw_string_ostream OS(Str); - IP->printInst(&(*i)->getInsts()->at(ii).Inst, OS, ""); - Out << DOT::EscapeString(OS.str()); - } - Out << "\" shape=\"record\" ];\n"; - - // Add edges. - for (MCBasicBlock::succ_const_iterator si = (*i)->succ_begin(), - se = (*i)->succ_end(); si != se; ++si) - Out << (*i)->getInsts()->getBeginAddr() << ":o -> " - << (*si)->getInsts()->getBeginAddr() << ":a\n"; - } - Out << "}\n"; -} - void llvm::DumpBytes(StringRef bytes) { static const char hex_rep[] = "0123456789abcdef"; // FIXME: The real way to do this is to figure out the longest instruction @@ -303,7 +258,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { } std::unique_ptr<const MCSubtargetInfo> STI( - TheTarget->createMCSubtargetInfo(TripleName, "", FeaturesStr)); + TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); if (!STI) { errs() << "error: no subtarget info for target " << TripleName << "\n"; return; @@ -326,19 +281,6 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { return; } - - if (Symbolize) { - std::unique_ptr<MCRelocationInfo> RelInfo( - TheTarget->createMCRelocationInfo(TripleName, Ctx)); - if (RelInfo) { - std::unique_ptr<MCSymbolizer> Symzer( - MCObjectSymbolizer::createObjectSymbolizer(Ctx, std::move(RelInfo), - Obj)); - if (Symzer) - DisAsm->setSymbolizer(std::move(Symzer)); - } - } - std::unique_ptr<const MCInstrAnalysis> MIA( TheTarget->createMCInstrAnalysis(MII.get())); @@ -351,45 +293,6 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { return; } - if (CFG || !YAMLCFG.empty()) { - std::unique_ptr<MCObjectDisassembler> OD( - new MCObjectDisassembler(*Obj, *DisAsm, *MIA)); - std::unique_ptr<MCModule> Mod(OD->buildModule(/* withCFG */ true)); - for (MCModule::const_atom_iterator AI = Mod->atom_begin(), - AE = Mod->atom_end(); - AI != AE; ++AI) { - outs() << "Atom " << (*AI)->getName() << ": \n"; - if (const MCTextAtom *TA = dyn_cast<MCTextAtom>(*AI)) { - for (MCTextAtom::const_iterator II = TA->begin(), IE = TA->end(); - II != IE; - ++II) { - IP->printInst(&II->Inst, outs(), ""); - outs() << "\n"; - } - } - } - if (CFG) { - for (MCModule::const_func_iterator FI = Mod->func_begin(), - FE = Mod->func_end(); - FI != FE; ++FI) { - static int filenum = 0; - emitDOTFile((Twine((*FI)->getName()) + "_" + - utostr(filenum) + ".dot").str().c_str(), - **FI, IP.get()); - ++filenum; - } - } - if (!YAMLCFG.empty()) { - std::string Error; - raw_fd_ostream YAMLOut(YAMLCFG.c_str(), Error, sys::fs::F_Text); - if (!Error.empty()) { - errs() << ToolName << ": warning: " << Error << '\n'; - return; - } - mcmodule2yaml(YAMLOut, *Mod, *MII, *MRI); - } - } - StringRef Fmt = Obj->getBytesInAddress() > 4 ? "\t\t%016" PRIx64 ": " : "\t\t\t%08" PRIx64 ": "; @@ -404,25 +307,18 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { } for (const SectionRef &Section : Obj->sections()) { - bool Text; - if (error(Section.isText(Text))) - break; - if (!Text) + if (!Section.isText() || Section.isVirtual()) continue; - uint64_t SectionAddr; - if (error(Section.getAddress(SectionAddr))) - break; - - uint64_t SectSize; - if (error(Section.getSize(SectSize))) - break; + uint64_t SectionAddr = Section.getAddress(); + uint64_t SectSize = Section.getSize(); + if (!SectSize) + continue; // Make a list of all the symbols in this section. std::vector<std::pair<uint64_t, StringRef>> Symbols; for (const SymbolRef &Symbol : Obj->symbols()) { - bool contains; - if (!error(Section.containsSymbol(Symbol, contains)) && contains) { + if (Section.containsSymbol(Symbol)) { uint64_t Address; if (error(Symbol.getAddress(Address))) break; @@ -477,10 +373,12 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { SmallString<40> Comments; raw_svector_ostream CommentStream(Comments); - StringRef Bytes; - if (error(Section.getContents(Bytes))) + StringRef BytesStr; + if (error(Section.getContents(BytesStr))) break; - StringRefMemoryObject memoryObject(Bytes, SectionAddr); + ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()), + BytesStr.size()); + uint64_t Size; uint64_t Index; @@ -488,17 +386,12 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { std::vector<RelocationRef>::const_iterator rel_end = Rels.end(); // Disassemble symbol by symbol. for (unsigned si = 0, se = Symbols.size(); si != se; ++si) { + uint64_t Start = Symbols[si].first; - uint64_t End; - // The end is either the size of the section or the beginning of the next - // symbol. - if (si == se - 1) - End = SectSize; - // Make sure this symbol takes up space. - else if (Symbols[si + 1].first != Start) - End = Symbols[si + 1].first - 1; - else - // This symbol has the same address as the next symbol. Skip it. + // The end is either the section end or the beginning of the next symbol. + uint64_t End = (si == se - 1) ? SectSize : Symbols[si + 1].first; + // If this symbol has the same address as the next symbol, then skip it. + if (Start == End) continue; outs() << '\n' << Symbols[si].second << ":\n"; @@ -512,13 +405,14 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { for (Index = Start; Index < End; Index += Size) { MCInst Inst; - if (DisAsm->getInstruction(Inst, Size, memoryObject, - SectionAddr + Index, - DebugOut, CommentStream)) { + if (DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), + SectionAddr + Index, DebugOut, + CommentStream)) { outs() << format("%8" PRIx64 ":", SectionAddr + Index); if (!NoShowRawInsn) { outs() << "\t"; - DumpBytes(StringRef(Bytes.data() + Index, Size)); + DumpBytes(StringRef( + reinterpret_cast<const char *>(Bytes.data()) + Index, Size)); } IP->printInst(&Inst, outs(), ""); outs() << CommentStream.str(); @@ -561,6 +455,11 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { static void PrintRelocations(const ObjectFile *Obj) { StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; + // Regular objdump doesn't print relocations in non-relocatable object + // files. + if (!Obj->isRelocatableObject()) + return; + for (const SectionRef &Section : Obj->sections()) { if (Section.relocation_begin() == Section.relocation_end()) continue; @@ -598,19 +497,11 @@ static void PrintSectionHeaders(const ObjectFile *Obj) { StringRef Name; if (error(Section.getName(Name))) return; - uint64_t Address; - if (error(Section.getAddress(Address))) - return; - uint64_t Size; - if (error(Section.getSize(Size))) - return; - bool Text, Data, BSS; - if (error(Section.isText(Text))) - return; - if (error(Section.isData(Data))) - return; - if (error(Section.isBSS(BSS))) - return; + uint64_t Address = Section.getAddress(); + uint64_t Size = Section.getSize(); + bool Text = Section.isText(); + bool Data = Section.isData(); + bool BSS = Section.isBSS(); std::string Type = (std::string(Text ? "TEXT " : "") + (Data ? "DATA " : "") + (BSS ? "BSS" : "")); outs() << format("%3d %-13s %08" PRIx64 " %016" PRIx64 " %s\n", i, @@ -624,25 +515,24 @@ static void PrintSectionContents(const ObjectFile *Obj) { for (const SectionRef &Section : Obj->sections()) { StringRef Name; StringRef Contents; - uint64_t BaseAddr; - bool BSS; if (error(Section.getName(Name))) continue; - if (error(Section.getContents(Contents))) - continue; - if (error(Section.getAddress(BaseAddr))) - continue; - if (error(Section.isBSS(BSS))) + uint64_t BaseAddr = Section.getAddress(); + uint64_t Size = Section.getSize(); + if (!Size) continue; outs() << "Contents of section " << Name << ":\n"; - if (BSS) { + if (Section.isBSS()) { outs() << format("<skipping contents of bss section at [%04" PRIx64 - ", %04" PRIx64 ")>\n", BaseAddr, - BaseAddr + Contents.size()); + ", %04" PRIx64 ")>\n", + BaseAddr, BaseAddr + Size); continue; } + if (error(Section.getContents(Contents))) + continue; + // Dump out the content as hex and printable ascii characters. for (std::size_t addr = 0, end = Contents.size(); addr < end; addr += 16) { outs() << format(" %04" PRIx64 " ", BaseAddr + addr); @@ -670,34 +560,32 @@ static void PrintSectionContents(const ObjectFile *Obj) { } static void PrintCOFFSymbolTable(const COFFObjectFile *coff) { - const coff_file_header *header; - if (error(coff->getHeader(header))) - return; - - for (unsigned SI = 0, SE = header->NumberOfSymbols; SI != SE; ++SI) { - const coff_symbol *Symbol; + for (unsigned SI = 0, SE = coff->getNumberOfSymbols(); SI != SE; ++SI) { + ErrorOr<COFFSymbolRef> Symbol = coff->getSymbol(SI); StringRef Name; - if (error(coff->getSymbol(SI, Symbol))) + if (error(Symbol.getError())) return; - if (error(coff->getSymbolName(Symbol, Name))) + if (error(coff->getSymbolName(*Symbol, Name))) return; outs() << "[" << format("%2d", SI) << "]" - << "(sec " << format("%2d", int(Symbol->SectionNumber)) << ")" + << "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")" << "(fl 0x00)" // Flag bits, which COFF doesn't have. - << "(ty " << format("%3x", unsigned(Symbol->Type)) << ")" - << "(scl " << format("%3x", unsigned(Symbol->StorageClass)) << ") " - << "(nx " << unsigned(Symbol->NumberOfAuxSymbols) << ") " - << "0x" << format("%08x", unsigned(Symbol->Value)) << " " + << "(ty " << format("%3x", unsigned(Symbol->getType())) << ")" + << "(scl " << format("%3x", unsigned(Symbol->getStorageClass())) << ") " + << "(nx " << unsigned(Symbol->getNumberOfAuxSymbols()) << ") " + << "0x" << format("%08x", unsigned(Symbol->getValue())) << " " << Name << "\n"; - for (unsigned AI = 0, AE = Symbol->NumberOfAuxSymbols; AI < AE; ++AI, ++SI) { + for (unsigned AI = 0, AE = Symbol->getNumberOfAuxSymbols(); AI < AE; ++AI, ++SI) { if (Symbol->isSectionDefinition()) { const coff_aux_section_definition *asd; if (error(coff->getAuxSymbol<coff_aux_section_definition>(SI + 1, asd))) return; + int32_t AuxNumber = asd->getNumber(Symbol->isBigObj()); + outs() << "AUX " << format("scnlen 0x%x nreloc %d nlnno %d checksum 0x%x " , unsigned(asd->Length) @@ -705,18 +593,18 @@ static void PrintCOFFSymbolTable(const COFFObjectFile *coff) { , unsigned(asd->NumberOfLinenumbers) , unsigned(asd->CheckSum)) << format("assoc %d comdat %d\n" - , unsigned(asd->Number) + , unsigned(AuxNumber) , unsigned(asd->Selection)); } else if (Symbol->isFileRecord()) { - const coff_aux_file *AF; - if (error(coff->getAuxSymbol<coff_aux_file>(SI + 1, AF))) + const char *FileName; + if (error(coff->getAuxSymbol<char>(SI + 1, FileName))) return; - StringRef Name(AF->FileName, - Symbol->NumberOfAuxSymbols * COFF::SymbolSize); + StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() * + coff->getSymbolTableEntrySize()); outs() << "AUX " << Name.rtrim(StringRef("\0", 1)) << '\n'; - SI = SI + Symbol->NumberOfAuxSymbols; + SI = SI + Symbol->getNumberOfAuxSymbols(); break; } else { outs() << "AUX Unknown\n"; @@ -809,10 +697,67 @@ static void PrintUnwindInfo(const ObjectFile *o) { if (const COFFObjectFile *coff = dyn_cast<COFFObjectFile>(o)) { printCOFFUnwindInfo(coff); - } else { + } else if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachOUnwindInfo(MachO); + else { // TODO: Extract DWARF dump tool to objdump. errs() << "This operation is only currently supported " - "for COFF object files.\n"; + "for COFF and MachO object files.\n"; + return; + } +} + +static void printExportsTrie(const ObjectFile *o) { + outs() << "Exports trie:\n"; + if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachOExportsTrie(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +static void printRebaseTable(const ObjectFile *o) { + outs() << "Rebase table:\n"; + if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachORebaseTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +static void printBindTable(const ObjectFile *o) { + outs() << "Bind table:\n"; + if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachOBindTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +static void printLazyBindTable(const ObjectFile *o) { + outs() << "Lazy bind table:\n"; + if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachOLazyBindTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; + return; + } +} + +static void printWeakBindTable(const ObjectFile *o) { + outs() << "Weak bind table:\n"; + if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + printMachOWeakBindTable(MachO); + else { + errs() << "This operation is only currently supported " + "for Mach-O executable files.\n"; return; } } @@ -822,6 +767,8 @@ static void printPrivateFileHeader(const ObjectFile *o) { printELFFileHeader(o); } else if (o->isCOFF()) { printCOFFFileHeader(o); + } else if (o->isMachO()) { + printMachOFileHeader(o); } } @@ -844,6 +791,16 @@ static void DumpObject(const ObjectFile *o) { PrintUnwindInfo(o); if (PrivateHeaders) printPrivateFileHeader(o); + if (ExportsTrie) + printExportsTrie(o); + if (Rebase) + printRebaseTable(o); + if (Bind) + printBindTable(o); + if (LazyBind) + printLazyBindTable(o); + if (WeakBind) + printWeakBindTable(o); } /// @brief Dump each object file in \a a; @@ -880,16 +837,16 @@ static void DumpInput(StringRef file) { } // Attempt to open the binary. - ErrorOr<Binary *> BinaryOrErr = createBinary(file); + ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(file); if (std::error_code EC = BinaryOrErr.getError()) { errs() << ToolName << ": '" << file << "': " << EC.message() << ".\n"; return; } - std::unique_ptr<Binary> binary(BinaryOrErr.get()); + Binary &Binary = *BinaryOrErr.get().getBinary(); - if (Archive *a = dyn_cast<Archive>(binary.get())) + if (Archive *a = dyn_cast<Archive>(&Binary)) DumpArchive(a); - else if (ObjectFile *o = dyn_cast<ObjectFile>(binary.get())) + else if (ObjectFile *o = dyn_cast<ObjectFile>(&Binary)) DumpObject(o); else errs() << ToolName << ": '" << file << "': " << "Unrecognized file type.\n"; @@ -925,7 +882,12 @@ int main(int argc, char **argv) { && !SectionContents && !SymbolTable && !UnwindInfo - && !PrivateHeaders) { + && !PrivateHeaders + && !ExportsTrie + && !Rebase + && !Bind + && !LazyBind + && !WeakBind) { cl::PrintHelpMessage(); return 2; } diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h index 80f8f58..ef1509f 100644 --- a/tools/llvm-objdump/llvm-objdump.h +++ b/tools/llvm-objdump/llvm-objdump.h @@ -7,23 +7,26 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_OBJDUMP_H -#define LLVM_OBJDUMP_H +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_LLVM_OBJDUMP_H +#define LLVM_TOOLS_LLVM_OBJDUMP_LLVM_OBJDUMP_H #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DataTypes.h" -#include "llvm/Support/StringRefMemoryObject.h" namespace llvm { namespace object { class COFFObjectFile; + class MachOObjectFile; class ObjectFile; class RelocationRef; } extern cl::opt<std::string> TripleName; extern cl::opt<std::string> ArchName; +extern cl::opt<std::string> MCPU; +extern cl::list<std::string> MAttrs; +extern cl::opt<bool> NoShowRawInsn; // Various helper functions. bool error(std::error_code ec); @@ -31,8 +34,15 @@ bool RelocAddressLess(object::RelocationRef a, object::RelocationRef b); void DumpBytes(StringRef bytes); void DisassembleInputMachO(StringRef Filename); void printCOFFUnwindInfo(const object::COFFObjectFile* o); +void printMachOUnwindInfo(const object::MachOObjectFile* o); +void printMachOExportsTrie(const object::MachOObjectFile* o); +void printMachORebaseTable(const object::MachOObjectFile* o); +void printMachOBindTable(const object::MachOObjectFile* o); +void printMachOLazyBindTable(const object::MachOObjectFile* o); +void printMachOWeakBindTable(const object::MachOObjectFile* o); void printELFFileHeader(const object::ObjectFile *o); void printCOFFFileHeader(const object::ObjectFile *o); +void printMachOFileHeader(const object::ObjectFile *o); } // end namespace llvm diff --git a/tools/llvm-profdata/CMakeLists.txt b/tools/llvm-profdata/CMakeLists.txt index 3529114..0e330fd 100644 --- a/tools/llvm-profdata/CMakeLists.txt +++ b/tools/llvm-profdata/CMakeLists.txt @@ -1,4 +1,8 @@ -set(LLVM_LINK_COMPONENTS profiledata support) +set(LLVM_LINK_COMPONENTS + Core + ProfileData + Support + ) add_llvm_tool(llvm-profdata llvm-profdata.cpp diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp index ba88aad..e977799 100644 --- a/tools/llvm-profdata/llvm-profdata.cpp +++ b/tools/llvm-profdata/llvm-profdata.cpp @@ -14,6 +14,9 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ProfileData/InstrProfReader.h" #include "llvm/ProfileData/InstrProfWriter.h" +#include "llvm/ProfileData/SampleProfReader.h" +#include "llvm/ProfileData/SampleProfWriter.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" @@ -33,32 +36,24 @@ static void exitWithError(const Twine &Message, StringRef Whence = "") { ::exit(1); } -int merge_main(int argc, const char *argv[]) { - cl::list<std::string> Inputs(cl::Positional, cl::Required, cl::OneOrMore, - cl::desc("<filenames...>")); - - cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), - cl::init("-"), - cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), cl::Required, - cl::aliasopt(OutputFilename)); - - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); +enum ProfileKinds { instr, sample }; +void mergeInstrProfile(cl::list<std::string> Inputs, StringRef OutputFilename) { if (OutputFilename.compare("-") == 0) exitWithError("Cannot write indexed profdata format to stdout."); - std::string ErrorInfo; - raw_fd_ostream Output(OutputFilename.data(), ErrorInfo, sys::fs::F_None); - if (!ErrorInfo.empty()) - exitWithError(ErrorInfo, OutputFilename); + std::error_code EC; + raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); + if (EC) + exitWithError(EC.message(), OutputFilename); InstrProfWriter Writer; for (const auto &Filename : Inputs) { - std::unique_ptr<InstrProfReader> Reader; - if (std::error_code ec = InstrProfReader::create(Filename, Reader)) + auto ReaderOrErr = InstrProfReader::create(Filename); + if (std::error_code ec = ReaderOrErr.getError()) exitWithError(ec.message(), Filename); + auto Reader = std::move(ReaderOrErr.get()); for (const auto &I : *Reader) if (std::error_code EC = Writer.addFunctionCounts(I.Name, I.Hash, I.Counts)) @@ -67,50 +62,86 @@ int merge_main(int argc, const char *argv[]) { exitWithError(Reader->getError().message(), Filename); } Writer.write(Output); - - return 0; } -int show_main(int argc, const char *argv[]) { - cl::opt<std::string> Filename(cl::Positional, cl::Required, - cl::desc("<profdata-file>")); +void mergeSampleProfile(cl::list<std::string> Inputs, StringRef OutputFilename, + sampleprof::SampleProfileFormat OutputFormat) { + using namespace sampleprof; + auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); + if (std::error_code EC = WriterOrErr.getError()) + exitWithError(EC.message(), OutputFilename); - cl::opt<bool> ShowCounts("counts", cl::init(false), - cl::desc("Show counter values for shown functions")); - cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false), - cl::desc("Details for every function")); - cl::opt<std::string> ShowFunction("function", - cl::desc("Details for matching functions")); + auto Writer = std::move(WriterOrErr.get()); + StringMap<FunctionSamples> ProfileMap; + for (const auto &Filename : Inputs) { + auto ReaderOrErr = + SampleProfileReader::create(Filename, getGlobalContext()); + if (std::error_code EC = ReaderOrErr.getError()) + exitWithError(EC.message(), Filename); + + auto Reader = std::move(ReaderOrErr.get()); + if (std::error_code EC = Reader->read()) + exitWithError(EC.message(), Filename); + + StringMap<FunctionSamples> &Profiles = Reader->getProfiles(); + for (StringMap<FunctionSamples>::iterator I = Profiles.begin(), + E = Profiles.end(); + I != E; ++I) { + StringRef FName = I->first(); + FunctionSamples &Samples = I->second; + ProfileMap[FName].merge(Samples); + } + } + Writer->write(ProfileMap); +} + +int merge_main(int argc, const char *argv[]) { + cl::list<std::string> Inputs(cl::Positional, cl::Required, cl::OneOrMore, + cl::desc("<filenames...>")); cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), - cl::init("-"), + cl::init("-"), cl::Required, cl::desc("Output file")); cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), cl::aliasopt(OutputFilename)); + cl::opt<ProfileKinds> ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"), clEnumValEnd)); + + cl::opt<sampleprof::SampleProfileFormat> OutputFormat( + cl::desc("Format of output profile (only meaningful with --sample)"), + cl::init(sampleprof::SPF_Binary), + cl::values(clEnumValN(sampleprof::SPF_Binary, "binary", + "Binary encoding (default)"), + clEnumValN(sampleprof::SPF_Text, "text", "Text encoding"), + clEnumValN(sampleprof::SPF_GCC, "gcc", "GCC encoding"), + clEnumValEnd)); - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - - std::unique_ptr<InstrProfReader> Reader; - if (std::error_code EC = InstrProfReader::create(Filename, Reader)) - exitWithError(EC.message(), Filename); + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); - if (OutputFilename.empty()) - OutputFilename = "-"; + if (ProfileKind == instr) + mergeInstrProfile(Inputs, OutputFilename); + else + mergeSampleProfile(Inputs, OutputFilename, OutputFormat); - std::string ErrorInfo; - raw_fd_ostream OS(OutputFilename.data(), ErrorInfo, sys::fs::F_Text); - if (!ErrorInfo.empty()) - exitWithError(ErrorInfo, OutputFilename); + return 0; +} - if (ShowAllFunctions && !ShowFunction.empty()) - errs() << "warning: -function argument ignored: showing all functions\n"; +int showInstrProfile(std::string Filename, bool ShowCounts, + bool ShowAllFunctions, std::string ShowFunction, + raw_fd_ostream &OS) { + auto ReaderOrErr = InstrProfReader::create(Filename); + if (std::error_code EC = ReaderOrErr.getError()) + exitWithError(EC.message(), Filename); + auto Reader = std::move(ReaderOrErr.get()); uint64_t MaxFunctionCount = 0, MaxBlockCount = 0; size_t ShownFunctions = 0, TotalFunctions = 0; for (const auto &Func : *Reader) { - bool Show = ShowAllFunctions || - (!ShowFunction.empty() && - Func.Name.find(ShowFunction) != Func.Name.npos); + bool Show = + ShowAllFunctions || (!ShowFunction.empty() && + Func.Name.find(ShowFunction) != Func.Name.npos); ++TotalFunctions; assert(Func.Counts.size() > 0 && "function missing entry counter"); @@ -150,6 +181,65 @@ int show_main(int argc, const char *argv[]) { return 0; } +int showSampleProfile(std::string Filename, bool ShowCounts, + bool ShowAllFunctions, std::string ShowFunction, + raw_fd_ostream &OS) { + using namespace sampleprof; + auto ReaderOrErr = SampleProfileReader::create(Filename, getGlobalContext()); + if (std::error_code EC = ReaderOrErr.getError()) + exitWithError(EC.message(), Filename); + + auto Reader = std::move(ReaderOrErr.get()); + Reader->read(); + if (ShowAllFunctions || ShowFunction.empty()) + Reader->dump(OS); + else + Reader->dumpFunctionProfile(ShowFunction, OS); + + return 0; +} + +int show_main(int argc, const char *argv[]) { + cl::opt<std::string> Filename(cl::Positional, cl::Required, + cl::desc("<profdata-file>")); + + cl::opt<bool> ShowCounts("counts", cl::init(false), + cl::desc("Show counter values for shown functions")); + cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false), + cl::desc("Details for every function")); + cl::opt<std::string> ShowFunction("function", + cl::desc("Details for matching functions")); + + cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file")); + cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::opt<ProfileKinds> ProfileKind( + cl::desc("Profile kind:"), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"), clEnumValEnd)); + + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); + + if (OutputFilename.empty()) + OutputFilename = "-"; + + std::error_code EC; + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); + if (EC) + exitWithError(EC.message(), OutputFilename); + + if (ShowAllFunctions && !ShowFunction.empty()) + errs() << "warning: -function argument ignored: showing all functions\n"; + + if (ProfileKind == instr) + return showInstrProfile(Filename, ShowCounts, ShowAllFunctions, + ShowFunction, OS); + else + return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, + ShowFunction, OS); +} + int main(int argc, const char *argv[]) { // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal(); diff --git a/tools/llvm-readobj/ARMAttributeParser.h b/tools/llvm-readobj/ARMAttributeParser.h index c286251..f924c83 100644 --- a/tools/llvm-readobj/ARMAttributeParser.h +++ b/tools/llvm-readobj/ARMAttributeParser.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_READOBJ_ARMATTRIBUTE_PARSER_H -#define LLVM_READOBJ_ARMATTRIBUTE_PARSER_H +#ifndef LLVM_TOOLS_LLVM_READOBJ_ARMATTRIBUTEPARSER_H +#define LLVM_TOOLS_LLVM_READOBJ_ARMATTRIBUTEPARSER_H #include "StreamWriter.h" #include "llvm/Support/ARMBuildAttributes.h" diff --git a/tools/llvm-readobj/ARMEHABIPrinter.h b/tools/llvm-readobj/ARMEHABIPrinter.h index 7608cfb..b15421d 100644 --- a/tools/llvm-readobj/ARMEHABIPrinter.h +++ b/tools/llvm-readobj/ARMEHABIPrinter.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_READOBJ_ARMEHABI_PRINTER_H -#define LLVM_READOBJ_ARMEHABI_PRINTER_H +#ifndef LLVM_TOOLS_LLVM_READOBJ_ARMEHABIPRINTER_H +#define LLVM_TOOLS_LLVM_READOBJ_ARMEHABIPRINTER_H #include "Error.h" #include "StreamWriter.h" diff --git a/tools/llvm-readobj/ARMWinEHPrinter.cpp b/tools/llvm-readobj/ARMWinEHPrinter.cpp index b486e4a..ede36d1 100644 --- a/tools/llvm-readobj/ARMWinEHPrinter.cpp +++ b/tools/llvm-readobj/ARMWinEHPrinter.cpp @@ -186,13 +186,8 @@ void Decoder::printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask) ErrorOr<object::SectionRef> Decoder::getSectionContaining(const COFFObjectFile &COFF, uint64_t VA) { for (const auto &Section : COFF.sections()) { - uint64_t Address; - uint64_t Size; - - if (std::error_code EC = Section.getAddress(Address)) - return EC; - if (std::error_code EC = Section.getSize(Size)) - return EC; + uint64_t Address = Section.getAddress(); + uint64_t Size = Section.getSize(); if (VA >= Address && (VA - Address) <= Size) return Section; @@ -233,7 +228,7 @@ ErrorOr<SymbolRef> Decoder::getRelocatedSymbol(const COFFObjectFile &, return readobj_error::unknown_symbol; } -bool Decoder::opcode_0xxxxxxx(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_0xxxxxxx(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { uint8_t Imm = OC[Offset] & 0x7f; SW.startLine() << format("0x%02x ; %s sp, #(%u * 4)\n", @@ -244,7 +239,7 @@ bool Decoder::opcode_0xxxxxxx(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_10Lxxxxx(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_10Lxxxxx(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { unsigned Link = (OC[Offset] & 0x20) >> 5; uint16_t RegisterMask = (Link << (Prologue ? 14 : 15)) @@ -263,7 +258,7 @@ bool Decoder::opcode_10Lxxxxx(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_1100xxxx(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_1100xxxx(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { if (Prologue) SW.startLine() << format("0x%02x ; mov r%u, sp\n", @@ -275,7 +270,7 @@ bool Decoder::opcode_1100xxxx(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11010Lxx(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11010Lxx(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { unsigned Link = (OC[Offset] & 0x4) >> 3; unsigned Count = (OC[Offset] & 0x3); @@ -292,7 +287,7 @@ bool Decoder::opcode_11010Lxx(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11011Lxx(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11011Lxx(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { unsigned Link = (OC[Offset] & 0x4) >> 2; unsigned Count = (OC[Offset] & 0x3) + 4; @@ -309,7 +304,7 @@ bool Decoder::opcode_11011Lxx(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11100xxx(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11100xxx(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { unsigned High = (OC[Offset] & 0x7); uint32_t VFPMask = (((1 << (High + 1)) - 1) << 8); @@ -323,7 +318,7 @@ bool Decoder::opcode_11100xxx(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_111010xx(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_111010xx(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { uint16_t Imm = ((OC[Offset + 0] & 0x03) << 8) | ((OC[Offset + 1] & 0xff) << 0); @@ -336,7 +331,7 @@ bool Decoder::opcode_111010xx(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_1110110L(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_1110110L(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { uint8_t GPRMask = ((OC[Offset + 0] & 0x01) << (Prologue ? 14 : 15)) | ((OC[Offset + 1] & 0xff) << 0); @@ -350,7 +345,7 @@ bool Decoder::opcode_1110110L(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11101110(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11101110(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { assert(!Prologue && "may not be used in prologue"); @@ -366,7 +361,7 @@ bool Decoder::opcode_11101110(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11101111(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11101111(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { assert(!Prologue && "may not be used in prologue"); @@ -382,7 +377,7 @@ bool Decoder::opcode_11101111(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11110101(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11110101(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { unsigned Start = (OC[Offset + 1] & 0xf0) >> 4; unsigned End = (OC[Offset + 1] & 0x0f) >> 0; @@ -397,7 +392,7 @@ bool Decoder::opcode_11110101(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11110110(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11110110(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { unsigned Start = (OC[Offset + 1] & 0xf0) >> 4; unsigned End = (OC[Offset + 1] & 0x0f) >> 0; @@ -412,7 +407,7 @@ bool Decoder::opcode_11110110(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11110111(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11110111(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { uint32_t Imm = (OC[Offset + 1] << 8) | (OC[Offset + 2] << 0); @@ -425,7 +420,7 @@ bool Decoder::opcode_11110111(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11111000(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11111000(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { uint32_t Imm = (OC[Offset + 1] << 16) | (OC[Offset + 2] << 8) @@ -440,7 +435,7 @@ bool Decoder::opcode_11111000(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11111001(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11111001(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { uint32_t Imm = (OC[Offset + 1] << 8) | (OC[Offset + 2] << 0); @@ -453,7 +448,7 @@ bool Decoder::opcode_11111001(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11111010(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11111010(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { uint32_t Imm = (OC[Offset + 1] << 16) | (OC[Offset + 2] << 8) @@ -468,41 +463,41 @@ bool Decoder::opcode_11111010(const ulittle8_t *OC, unsigned &Offset, return false; } -bool Decoder::opcode_11111011(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11111011(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { SW.startLine() << format("0x%02x ; nop\n", OC[Offset]); ++Offset; return false; } -bool Decoder::opcode_11111100(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11111100(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { SW.startLine() << format("0x%02x ; nop.w\n", OC[Offset]); ++Offset; return false; } -bool Decoder::opcode_11111101(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11111101(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { SW.startLine() << format("0x%02x ; b\n", OC[Offset]); ++Offset; return true; } -bool Decoder::opcode_11111110(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11111110(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { SW.startLine() << format("0x%02x ; b.w\n", OC[Offset]); ++Offset; return true; } -bool Decoder::opcode_11111111(const ulittle8_t *OC, unsigned &Offset, +bool Decoder::opcode_11111111(const uint8_t *OC, unsigned &Offset, unsigned Length, bool Prologue) { ++Offset; return true; } -void Decoder::decodeOpcodes(ArrayRef<ulittle8_t> Opcodes, unsigned Offset, +void Decoder::decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset, bool Prologue) { assert((!Prologue || Offset == 0) && "prologue should always use offset 0"); @@ -525,10 +520,7 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, if (COFF.getSectionContents(COFF.getCOFFSection(Section), Contents)) return false; - uint64_t SectionVA; - if (Section.getAddress(SectionVA)) - return false; - + uint64_t SectionVA = Section.getAddress(); uint64_t Offset = VA - SectionVA; const ulittle32_t *Data = reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset); @@ -546,7 +538,7 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, static_cast<uint64_t>(XData.CodeWords() * sizeof(uint32_t))); if (XData.E()) { - ArrayRef<ulittle8_t> UC = XData.UnwindByteCode(); + ArrayRef<uint8_t> UC = XData.UnwindByteCode(); if (!XData.F()) { ListScope PS(SW, "Prologue"); decodeOpcodes(UC, 0, /*Prologue=*/true); @@ -741,4 +733,3 @@ std::error_code Decoder::dumpProcedureData(const COFFObjectFile &COFF) { } } } - diff --git a/tools/llvm-readobj/ARMWinEHPrinter.h b/tools/llvm-readobj/ARMWinEHPrinter.h index 740c8b5..274ef11 100644 --- a/tools/llvm-readobj/ARMWinEHPrinter.h +++ b/tools/llvm-readobj/ARMWinEHPrinter.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_READOBJ_ARMWINEHPRINTER_H -#define LLVM_READOBJ_ARMWINEHPRINTER_H +#ifndef LLVM_TOOLS_LLVM_READOBJ_ARMWINEHPRINTER_H +#define LLVM_TOOLS_LLVM_READOBJ_ARMWINEHPRINTER_H #include "StreamWriter.h" #include "llvm/Object/COFF.h" @@ -28,55 +28,54 @@ class Decoder { struct RingEntry { uint8_t Mask; uint8_t Value; - bool (Decoder::*Routine)(const support::ulittle8_t *, unsigned &, unsigned, - bool); + bool (Decoder::*Routine)(const uint8_t *, unsigned &, unsigned, bool); }; static const RingEntry Ring[]; - bool opcode_0xxxxxxx(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_0xxxxxxx(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_10Lxxxxx(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_10Lxxxxx(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_1100xxxx(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_1100xxxx(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11010Lxx(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11010Lxx(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11011Lxx(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11011Lxx(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11100xxx(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11100xxx(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_111010xx(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_111010xx(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_1110110L(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_1110110L(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11101110(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11101110(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11101111(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11101111(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11110101(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11110101(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11110110(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11110110(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11110111(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11110111(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11111000(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11111000(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11111001(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11111001(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11111010(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11111010(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11111011(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11111011(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11111100(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11111100(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11111101(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11111101(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11111110(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11111110(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - bool opcode_11111111(const support::ulittle8_t *Opcodes, unsigned &Offset, + bool opcode_11111111(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); - void decodeOpcodes(ArrayRef<support::ulittle8_t> Opcodes, unsigned Offset, + void decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset, bool Prologue); void printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask); @@ -116,4 +115,3 @@ public: } #endif - diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index 7842cd4..5276428 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -20,6 +20,7 @@ #include "Win64EHDumper.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/COFF.h" @@ -49,35 +50,49 @@ public: cacheRelocations(); } - virtual void printFileHeaders() override; - virtual void printSections() override; - virtual void printRelocations() override; - virtual void printSymbols() override; - virtual void printDynamicSymbols() override; - virtual void printUnwindInfo() override; + void printFileHeaders() override; + void printSections() override; + void printRelocations() override; + void printSymbols() override; + void printDynamicSymbols() override; + void printUnwindInfo() override; + void printCOFFImports() override; + void printCOFFDirectives() override; + void printCOFFBaseReloc() override; private: void printSymbol(const SymbolRef &Sym); void printRelocation(const SectionRef &Section, const RelocationRef &Reloc); void printDataDirectory(uint32_t Index, const std::string &FieldName); + void printDOSHeader(const dos_header *DH); template <class PEHeader> void printPEHeader(const PEHeader *Hdr); void printBaseOfDataField(const pe32_header *Hdr); void printBaseOfDataField(const pe32plus_header *Hdr); void printCodeViewLineTables(const SectionRef &Section); + void printCodeViewSymbolsSubsection(StringRef Subsection, + const SectionRef &Section, + uint32_t Offset); + void cacheRelocations(); std::error_code resolveSymbol(const coff_section *Section, uint64_t Offset, SymbolRef &Sym); std::error_code resolveSymbolName(const coff_section *Section, uint64_t Offset, StringRef &Name); + void printImportedSymbols(iterator_range<imported_symbol_iterator> Range); + void printDelayImportedSymbols( + const DelayImportDirectoryEntryRef &I, + iterator_range<imported_symbol_iterator> Range); typedef DenseMap<const coff_section*, std::vector<RelocationRef> > RelocMapTy; const llvm::object::COFFObjectFile *Obj; RelocMapTy RelocMap; + StringRef CVFileIndexToStringOffsetTable; + StringRef CVStringTable; }; } // namespace @@ -313,9 +328,10 @@ WeakExternalCharacteristics[] = { template <typename T> static std::error_code getSymbolAuxData(const COFFObjectFile *Obj, - const coff_symbol *Symbol, - const T *&Aux) { + COFFSymbolRef Symbol, + uint8_t AuxSymbolIdx, const T *&Aux) { ArrayRef<uint8_t> AuxData = Obj->getSymbolAuxData(Symbol); + AuxData = AuxData.slice(AuxSymbolIdx * Obj->getSymbolTableEntrySize()); Aux = reinterpret_cast<const T*>(AuxData.data()); return readobj_error::success; } @@ -342,25 +358,20 @@ void COFFDumper::printDataDirectory(uint32_t Index, const std::string &FieldName } void COFFDumper::printFileHeaders() { - // Print COFF header - const coff_file_header *COFFHeader = nullptr; - if (error(Obj->getCOFFHeader(COFFHeader))) - return; - - time_t TDS = COFFHeader->TimeDateStamp; + time_t TDS = Obj->getTimeDateStamp(); char FormattedTime[20] = { }; strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS)); { DictScope D(W, "ImageFileHeader"); - W.printEnum ("Machine", COFFHeader->Machine, + W.printEnum ("Machine", Obj->getMachine(), makeArrayRef(ImageFileMachineType)); - W.printNumber("SectionCount", COFFHeader->NumberOfSections); - W.printHex ("TimeDateStamp", FormattedTime, COFFHeader->TimeDateStamp); - W.printHex ("PointerToSymbolTable", COFFHeader->PointerToSymbolTable); - W.printNumber("SymbolCount", COFFHeader->NumberOfSymbols); - W.printNumber("OptionalHeaderSize", COFFHeader->SizeOfOptionalHeader); - W.printFlags ("Characteristics", COFFHeader->Characteristics, + W.printNumber("SectionCount", Obj->getNumberOfSections()); + W.printHex ("TimeDateStamp", FormattedTime, Obj->getTimeDateStamp()); + W.printHex ("PointerToSymbolTable", Obj->getPointerToSymbolTable()); + W.printNumber("SymbolCount", Obj->getNumberOfSymbols()); + W.printNumber("OptionalHeaderSize", Obj->getSizeOfOptionalHeader()); + W.printFlags ("Characteristics", Obj->getCharacteristics(), makeArrayRef(ImageFileCharacteristics)); } @@ -377,6 +388,30 @@ void COFFDumper::printFileHeaders() { return; if (PEPlusHeader) printPEHeader<pe32plus_header>(PEPlusHeader); + + if (const dos_header *DH = Obj->getDOSHeader()) + printDOSHeader(DH); +} + +void COFFDumper::printDOSHeader(const dos_header *DH) { + DictScope D(W, "DOSHeader"); + W.printString("Magic", StringRef(DH->Magic, sizeof(DH->Magic))); + W.printNumber("UsedBytesInTheLastPage", DH->UsedBytesInTheLastPage); + W.printNumber("FileSizeInPages", DH->FileSizeInPages); + W.printNumber("NumberOfRelocationItems", DH->NumberOfRelocationItems); + W.printNumber("HeaderSizeInParagraphs", DH->HeaderSizeInParagraphs); + W.printNumber("MinimumExtraParagraphs", DH->MinimumExtraParagraphs); + W.printNumber("MaximumExtraParagraphs", DH->MaximumExtraParagraphs); + W.printNumber("InitialRelativeSS", DH->InitialRelativeSS); + W.printNumber("InitialSP", DH->InitialSP); + W.printNumber("Checksum", DH->Checksum); + W.printNumber("InitialIP", DH->InitialIP); + W.printNumber("InitialRelativeCS", DH->InitialRelativeCS); + W.printNumber("AddressOfRelocationTable", DH->AddressOfRelocationTable); + W.printNumber("OverlayNumber", DH->OverlayNumber); + W.printNumber("OEMid", DH->OEMid); + W.printNumber("OEMinfo", DH->OEMinfo); + W.printNumber("AddressOfNewExeHeader", DH->AddressOfNewExeHeader); } template <class PEHeader> @@ -404,7 +439,7 @@ void COFFDumper::printPEHeader(const PEHeader *Hdr) { W.printNumber("SizeOfImage", Hdr->SizeOfImage); W.printNumber("SizeOfHeaders", Hdr->SizeOfHeaders); W.printEnum ("Subsystem", Hdr->Subsystem, makeArrayRef(PEWindowsSubsystem)); - W.printFlags ("Subsystem", Hdr->DLLCharacteristics, + W.printFlags ("Characteristics", Hdr->DLLCharacteristics, makeArrayRef(PEDLLCharacteristics)); W.printNumber("SizeOfStackReserve", Hdr->SizeOfStackReserve); W.printNumber("SizeOfStackCommit", Hdr->SizeOfStackCommit); @@ -440,11 +475,10 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { SmallVector<StringRef, 10> FunctionNames; StringMap<StringRef> FunctionLineTables; - StringRef FileIndexToStringOffsetTable; - StringRef StringTable; ListScope D(W, "CodeViewLineTables"); { + // FIXME: Add more offset correctness checks. DataExtractor DE(Data, true, 4); uint32_t Offset = 0, Magic = DE.getU32(&Offset); @@ -474,6 +508,9 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { W.printBinaryBlock("Contents", Contents); switch (SubSectionType) { + case COFF::DEBUG_SYMBOL_SUBSECTION: + printCodeViewSymbolsSubsection(Contents, Section, Offset); + break; case COFF::DEBUG_LINE_TABLE_SUBSECTION: { // Holds a PC to file:line table. Some data to parse this subsection is // stored in the other subsections, so just check sanity and store the @@ -502,25 +539,25 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { break; } case COFF::DEBUG_STRING_TABLE_SUBSECTION: - if (PayloadSize == 0 || StringTable.data() != nullptr || + if (PayloadSize == 0 || CVStringTable.data() != nullptr || Contents.back() != '\0') { // Empty or duplicate or non-null-terminated subsection. error(object_error::parse_failed); return; } - StringTable = Contents; + CVStringTable = Contents; break; case COFF::DEBUG_INDEX_SUBSECTION: // Holds the translation table from file indices // to offsets in the string table. if (PayloadSize == 0 || - FileIndexToStringOffsetTable.data() != nullptr) { + CVFileIndexToStringOffsetTable.data() != nullptr) { // Empty or duplicate subsection. error(object_error::parse_failed); return; } - FileIndexToStringOffsetTable = Contents; + CVFileIndexToStringOffsetTable = Contents; break; } Offset += PayloadSize; @@ -555,7 +592,7 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { uint32_t FilenameOffset; { - DataExtractor SDE(FileIndexToStringOffsetTable, true, 4); + DataExtractor SDE(CVFileIndexToStringOffsetTable, true, 4); uint32_t OffsetInSDE = OffsetInIndex; if (!SDE.isValidOffset(OffsetInSDE)) { error(object_error::parse_failed); @@ -564,15 +601,15 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { FilenameOffset = SDE.getU32(&OffsetInSDE); } - if (FilenameOffset == 0 || FilenameOffset + 1 >= StringTable.size() || - StringTable.data()[FilenameOffset - 1] != '\0') { + if (FilenameOffset == 0 || FilenameOffset + 1 >= CVStringTable.size() || + CVStringTable.data()[FilenameOffset - 1] != '\0') { // Each string in an F3 subsection should be preceded by a null // character. error(object_error::parse_failed); return; } - StringRef Filename(StringTable.data() + FilenameOffset); + StringRef Filename(CVStringTable.data() + FilenameOffset); ListScope S(W, "FilenameSegment"); W.printString("Filename", Filename); for (unsigned J = 0; J != SegmentLength && DE.isValidOffset(Offset); @@ -593,6 +630,80 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { } } +void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, + const SectionRef &Section, + uint32_t OffsetInSection) { + if (Subsection.size() == 0) { + error(object_error::parse_failed); + return; + } + DataExtractor DE(Subsection, true, 4); + uint32_t Offset = 0; + + // Function-level subsections have "procedure start" and "procedure end" + // commands that should come in pairs and surround relevant info. + bool InFunctionScope = false; + while (DE.isValidOffset(Offset)) { + // Read subsection segments one by one. + uint16_t Size = DE.getU16(&Offset); + // The section size includes the size of the type identifier. + if (Size < 2 || !DE.isValidOffsetForDataOfSize(Offset, Size)) { + error(object_error::parse_failed); + return; + } + Size -= 2; + uint16_t Type = DE.getU16(&Offset); + switch (Type) { + case COFF::DEBUG_SYMBOL_TYPE_PROC_START: { + DictScope S(W, "ProcStart"); + if (InFunctionScope || Size < 36) { + error(object_error::parse_failed); + return; + } + InFunctionScope = true; + + // We're currently interested in a limited subset of fields in this + // segment, just ignore the rest of the fields for now. + uint8_t Unused[12]; + DE.getU8(&Offset, Unused, 12); + uint32_t CodeSize = DE.getU32(&Offset); + DE.getU8(&Offset, Unused, 12); + StringRef SectionName; + if (error(resolveSymbolName(Obj->getCOFFSection(Section), + OffsetInSection + Offset, SectionName))) + return; + Offset += 4; + DE.getU8(&Offset, Unused, 3); + StringRef DisplayName = DE.getCStr(&Offset); + if (!DE.isValidOffset(Offset)) { + error(object_error::parse_failed); + return; + } + W.printString("DisplayName", DisplayName); + W.printString("Section", SectionName); + W.printHex("CodeSize", CodeSize); + + break; + } + case COFF::DEBUG_SYMBOL_TYPE_PROC_END: { + W.startLine() << "ProcEnd\n"; + if (!InFunctionScope || Size > 0) { + error(object_error::parse_failed); + return; + } + InFunctionScope = false; + break; + } + default: + Offset += Size; + break; + } + } + + if (InFunctionScope) + error(object_error::parse_failed); +} + void COFFDumper::printSections() { ListScope SectionsD(W, "Sections"); int SectionNumber = 0; @@ -628,8 +739,7 @@ void COFFDumper::printSections() { if (opts::SectionSymbols) { ListScope D(W, "Symbols"); for (const SymbolRef &Symbol : Obj->symbols()) { - bool Contained = false; - if (Sec.containsSymbol(Symbol, Contained) || !Contained) + if (!Sec.containsSymbol(Symbol)) continue; printSymbol(Symbol); @@ -639,7 +749,8 @@ void COFFDumper::printSections() { if (Name == ".debug$S" && opts::CodeViewLineTables) printCodeViewLineTables(Sec); - if (opts::SectionData) { + if (opts::SectionData && + !(Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)) { StringRef Data; if (error(Sec.getContents(Data))) break; @@ -683,7 +794,6 @@ void COFFDumper::printRelocation(const SectionRef &Section, uint64_t RelocType; SmallString<32> RelocName; StringRef SymbolName; - StringRef Contents; if (error(Reloc.getOffset(Offset))) return; if (error(Reloc.getType(RelocType))) @@ -691,21 +801,19 @@ void COFFDumper::printRelocation(const SectionRef &Section, if (error(Reloc.getTypeName(RelocName))) return; symbol_iterator Symbol = Reloc.getSymbol(); - if (error(Symbol->getName(SymbolName))) - return; - if (error(Section.getContents(Contents))) + if (Symbol != Obj->symbol_end() && error(Symbol->getName(SymbolName))) return; if (opts::ExpandRelocs) { DictScope Group(W, "Relocation"); W.printHex("Offset", Offset); W.printNumber("Type", RelocName, RelocType); - W.printString("Symbol", SymbolName.size() > 0 ? SymbolName : "-"); + W.printString("Symbol", SymbolName.empty() ? "-" : SymbolName); } else { raw_ostream& OS = W.startLine(); OS << W.hex(Offset) << " " << RelocName - << " " << (SymbolName.size() > 0 ? SymbolName : "-") + << " " << (SymbolName.empty() ? "-" : SymbolName) << "\n"; } } @@ -719,12 +827,30 @@ void COFFDumper::printSymbols() { void COFFDumper::printDynamicSymbols() { ListScope Group(W, "DynamicSymbols"); } +static ErrorOr<StringRef> +getSectionName(const llvm::object::COFFObjectFile *Obj, int32_t SectionNumber, + const coff_section *Section) { + if (Section) { + StringRef SectionName; + if (std::error_code EC = Obj->getSectionName(Section, SectionName)) + return EC; + return SectionName; + } + if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) + return StringRef("IMAGE_SYM_DEBUG"); + if (SectionNumber == llvm::COFF::IMAGE_SYM_ABSOLUTE) + return StringRef("IMAGE_SYM_ABSOLUTE"); + if (SectionNumber == llvm::COFF::IMAGE_SYM_UNDEFINED) + return StringRef("IMAGE_SYM_UNDEFINED"); + return StringRef(""); +} + void COFFDumper::printSymbol(const SymbolRef &Sym) { DictScope D(W, "Symbol"); - const coff_symbol *Symbol = Obj->getCOFFSymbol(Sym); + COFFSymbolRef Symbol = Obj->getCOFFSymbol(Sym); const coff_section *Section; - if (std::error_code EC = Obj->getSection(Symbol->SectionNumber, Section)) { + if (std::error_code EC = Obj->getSection(Symbol.getSectionNumber(), Section)) { W.startLine() << "Invalid section number: " << EC.message() << "\n"; W.flush(); return; @@ -735,23 +861,25 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { SymbolName = ""; StringRef SectionName = ""; - if (Section) - Obj->getSectionName(Section, SectionName); + ErrorOr<StringRef> Res = + getSectionName(Obj, Symbol.getSectionNumber(), Section); + if (Res) + SectionName = *Res; W.printString("Name", SymbolName); - W.printNumber("Value", Symbol->Value); - W.printNumber("Section", SectionName, Symbol->SectionNumber); - W.printEnum ("BaseType", Symbol->getBaseType(), makeArrayRef(ImageSymType)); - W.printEnum ("ComplexType", Symbol->getComplexType(), + W.printNumber("Value", Symbol.getValue()); + W.printNumber("Section", SectionName, Symbol.getSectionNumber()); + W.printEnum ("BaseType", Symbol.getBaseType(), makeArrayRef(ImageSymType)); + W.printEnum ("ComplexType", Symbol.getComplexType(), makeArrayRef(ImageSymDType)); - W.printEnum ("StorageClass", Symbol->StorageClass, + W.printEnum ("StorageClass", Symbol.getStorageClass(), makeArrayRef(ImageSymClass)); - W.printNumber("AuxSymbolCount", Symbol->NumberOfAuxSymbols); + W.printNumber("AuxSymbolCount", Symbol.getNumberOfAuxSymbols()); - for (unsigned I = 0; I < Symbol->NumberOfAuxSymbols; ++I) { - if (Symbol->isFunctionDefinition()) { + for (uint8_t I = 0; I < Symbol.getNumberOfAuxSymbols(); ++I) { + if (Symbol.isFunctionDefinition()) { const coff_aux_function_definition *Aux; - if (error(getSymbolAuxData(Obj, Symbol + I, Aux))) + if (error(getSymbolAuxData(Obj, Symbol, I, Aux))) break; DictScope AS(W, "AuxFunctionDef"); @@ -759,18 +887,16 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { W.printNumber("TotalSize", Aux->TotalSize); W.printHex("PointerToLineNumber", Aux->PointerToLinenumber); W.printHex("PointerToNextFunction", Aux->PointerToNextFunction); - W.printBinary("Unused", makeArrayRef(Aux->Unused)); - } else if (Symbol->isWeakExternal()) { + } else if (Symbol.isAnyUndefined()) { const coff_aux_weak_external *Aux; - if (error(getSymbolAuxData(Obj, Symbol + I, Aux))) + if (error(getSymbolAuxData(Obj, Symbol, I, Aux))) break; - const coff_symbol *Linked; + ErrorOr<COFFSymbolRef> Linked = Obj->getSymbol(Aux->TagIndex); StringRef LinkedName; - std::error_code EC; - if ((EC = Obj->getSymbol(Aux->TagIndex, Linked)) || - (EC = Obj->getSymbolName(Linked, LinkedName))) { + std::error_code EC = Linked.getError(); + if (EC || (EC = Obj->getSymbolName(*Linked, LinkedName))) { LinkedName = ""; error(EC); } @@ -779,56 +905,60 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { W.printNumber("Linked", LinkedName, Aux->TagIndex); W.printEnum ("Search", Aux->Characteristics, makeArrayRef(WeakExternalCharacteristics)); - W.printBinary("Unused", makeArrayRef(Aux->Unused)); - } else if (Symbol->isFileRecord()) { - const coff_aux_file *Aux; - if (error(getSymbolAuxData(Obj, Symbol + I, Aux))) + } else if (Symbol.isFileRecord()) { + const char *FileName; + if (error(getSymbolAuxData(Obj, Symbol, I, FileName))) break; DictScope AS(W, "AuxFileRecord"); - StringRef Name(Aux->FileName, - Symbol->NumberOfAuxSymbols * COFF::SymbolSize); + StringRef Name(FileName, Symbol.getNumberOfAuxSymbols() * + Obj->getSymbolTableEntrySize()); W.printString("FileName", Name.rtrim(StringRef("\0", 1))); break; - } else if (Symbol->isSectionDefinition()) { + } else if (Symbol.isSectionDefinition()) { const coff_aux_section_definition *Aux; - if (error(getSymbolAuxData(Obj, Symbol + I, Aux))) + if (error(getSymbolAuxData(Obj, Symbol, I, Aux))) break; + int32_t AuxNumber = Aux->getNumber(Symbol.isBigObj()); + DictScope AS(W, "AuxSectionDef"); W.printNumber("Length", Aux->Length); W.printNumber("RelocationCount", Aux->NumberOfRelocations); W.printNumber("LineNumberCount", Aux->NumberOfLinenumbers); W.printHex("Checksum", Aux->CheckSum); - W.printNumber("Number", Aux->Number); + W.printNumber("Number", AuxNumber); W.printEnum("Selection", Aux->Selection, makeArrayRef(ImageCOMDATSelect)); - W.printBinary("Unused", makeArrayRef(Aux->Unused)); if (Section && Section->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT && Aux->Selection == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { const coff_section *Assoc; - StringRef AssocName; - std::error_code EC; - if ((EC = Obj->getSection(Aux->Number, Assoc)) || - (EC = Obj->getSectionName(Assoc, AssocName))) { + StringRef AssocName = ""; + std::error_code EC = Obj->getSection(AuxNumber, Assoc); + ErrorOr<StringRef> Res = getSectionName(Obj, AuxNumber, Assoc); + if (Res) + AssocName = *Res; + if (!EC) + EC = Res.getError(); + if (EC) { AssocName = ""; error(EC); } - W.printNumber("AssocSection", AssocName, Aux->Number); + W.printNumber("AssocSection", AssocName, AuxNumber); } - } else if (Symbol->isCLRToken()) { + } else if (Symbol.isCLRToken()) { const coff_aux_clr_token *Aux; - if (error(getSymbolAuxData(Obj, Symbol + I, Aux))) + if (error(getSymbolAuxData(Obj, Symbol, I, Aux))) break; - const coff_symbol *ReferredSym; + ErrorOr<COFFSymbolRef> ReferredSym = + Obj->getSymbol(Aux->SymbolTableIndex); StringRef ReferredName; - std::error_code EC; - if ((EC = Obj->getSymbol(Aux->SymbolTableIndex, ReferredSym)) || - (EC = Obj->getSymbolName(ReferredSym, ReferredName))) { + std::error_code EC = ReferredSym.getError(); + if (EC || (EC = Obj->getSymbolName(*ReferredSym, ReferredName))) { ReferredName = ""; error(EC); } @@ -837,7 +967,6 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { W.printNumber("AuxType", Aux->AuxType); W.printNumber("Reserved", Aux->Reserved); W.printNumber("SymbolTableIndex", ReferredName, Aux->SymbolTableIndex); - W.printBinary("Unused", makeArrayRef(Aux->Unused)); } else { W.startLine() << "<unhandled auxiliary record>\n"; @@ -846,12 +975,8 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { } void COFFDumper::printUnwindInfo() { - const coff_file_header *Header; - if (error(Obj->getCOFFHeader(Header))) - return; - ListScope D(W, "UnwindInformation"); - switch (Header->Machine) { + switch (Obj->getMachine()) { case COFF::IMAGE_FILE_MACHINE_AMD64: { Win64EH::Dumper Dumper(W); Win64EH::Dumper::SymbolResolver @@ -870,9 +995,113 @@ void COFFDumper::printUnwindInfo() { break; } default: - W.printEnum("unsupported Image Machine", Header->Machine, + W.printEnum("unsupported Image Machine", Obj->getMachine(), makeArrayRef(ImageFileMachineType)); break; } } +void COFFDumper::printImportedSymbols( + iterator_range<imported_symbol_iterator> Range) { + for (const ImportedSymbolRef &I : Range) { + StringRef Sym; + if (error(I.getSymbolName(Sym))) return; + uint16_t Ordinal; + if (error(I.getOrdinal(Ordinal))) return; + W.printNumber("Symbol", Sym, Ordinal); + } +} + +void COFFDumper::printDelayImportedSymbols( + const DelayImportDirectoryEntryRef &I, + iterator_range<imported_symbol_iterator> Range) { + int Index = 0; + for (const ImportedSymbolRef &S : Range) { + DictScope Import(W, "Import"); + StringRef Sym; + if (error(S.getSymbolName(Sym))) return; + uint16_t Ordinal; + if (error(S.getOrdinal(Ordinal))) return; + W.printNumber("Symbol", Sym, Ordinal); + uint64_t Addr; + if (error(I.getImportAddress(Index++, Addr))) return; + W.printHex("Address", Addr); + } +} + +void COFFDumper::printCOFFImports() { + // Regular imports + for (const ImportDirectoryEntryRef &I : Obj->import_directories()) { + DictScope Import(W, "Import"); + StringRef Name; + if (error(I.getName(Name))) return; + W.printString("Name", Name); + uint32_t Addr; + if (error(I.getImportLookupTableRVA(Addr))) return; + W.printHex("ImportLookupTableRVA", Addr); + if (error(I.getImportAddressTableRVA(Addr))) return; + W.printHex("ImportAddressTableRVA", Addr); + printImportedSymbols(I.imported_symbols()); + } + + // Delay imports + for (const DelayImportDirectoryEntryRef &I : Obj->delay_import_directories()) { + DictScope Import(W, "DelayImport"); + StringRef Name; + if (error(I.getName(Name))) return; + W.printString("Name", Name); + const delay_import_directory_table_entry *Table; + if (error(I.getDelayImportTable(Table))) return; + W.printHex("Attributes", Table->Attributes); + W.printHex("ModuleHandle", Table->ModuleHandle); + W.printHex("ImportAddressTable", Table->DelayImportAddressTable); + W.printHex("ImportNameTable", Table->DelayImportNameTable); + W.printHex("BoundDelayImportTable", Table->BoundDelayImportTable); + W.printHex("UnloadDelayImportTable", Table->UnloadDelayImportTable); + printDelayImportedSymbols(I, I.imported_symbols()); + } +} + +void COFFDumper::printCOFFDirectives() { + for (const SectionRef &Section : Obj->sections()) { + StringRef Contents; + StringRef Name; + + if (error(Section.getName(Name))) + continue; + if (Name != ".drectve") + continue; + + if (error(Section.getContents(Contents))) + return; + + W.printString("Directive(s)", Contents); + } +} + +static StringRef getBaseRelocTypeName(uint8_t Type) { + switch (Type) { + case COFF::IMAGE_REL_BASED_ABSOLUTE: return "ABSOLUTE"; + case COFF::IMAGE_REL_BASED_HIGH: return "HIGH"; + case COFF::IMAGE_REL_BASED_LOW: return "LOW"; + case COFF::IMAGE_REL_BASED_HIGHLOW: return "HIGHLOW"; + case COFF::IMAGE_REL_BASED_HIGHADJ: return "HIGHADJ"; + case COFF::IMAGE_REL_BASED_DIR64: return "DIR64"; + default: return "unknown (" + llvm::utostr(Type) + ")"; + } +} + +void COFFDumper::printCOFFBaseReloc() { + ListScope D(W, "BaseReloc"); + for (const BaseRelocRef &I : Obj->base_relocs()) { + uint8_t Type; + uint32_t RVA; + if (error(I.getRVA(RVA))) + continue; + if (error(I.getType(Type))) + continue; + DictScope Import(W, "Entry"); + W.printString("Type", getBaseRelocTypeName(Type)); + W.printHex("Address", RVA); + } +} diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index 5df51e2..d68c786 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -407,6 +407,7 @@ static const char *getElfSectionType(unsigned Arch, unsigned Type) { switch (Type) { LLVM_READOBJ_ENUM_CASE(ELF, SHT_MIPS_REGINFO); LLVM_READOBJ_ENUM_CASE(ELF, SHT_MIPS_OPTIONS); + LLVM_READOBJ_ENUM_CASE(ELF, SHT_MIPS_ABIFLAGS); } } @@ -603,7 +604,7 @@ void ELFDumper<ELFT>::printSections() { } } - if (opts::SectionData) { + if (opts::SectionData && Section->sh_type != ELF::SHT_NOBITS) { ArrayRef<uint8_t> Data = errorOrDefault(Obj->getSectionContents(Section)); W.printBinaryBlock("SectionData", StringRef((const char *)Data.data(), Data.size())); @@ -675,7 +676,8 @@ void ELFDumper<ELFT>::printRelocation(const Elf_Shdr *Sec, DictScope Group(W, "Relocation"); W.printHex("Offset", Rel.r_offset); W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL())); - W.printString("Symbol", SymbolName.size() > 0 ? SymbolName : "-"); + W.printNumber("Symbol", SymbolName.size() > 0 ? SymbolName : "-", + Rel.getSymbol(Obj->isMips64EL())); W.printHex("Addend", Rel.r_addend); } else { raw_ostream& OS = W.startLine(); diff --git a/tools/llvm-readobj/Error.cpp b/tools/llvm-readobj/Error.cpp index a078f5c..7e6f780 100644 --- a/tools/llvm-readobj/Error.cpp +++ b/tools/llvm-readobj/Error.cpp @@ -24,7 +24,7 @@ public: }; } // namespace -const char *_readobj_error_category::name() const { +const char *_readobj_error_category::name() const LLVM_NOEXCEPT { return "llvm.readobj"; } diff --git a/tools/llvm-readobj/Error.h b/tools/llvm-readobj/Error.h index 81ce408..f3e24bb 100644 --- a/tools/llvm-readobj/Error.h +++ b/tools/llvm-readobj/Error.h @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_READOBJ_ERROR_H -#define LLVM_READOBJ_ERROR_H +#ifndef LLVM_TOOLS_LLVM_READOBJ_ERROR_H +#define LLVM_TOOLS_LLVM_READOBJ_ERROR_H #include <system_error> diff --git a/tools/llvm-readobj/MachODumper.cpp b/tools/llvm-readobj/MachODumper.cpp index a5e5cf8..7e8fdad 100644 --- a/tools/llvm-readobj/MachODumper.cpp +++ b/tools/llvm-readobj/MachODumper.cpp @@ -31,14 +31,17 @@ public: : ObjDumper(Writer) , Obj(Obj) { } - virtual void printFileHeaders() override; - virtual void printSections() override; - virtual void printRelocations() override; - virtual void printSymbols() override; - virtual void printDynamicSymbols() override; - virtual void printUnwindInfo() override; + void printFileHeaders() override; + void printSections() override; + void printRelocations() override; + void printSymbols() override; + void printDynamicSymbols() override; + void printUnwindInfo() override; private: + template<class MachHeader> + void printFileHeaders(const MachHeader &Header); + void printSymbol(const SymbolRef &Symbol); void printRelocation(const RelocationRef &Reloc); @@ -68,6 +71,137 @@ std::error_code createMachODumper(const object::ObjectFile *Obj, } // namespace llvm +static const EnumEntry<uint32_t> MachOMagics[] = { + { "Magic", MachO::MH_MAGIC }, + { "Cigam", MachO::MH_CIGAM }, + { "Magic64", MachO::MH_MAGIC_64 }, + { "Cigam64", MachO::MH_CIGAM_64 }, + { "FatMagic", MachO::FAT_MAGIC }, + { "FatCigam", MachO::FAT_CIGAM }, +}; + +static const EnumEntry<uint32_t> MachOHeaderFileTypes[] = { + { "Relocatable", MachO::MH_OBJECT }, + { "Executable", MachO::MH_EXECUTE }, + { "FixedVMLibrary", MachO::MH_FVMLIB }, + { "Core", MachO::MH_CORE }, + { "PreloadedExecutable", MachO::MH_PRELOAD }, + { "DynamicLibrary", MachO::MH_DYLIB }, + { "DynamicLinker", MachO::MH_DYLINKER }, + { "Bundle", MachO::MH_BUNDLE }, + { "DynamicLibraryStub", MachO::MH_DYLIB_STUB }, + { "DWARFSymbol", MachO::MH_DSYM }, + { "KextBundle", MachO::MH_KEXT_BUNDLE }, +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuTypes[] = { + { "Any" , static_cast<uint32_t>(MachO::CPU_TYPE_ANY) }, + { "X86" , MachO::CPU_TYPE_X86 }, + { "X86-64" , MachO::CPU_TYPE_X86_64 }, + { "Mc98000" , MachO::CPU_TYPE_MC98000 }, + { "Arm" , MachO::CPU_TYPE_ARM }, + { "Arm64" , MachO::CPU_TYPE_ARM64 }, + { "Sparc" , MachO::CPU_TYPE_SPARC }, + { "PowerPC" , MachO::CPU_TYPE_POWERPC }, + { "PowerPC64" , MachO::CPU_TYPE_POWERPC64 }, +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesX86[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_I386_ALL), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_386), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_486), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_486SX), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_586), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTPRO), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTII_M3), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTII_M5), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_CELERON), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_CELERON_MOBILE), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_3), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_3_M), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_3_XEON), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_M), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_4), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_PENTIUM_4_M), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ITANIUM), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ITANIUM_2), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_XEON), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_XEON_MP), +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesX64[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_X86_64_ALL), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_X86_ARCH1), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_X86_64_H), +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesARM[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_ALL), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V4T), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V6), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V5), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V5TEJ), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_XSCALE), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7S), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7K), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V6M), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7M), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM_V7EM), +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesARM64[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_ARM64_ALL), +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesSPARC[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_SPARC_ALL), +}; + +static const EnumEntry<uint32_t> MachOHeaderCpuSubtypesPPC[] = { + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_ALL), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_601), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_602), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_603), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_603e), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_603ev), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_604), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_604e), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_620), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_750), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_7400), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_7450), + LLVM_READOBJ_ENUM_ENT(MachO, CPU_SUBTYPE_POWERPC_970), +}; + +static const EnumEntry<uint32_t> MachOHeaderFlags[] = { + LLVM_READOBJ_ENUM_ENT(MachO, MH_NOUNDEFS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_INCRLINK), + LLVM_READOBJ_ENUM_ENT(MachO, MH_DYLDLINK), + LLVM_READOBJ_ENUM_ENT(MachO, MH_BINDATLOAD), + LLVM_READOBJ_ENUM_ENT(MachO, MH_PREBOUND), + LLVM_READOBJ_ENUM_ENT(MachO, MH_SPLIT_SEGS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_LAZY_INIT), + LLVM_READOBJ_ENUM_ENT(MachO, MH_TWOLEVEL), + LLVM_READOBJ_ENUM_ENT(MachO, MH_FORCE_FLAT), + LLVM_READOBJ_ENUM_ENT(MachO, MH_NOMULTIDEFS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_NOFIXPREBINDING), + LLVM_READOBJ_ENUM_ENT(MachO, MH_PREBINDABLE), + LLVM_READOBJ_ENUM_ENT(MachO, MH_ALLMODSBOUND), + LLVM_READOBJ_ENUM_ENT(MachO, MH_SUBSECTIONS_VIA_SYMBOLS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_CANONICAL), + LLVM_READOBJ_ENUM_ENT(MachO, MH_WEAK_DEFINES), + LLVM_READOBJ_ENUM_ENT(MachO, MH_BINDS_TO_WEAK), + LLVM_READOBJ_ENUM_ENT(MachO, MH_ALLOW_STACK_EXECUTION), + LLVM_READOBJ_ENUM_ENT(MachO, MH_ROOT_SAFE), + LLVM_READOBJ_ENUM_ENT(MachO, MH_SETUID_SAFE), + LLVM_READOBJ_ENUM_ENT(MachO, MH_NO_REEXPORTED_DYLIBS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_PIE), + LLVM_READOBJ_ENUM_ENT(MachO, MH_DEAD_STRIPPABLE_DYLIB), + LLVM_READOBJ_ENUM_ENT(MachO, MH_HAS_TLV_DESCRIPTORS), + LLVM_READOBJ_ENUM_ENT(MachO, MH_NO_HEAP_EXECUTION), + LLVM_READOBJ_ENUM_ENT(MachO, MH_APP_EXTENSION_SAFE), +}; static const EnumEntry<unsigned> MachOSectionTypes[] = { { "Regular" , 0x00 }, @@ -205,7 +339,47 @@ static void getSymbol(const MachOObjectFile *Obj, } void MachODumper::printFileHeaders() { - W.startLine() << "FileHeaders not implemented.\n"; + DictScope H(W, "MachHeader"); + if (!Obj->is64Bit()) { + printFileHeaders(Obj->getHeader()); + } else { + printFileHeaders(Obj->getHeader64()); + W.printHex("Reserved", Obj->getHeader64().reserved); + } +} + +template<class MachHeader> +void MachODumper::printFileHeaders(const MachHeader &Header) { + W.printEnum("Magic", Header.magic, makeArrayRef(MachOMagics)); + W.printEnum("CpuType", Header.cputype, makeArrayRef(MachOHeaderCpuTypes)); + uint32_t subtype = Header.cpusubtype & ~MachO::CPU_SUBTYPE_MASK; + switch (Header.cputype) { + case MachO::CPU_TYPE_X86: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesX86)); + break; + case MachO::CPU_TYPE_X86_64: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesX64)); + break; + case MachO::CPU_TYPE_ARM: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesARM)); + break; + case MachO::CPU_TYPE_POWERPC: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesPPC)); + break; + case MachO::CPU_TYPE_SPARC: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesSPARC)); + break; + case MachO::CPU_TYPE_ARM64: + W.printEnum("CpuSubType", subtype, makeArrayRef(MachOHeaderCpuSubtypesARM64)); + break; + case MachO::CPU_TYPE_POWERPC64: + default: + W.printHex("CpuSubtype", subtype); + } + W.printEnum("FileType", Header.filetype, makeArrayRef(MachOHeaderFileTypes)); + W.printNumber("NumOfLoadCommands", Header.ncmds); + W.printNumber("SizeOfLoadCommands", Header.sizeofcmds); + W.printFlags("Flags", Header.flags, makeArrayRef(MachOHeaderFlags)); } void MachODumper::printSections() { @@ -257,8 +431,7 @@ void MachODumper::printSections(const MachOObjectFile *Obj) { if (opts::SectionSymbols) { ListScope D(W, "Symbols"); for (const SymbolRef &Symbol : Obj->symbols()) { - bool Contained = false; - if (Section.containsSymbol(Symbol, Contained) || !Contained) + if (!Section.containsSymbol(Symbol)) continue; printSymbol(Symbol); @@ -266,11 +439,14 @@ void MachODumper::printSections(const MachOObjectFile *Obj) { } if (opts::SectionData) { - StringRef Data; - if (error(Section.getContents(Data))) - break; + bool IsBSS = Section.isBSS(); + if (!IsBSS) { + StringRef Data; + if (error(Section.getContents(Data))) + break; - W.printBinaryBlock("SectionData", Data); + W.printBinaryBlock("SectionData", Data); + } } } } diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index f80a28b..a34e091 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_READOBJ_OBJDUMPER_H -#define LLVM_READOBJ_OBJDUMPER_H +#ifndef LLVM_TOOLS_LLVM_READOBJ_OBJDUMPER_H +#define LLVM_TOOLS_LLVM_READOBJ_OBJDUMPER_H #include <memory> #include <system_error> @@ -43,6 +43,11 @@ public: // Only implemented for MIPS ELF at this time. virtual void printMipsPLTGOT() { } + // Only implemented for PE/COFF. + virtual void printCOFFImports() { } + virtual void printCOFFDirectives() { } + virtual void printCOFFBaseReloc() { } + protected: StreamWriter& W; }; diff --git a/tools/llvm-readobj/StreamWriter.h b/tools/llvm-readobj/StreamWriter.h index 04b38fb..2fc53ee 100644 --- a/tools/llvm-readobj/StreamWriter.h +++ b/tools/llvm-readobj/StreamWriter.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_READOBJ_STREAMWRITER_H -#define LLVM_READOBJ_STREAMWRITER_H +#ifndef LLVM_TOOLS_LLVM_READOBJ_STREAMWRITER_H +#define LLVM_TOOLS_LLVM_READOBJ_STREAMWRITER_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" @@ -214,8 +214,8 @@ public: } void printBinary(StringRef Label, StringRef Str, ArrayRef<char> Value) { - ArrayRef<uint8_t> V(reinterpret_cast<const uint8_t*>(Value.data()), - Value.size()); + auto V = makeArrayRef(reinterpret_cast<const uint8_t*>(Value.data()), + Value.size()); printBinaryImpl(Label, Str, V, false); } @@ -224,20 +224,20 @@ public: } void printBinary(StringRef Label, ArrayRef<char> Value) { - ArrayRef<uint8_t> V(reinterpret_cast<const uint8_t*>(Value.data()), - Value.size()); + auto V = makeArrayRef(reinterpret_cast<const uint8_t*>(Value.data()), + Value.size()); printBinaryImpl(Label, StringRef(), V, false); } void printBinary(StringRef Label, StringRef Value) { - ArrayRef<uint8_t> V(reinterpret_cast<const uint8_t*>(Value.data()), - Value.size()); + auto V = makeArrayRef(reinterpret_cast<const uint8_t*>(Value.data()), + Value.size()); printBinaryImpl(Label, StringRef(), V, false); } void printBinaryBlock(StringRef Label, StringRef Value) { - ArrayRef<uint8_t> V(reinterpret_cast<const uint8_t*>(Value.data()), - Value.size()); + auto V = makeArrayRef(reinterpret_cast<const uint8_t*>(Value.data()), + Value.size()); printBinaryImpl(Label, StringRef(), V, true); } diff --git a/tools/llvm-readobj/Win64EHDumper.h b/tools/llvm-readobj/Win64EHDumper.h index 9ce4d39..a80df9c 100644 --- a/tools/llvm-readobj/Win64EHDumper.h +++ b/tools/llvm-readobj/Win64EHDumper.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_READOBJ_WIN64EHPRINTER_H -#define LLVM_TOOLS_READOBJ_WIN64EHPRINTER_H +#ifndef LLVM_TOOLS_LLVM_READOBJ_WIN64EHDUMPER_H +#define LLVM_TOOLS_LLVM_READOBJ_WIN64EHDUMPER_H #include "StreamWriter.h" #include "llvm/Support/Win64EH.h" diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index 8d2a997..d08f186 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -24,6 +24,7 @@ #include "ObjDumper.h" #include "StreamWriter.h" #include "llvm/Object/Archive.h" +#include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" @@ -140,6 +141,20 @@ namespace opts { cl::opt<bool> MipsPLTGOT("mips-plt-got", cl::desc("Display the MIPS GOT and PLT GOT sections")); + + // -coff-imports + cl::opt<bool> + COFFImports("coff-imports", cl::desc("Display the PE/COFF import table")); + + // -coff-directives + cl::opt<bool> + COFFDirectives("coff-directives", + cl::desc("Display the PE/COFF .drectve section")); + + // -coff-basereloc + cl::opt<bool> + COFFBaseRelocs("coff-basereloc", + cl::desc("Display the PE/COFF .reloc section")); } // namespace opts static int ReturnValue = EXIT_SUCCESS; @@ -158,8 +173,8 @@ bool error(std::error_code EC) { bool relocAddressLess(RelocationRef a, RelocationRef b) { uint64_t a_addr, b_addr; - if (error(a.getOffset(a_addr))) return false; - if (error(b.getOffset(b_addr))) return false; + if (error(a.getOffset(a_addr))) exit(ReturnValue); + if (error(b.getOffset(b_addr))) exit(ReturnValue); return a_addr < b_addr; } @@ -210,6 +225,17 @@ static std::error_code createDumper(const ObjectFile *Obj, StreamWriter &Writer, return readobj_error::unsupported_obj_file_format; } +static StringRef getLoadName(const ObjectFile *Obj) { + if (auto *ELF = dyn_cast<ELF32LEObjectFile>(Obj)) + return ELF->getLoadName(); + if (auto *ELF = dyn_cast<ELF64LEObjectFile>(Obj)) + return ELF->getLoadName(); + if (auto *ELF = dyn_cast<ELF32BEObjectFile>(Obj)) + return ELF->getLoadName(); + if (auto *ELF = dyn_cast<ELF64BEObjectFile>(Obj)) + return ELF->getLoadName(); + llvm_unreachable("Not ELF"); +} /// @brief Dumps the specified object file. static void dumpObject(const ObjectFile *Obj) { @@ -228,7 +254,7 @@ static void dumpObject(const ObjectFile *Obj) { << "\n"; outs() << "AddressSize: " << (8*Obj->getBytesInAddress()) << "bit\n"; if (Obj->isELF()) - outs() << "LoadName: " << Obj->getLoadName() << "\n"; + outs() << "LoadName: " << getLoadName(Obj) << "\n"; if (opts::FileHeaders) Dumper->printFileHeaders(); @@ -254,6 +280,12 @@ static void dumpObject(const ObjectFile *Obj) { if (isMipsArch(Obj->getArch()) && Obj->isELF()) if (opts::MipsPLTGOT) Dumper->printMipsPLTGOT(); + if (opts::COFFImports) + Dumper->printCOFFImports(); + if (opts::COFFDirectives) + Dumper->printCOFFDirectives(); + if (opts::COFFBaseRelocs) + Dumper->printCOFFBaseReloc(); } @@ -287,16 +319,16 @@ static void dumpInput(StringRef File) { } // Attempt to open the binary. - ErrorOr<Binary *> BinaryOrErr = createBinary(File); + ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File); if (std::error_code EC = BinaryOrErr.getError()) { reportError(File, EC); return; } - std::unique_ptr<Binary> Binary(BinaryOrErr.get()); + Binary &Binary = *BinaryOrErr.get().getBinary(); - if (Archive *Arc = dyn_cast<Archive>(Binary.get())) + if (Archive *Arc = dyn_cast<Archive>(&Binary)) dumpArchive(Arc); - else if (ObjectFile *Obj = dyn_cast<ObjectFile>(Binary.get())) + else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary)) dumpObject(Obj); else reportError(File, readobj_error::unrecognized_file_format); diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 0413948..1c33417 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/tools/llvm-readobj/llvm-readobj.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_READ_OBJ_H -#define LLVM_TOOLS_READ_OBJ_H +#ifndef LLVM_TOOLS_LLVM_READOBJ_LLVM_READOBJ_H +#define LLVM_TOOLS_LLVM_READOBJ_LLVM_READOBJ_H #include "llvm/Support/CommandLine.h" #include <string> diff --git a/tools/llvm-rtdyld/Android.mk b/tools/llvm-rtdyld/Android.mk index 6f902d3..4f4fb4c 100644 --- a/tools/llvm-rtdyld/Android.mk +++ b/tools/llvm-rtdyld/Android.mk @@ -39,6 +39,7 @@ llvm_rtdyld_STATIC_LIBRARIES := \ libLLVMX86Disassembler \ libLLVMDebugInfo \ libLLVMExecutionEngine \ + libLLVMCodeGen \ libLLVMObject \ libLLVMMC \ libLLVMMCParser \ @@ -46,6 +47,7 @@ llvm_rtdyld_STATIC_LIBRARIES := \ libLLVMBitReader \ libLLVMCore \ libLLVMSupport \ + libLLVMMCDisassembler \ include $(CLEAR_VARS) diff --git a/tools/llvm-rtdyld/LLVMBuild.txt b/tools/llvm-rtdyld/LLVMBuild.txt index b36d13c..c4ed49b 100644 --- a/tools/llvm-rtdyld/LLVMBuild.txt +++ b/tools/llvm-rtdyld/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-rtdyld parent = Tools -required_libraries = JIT MC Object RuntimeDyld Support all-targets +required_libraries = MC Object RuntimeDyld Support all-targets diff --git a/tools/llvm-rtdyld/Makefile b/tools/llvm-rtdyld/Makefile index fabdd68..9de753e 100644 --- a/tools/llvm-rtdyld/Makefile +++ b/tools/llvm-rtdyld/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-rtdyld -LINK_COMPONENTS := all-targets support MC object RuntimeDyld JIT debuginfo +LINK_COMPONENTS := all-targets support MC object RuntimeDyld MCJIT debuginfo # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp index 45734f4..87d381e 100644 --- a/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -34,6 +34,7 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" +#include <list> #include <system_error> using namespace llvm; @@ -78,6 +79,31 @@ CheckFiles("check", cl::desc("File containing RuntimeDyld verifier checks."), cl::ZeroOrMore); +static cl::opt<uint64_t> +TargetAddrStart("target-addr-start", + cl::desc("For -verify only: start of phony target address " + "range."), + cl::init(4096), // Start at "page 1" - no allocating at "null". + cl::Hidden); + +static cl::opt<uint64_t> +TargetAddrEnd("target-addr-end", + cl::desc("For -verify only: end of phony target address range."), + cl::init(~0ULL), + cl::Hidden); + +static cl::opt<uint64_t> +TargetSectionSep("target-section-sep", + cl::desc("For -verify only: Separation between sections in " + "phony target address space."), + cl::init(0), + cl::Hidden); + +static cl::list<std::string> +SpecificSectionMappings("map-section", + cl::desc("Map a section to a specific address."), + cl::ZeroOrMore); + /* *** */ // A trivial memory manager that doesn't do anything fancy, just uses the @@ -183,8 +209,8 @@ static int printLineInfoForInput() { std::unique_ptr<ObjectImage> LoadedObject; // Load the object file - LoadedObject.reset( - Dyld.loadObject(new ObjectBuffer(InputBuffer.get().release()))); + LoadedObject = Dyld.loadObject( + llvm::make_unique<ObjectBuffer>(std::move(*InputBuffer))); if (!LoadedObject) { return Error(Dyld.getErrorString()); } @@ -193,7 +219,7 @@ static int printLineInfoForInput() { Dyld.resolveRelocations(); std::unique_ptr<DIContext> Context( - DIContext::getDWARFContext(LoadedObject->getObjectFile())); + DIContext::getDWARFContext(*LoadedObject->getObjectFile())); // Use symbol info to iterate functions in the object. for (object::symbol_iterator I = LoadedObject->begin_symbols(), @@ -244,8 +270,8 @@ static int executeInput() { return Error("unable to read input: '" + EC.message() + "'"); std::unique_ptr<ObjectImage> LoadedObject; // Load the object file - LoadedObject.reset( - Dyld.loadObject(new ObjectBuffer(InputBuffer.get().release()))); + LoadedObject = Dyld.loadObject( + llvm::make_unique<ObjectBuffer>(std::move(*InputBuffer))); if (!LoadedObject) { return Error(Dyld.getErrorString()); } @@ -300,6 +326,134 @@ static int checkAllExpressions(RuntimeDyldChecker &Checker) { return 0; } +std::map<void*, uint64_t> +applySpecificSectionMappings(RuntimeDyldChecker &Checker) { + + std::map<void*, uint64_t> SpecificMappings; + + for (StringRef Mapping : SpecificSectionMappings) { + + size_t EqualsIdx = Mapping.find_first_of("="); + StringRef SectionIDStr = Mapping.substr(0, EqualsIdx); + size_t ComaIdx = Mapping.find_first_of(","); + + if (ComaIdx == StringRef::npos) { + errs() << "Invalid section specification '" << Mapping + << "'. Should be '<file name>,<section name>=<addr>'\n"; + exit(1); + } + + StringRef FileName = SectionIDStr.substr(0, ComaIdx); + StringRef SectionName = SectionIDStr.substr(ComaIdx + 1); + + uint64_t OldAddrInt; + std::string ErrorMsg; + std::tie(OldAddrInt, ErrorMsg) = + Checker.getSectionAddr(FileName, SectionName, true); + + if (ErrorMsg != "") { + errs() << ErrorMsg; + exit(1); + } + + void* OldAddr = reinterpret_cast<void*>(static_cast<uintptr_t>(OldAddrInt)); + + StringRef NewAddrStr = Mapping.substr(EqualsIdx + 1); + uint64_t NewAddr; + + if (NewAddrStr.getAsInteger(0, NewAddr)) { + errs() << "Invalid section address in mapping: " << Mapping << "\n"; + exit(1); + } + + Checker.getRTDyld().mapSectionAddress(OldAddr, NewAddr); + SpecificMappings[OldAddr] = NewAddr; + } + + return SpecificMappings; +} + +// Scatter sections in all directions! +// Remaps section addresses for -verify mode. The following command line options +// can be used to customize the layout of the memory within the phony target's +// address space: +// -target-addr-start <s> -- Specify where the phony target addres range starts. +// -target-addr-end <e> -- Specify where the phony target address range ends. +// -target-section-sep <d> -- Specify how big a gap should be left between the +// end of one section and the start of the next. +// Defaults to zero. Set to something big +// (e.g. 1 << 32) to stress-test stubs, GOTs, etc. +// +void remapSections(const llvm::Triple &TargetTriple, + const TrivialMemoryManager &MemMgr, + RuntimeDyldChecker &Checker) { + + // Set up a work list (section addr/size pairs). + typedef std::list<std::pair<void*, uint64_t>> WorklistT; + WorklistT Worklist; + + for (const auto& CodeSection : MemMgr.FunctionMemory) + Worklist.push_back(std::make_pair(CodeSection.base(), CodeSection.size())); + for (const auto& DataSection : MemMgr.DataMemory) + Worklist.push_back(std::make_pair(DataSection.base(), DataSection.size())); + + // Apply any section-specific mappings that were requested on the command + // line. + typedef std::map<void*, uint64_t> AppliedMappingsT; + AppliedMappingsT AppliedMappings = applySpecificSectionMappings(Checker); + + // Keep an "already allocated" mapping of section target addresses to sizes. + // Sections whose address mappings aren't specified on the command line will + // allocated around the explicitly mapped sections while maintaining the + // minimum separation. + std::map<uint64_t, uint64_t> AlreadyAllocated; + + // Move the previously applied mappings into the already-allocated map. + for (WorklistT::iterator I = Worklist.begin(), E = Worklist.end(); + I != E;) { + WorklistT::iterator Tmp = I; + ++I; + AppliedMappingsT::iterator AI = AppliedMappings.find(Tmp->first); + + if (AI != AppliedMappings.end()) { + AlreadyAllocated[AI->second] = Tmp->second; + Worklist.erase(Tmp); + } + } + + // If the -target-addr-end option wasn't explicitly passed, then set it to a + // sensible default based on the target triple. + if (TargetAddrEnd.getNumOccurrences() == 0) { + if (TargetTriple.isArch16Bit()) + TargetAddrEnd = (1ULL << 16) - 1; + else if (TargetTriple.isArch32Bit()) + TargetAddrEnd = (1ULL << 32) - 1; + // TargetAddrEnd already has a sensible default for 64-bit systems, so + // there's nothing to do in the 64-bit case. + } + + // Process any elements remaining in the worklist. + while (!Worklist.empty()) { + std::pair<void*, uint64_t> CurEntry = Worklist.front(); + Worklist.pop_front(); + + uint64_t NextSectionAddr = TargetAddrStart; + + for (const auto &Alloc : AlreadyAllocated) + if (NextSectionAddr + CurEntry.second + TargetSectionSep <= Alloc.first) + break; + else + NextSectionAddr = Alloc.first + Alloc.second + TargetSectionSep; + + AlreadyAllocated[NextSectionAddr] = CurEntry.second; + Checker.getRTDyld().mapSectionAddress(CurEntry.first, NextSectionAddr); + } + +} + +// Load and link the objects specified on the command line, but do not execute +// anything. Instead, attach a RuntimeDyldChecker instance and call it to +// verify the correctness of the linked memory. static int linkAndVerify() { // Check for missing triple. @@ -347,6 +501,9 @@ static int linkAndVerify() { // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; RuntimeDyld Dyld(&MemMgr); + Dyld.setProcessAllSections(true); + RuntimeDyldChecker Checker(Dyld, Disassembler.get(), InstPrinter.get(), + llvm::dbgs()); // If we don't have any input files, read from stdin. if (!InputFileList.size()) @@ -360,19 +517,30 @@ static int linkAndVerify() { std::unique_ptr<ObjectImage> LoadedObject; // Load the object file - LoadedObject.reset( - Dyld.loadObject(new ObjectBuffer(InputBuffer.get().release()))); + LoadedObject = Dyld.loadObject( + llvm::make_unique<ObjectBuffer>(std::move(*InputBuffer))); if (!LoadedObject) { return Error(Dyld.getErrorString()); } } + // Re-map the section addresses into the phony target address space. + remapSections(TheTriple, MemMgr, Checker); + // Resolve all the relocations we can. Dyld.resolveRelocations(); - RuntimeDyldChecker Checker(Dyld, Disassembler.get(), InstPrinter.get(), - llvm::dbgs()); - return checkAllExpressions(Checker); + // Register EH frames. + Dyld.registerEHFrames(); + + int ErrorCode = checkAllExpressions(Checker); + if (Dyld.hasError()) { + errs() << "RTDyld reported an error applying relocations:\n " + << Dyld.getErrorString() << "\n"; + ErrorCode = 1; + } + + return ErrorCode; } int main(int argc, char **argv) { diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt new file mode 100644 index 0000000..100c184 --- /dev/null +++ b/tools/llvm-shlib/CMakeLists.txt @@ -0,0 +1,100 @@ +# This tool creates a shared library from the LLVM libraries. Generating this +# library is enabled by setting LLVM_BUILD_LLVM_DYLIB=yes on the CMake +# commandline. By default the shared library only exports the LLVM C API. + + +# You can configure which libraries from LLVM you want to include in the shared +# library by setting LLVM_DYLIB_COMPONENTS to a semi-colon delimited list of +# LLVM components. All compoenent names handled by llvm-config are valid. + +if(NOT DEFINED LLVM_DYLIB_COMPONENTS) + set(LLVM_DYLIB_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Analysis + BitReader + BitWriter + CodeGen + Core + ExecutionEngine + IPA + IPO + IRReader + InstCombine + Instrumentation + Interpreter + Linker + MCDisassembler + MCJIT + ObjCARCOpts + Object + ScalarOpts + Support + Target + TransformUtils + Vectorize + native + ) +endif() + +add_definitions( -DLLVM_VERSION_INFO=\"${PACKAGE_VERSION}\" ) + +set(SOURCES + libllvm.cpp + ) + +if(NOT DEFINED LLVM_EXPORTED_SYMBOL_FILE) + + if( WIN32 AND NOT CYGWIN ) + message(FATAL_ERROR "Auto-generation not implemented for Win32 without GNU utils. Please specify LLVM_EXPORTED_SYMBOL_FILE.") + endif() + + # To get the export list for a single llvm library: + # nm ${LIB_PATH} | awk "/T _LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_PATH}.exports + + set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/libllvm.exports) + + llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) + + foreach (lib ${LIB_NAMES}) + + set(LIB_DIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib) + set(LIB_NAME ${LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${lib}) + set(LIB_PATH ${LIB_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(LIB_EXPORTS_PATH ${LIB_NAME}.exports) + + list(APPEND LLVM_DYLIB_REQUIRED_EXPORTS ${LIB_EXPORTS_PATH}) + + add_custom_command(OUTPUT ${LIB_EXPORTS_PATH} + COMMAND nm ${LIB_PATH} | awk "/T _LLVM/ || /T LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_EXPORTS_PATH} + WORKING_DIRECTORY ${LIB_DIR} + DEPENDS ${lib} + COMMENT "Generating Export list for ${lib}..." + VERBATIM ) + endforeach () + + add_custom_command(OUTPUT ${LLVM_EXPORTED_SYMBOL_FILE} + COMMAND cat ${LLVM_DYLIB_REQUIRED_EXPORTS} > ${LLVM_EXPORTED_SYMBOL_FILE} + WORKING_DIRECTORY ${LIB_DIR} + DEPENDS ${LLVM_DYLIB_REQUIRED_EXPORTS} + COMMENT "Generating combined export list...") + +endif() + +add_llvm_library(LLVM SHARED ${SOURCES}) + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") # FIXME: It should be "GNU ld for elf" + # GNU ld doesn't resolve symbols in the version script. + list(REMOVE_DUPLICATES LIB_NAMES) + set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES} -Wl,--no-whole-archive) +endif() + +target_link_libraries(LLVM ${cmake_2_8_12_PRIVATE} ${LIB_NAMES}) + +add_dependencies(LLVM ${LLVM_EXPORTED_SYMBOL_FILE}) + +if (APPLE) + set_property(TARGET LLVM APPEND_STRING PROPERTY + LINK_FLAGS + " -compatibility_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR} -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}") +endif() + diff --git a/tools/llvm-shlib/libllvm.cpp b/tools/llvm-shlib/libllvm.cpp new file mode 100644 index 0000000..40b4f66 --- /dev/null +++ b/tools/llvm-shlib/libllvm.cpp @@ -0,0 +1,13 @@ +//===-libllvm.cpp - LLVM Shared Library -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is empty and serves only the purpose of making CMake happy because +// you can't define a target with no sources. +// +//===----------------------------------------------------------------------===// diff --git a/tools/llvm-size/llvm-size.cpp b/tools/llvm-size/llvm-size.cpp index 50b5220..59a5f20 100644 --- a/tools/llvm-size/llvm-size.cpp +++ b/tools/llvm-size/llvm-size.cpp @@ -297,17 +297,13 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) { std::size_t max_size_len = strlen("size"); std::size_t max_addr_len = strlen("addr"); for (const SectionRef &Section : Obj->sections()) { - uint64_t size = 0; - if (error(Section.getSize(size))) - return; + uint64_t size = Section.getSize(); total += size; StringRef name; - uint64_t addr = 0; if (error(Section.getName(name))) return; - if (error(Section.getAddress(addr))) - return; + uint64_t addr = Section.getAddress(); max_name_len = std::max(max_name_len, name.size()); max_size_len = std::max(max_size_len, getNumLengthAsString(size)); max_addr_len = std::max(max_addr_len, getNumLengthAsString(addr)); @@ -337,14 +333,10 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) { // Print each section. for (const SectionRef &Section : Obj->sections()) { StringRef name; - uint64_t size = 0; - uint64_t addr = 0; if (error(Section.getName(name))) return; - if (error(Section.getSize(size))) - return; - if (error(Section.getAddress(addr))) - return; + uint64_t size = Section.getSize(); + uint64_t addr = Section.getAddress(); std::string namestr = name; outs() << format(fmt.str().c_str(), namestr.c_str(), size, addr); @@ -365,18 +357,10 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) { // Make one pass over the section table to calculate sizes. for (const SectionRef &Section : Obj->sections()) { - uint64_t size = 0; - bool isText = false; - bool isData = false; - bool isBSS = false; - if (error(Section.getSize(size))) - return; - if (error(Section.isText(isText))) - return; - if (error(Section.isData(isData))) - return; - if (error(Section.isBSS(isBSS))) - return; + uint64_t size = Section.getSize(); + bool isText = Section.isText(); + bool isData = Section.isData(); + bool isBSS = Section.isBSS(); if (isText) total_text += size; else if (isData) @@ -444,8 +428,7 @@ static bool checkMachOAndArchFlags(ObjectFile *o, StringRef file) { static void PrintFileSectionSizes(StringRef file) { // If file is not stdin, check that it exists. if (file != "-") { - bool exists; - if (sys::fs::exists(file, exists) || !exists) { + if (!sys::fs::exists(file)) { errs() << ToolName << ": '" << file << "': " << "No such file\n"; return; @@ -453,14 +436,14 @@ static void PrintFileSectionSizes(StringRef file) { } // Attempt to open the binary. - ErrorOr<Binary *> BinaryOrErr = createBinary(file); + ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(file); if (std::error_code EC = BinaryOrErr.getError()) { errs() << ToolName << ": " << file << ": " << EC.message() << ".\n"; return; } - std::unique_ptr<Binary> binary(BinaryOrErr.get()); + Binary &Bin = *BinaryOrErr.get().getBinary(); - if (Archive *a = dyn_cast<Archive>(binary.get())) { + if (Archive *a = dyn_cast<Archive>(&Bin)) { // This is an archive. Iterate over each member and display its sizes. for (object::Archive::child_iterator i = a->child_begin(), e = a->child_end(); @@ -488,7 +471,7 @@ static void PrintFileSectionSizes(StringRef file) { } } } else if (MachOUniversalBinary *UB = - dyn_cast<MachOUniversalBinary>(binary.get())) { + dyn_cast<MachOUniversalBinary>(&Bin)) { // If we have a list of architecture flags specified dump only those. if (!ArchAll && ArchFlags.size() != 0) { // Look for a slice in the universal binary that matches each ArchFlag. @@ -692,7 +675,7 @@ static void PrintFileSectionSizes(StringRef file) { } } } - } else if (ObjectFile *o = dyn_cast<ObjectFile>(binary.get())) { + } else if (ObjectFile *o = dyn_cast<ObjectFile>(&Bin)) { if (!checkMachOAndArchFlags(o, file)) return; if (OutputFormat == sysv) @@ -731,8 +714,7 @@ int main(int argc, char **argv) { if (ArchFlags[i] == "all") { ArchAll = true; } else { - Triple T = MachOObjectFile::getArch(ArchFlags[i]); - if (T.getArch() == Triple::UnknownArch) { + if (!MachOObjectFile::isValidArch(ArchFlags[i])) { outs() << ToolName << ": for the -arch option: Unknown architecture " << "named '" << ArchFlags[i] << "'"; return 1; diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp index 23d3b63..21a79e3 100644 --- a/tools/llvm-stress/llvm-stress.cpp +++ b/tools/llvm-stress/llvm-stress.cpp @@ -704,11 +704,10 @@ int main(int argc, char **argv) { if (OutputFilename.empty()) OutputFilename = "-"; - std::string ErrorInfo; - Out.reset(new tool_output_file(OutputFilename.c_str(), ErrorInfo, - sys::fs::F_None)); - if (!ErrorInfo.empty()) { - errs() << ErrorInfo << '\n'; + std::error_code EC; + Out.reset(new tool_output_file(OutputFilename, EC, sys::fs::F_None)); + if (EC) { + errs() << EC.message() << '\n'; return 1; } diff --git a/tools/llvm-symbolizer/LLVMSymbolize.cpp b/tools/llvm-symbolizer/LLVMSymbolize.cpp index c1d39ef..36061d7 100644 --- a/tools/llvm-symbolizer/LLVMSymbolize.cpp +++ b/tools/llvm-symbolizer/LLVMSymbolize.cpp @@ -45,8 +45,26 @@ getDILineInfoSpecifier(const LLVMSymbolizer::Options &Opts) { ModuleInfo::ModuleInfo(ObjectFile *Obj, DIContext *DICtx) : Module(Obj), DebugInfoContext(DICtx) { + std::unique_ptr<DataExtractor> OpdExtractor; + uint64_t OpdAddress = 0; + // Find the .opd (function descriptor) section if any, for big-endian + // PowerPC64 ELF. + if (Module->getArch() == Triple::ppc64) { + for (section_iterator Section : Module->sections()) { + StringRef Name; + if (!error(Section->getName(Name)) && Name == ".opd") { + StringRef Data; + if (!error(Section->getContents(Data))) { + OpdExtractor.reset(new DataExtractor(Data, Module->isLittleEndian(), + Module->getBytesInAddress())); + OpdAddress = Section->getAddress(); + } + break; + } + } + } for (const SymbolRef &Symbol : Module->symbols()) { - addSymbol(Symbol); + addSymbol(Symbol, OpdExtractor.get(), OpdAddress); } bool NoSymbolTable = (Module->symbol_begin() == Module->symbol_end()); if (NoSymbolTable && Module->isELF()) { @@ -54,12 +72,13 @@ ModuleInfo::ModuleInfo(ObjectFile *Obj, DIContext *DICtx) std::pair<symbol_iterator, symbol_iterator> IDyn = getELFDynamicSymbolIterators(Module); for (symbol_iterator si = IDyn.first, se = IDyn.second; si != se; ++si) { - addSymbol(*si); + addSymbol(*si, OpdExtractor.get(), OpdAddress); } } } -void ModuleInfo::addSymbol(const SymbolRef &Symbol) { +void ModuleInfo::addSymbol(const SymbolRef &Symbol, DataExtractor *OpdExtractor, + uint64_t OpdAddress) { SymbolRef::Type SymbolType; if (error(Symbol.getType(SymbolType))) return; @@ -69,6 +88,18 @@ void ModuleInfo::addSymbol(const SymbolRef &Symbol) { if (error(Symbol.getAddress(SymbolAddress)) || SymbolAddress == UnknownAddressOrSize) return; + if (OpdExtractor) { + // For big-endian PowerPC64 ELF, symbols in the .opd section refer to + // function descriptors. The first word of the descriptor is a pointer to + // the function's code. + // For the purposes of symbolization, pretend the symbol's address is that + // of the function's code, not the descriptor. + uint64_t OpdOffset = SymbolAddress - OpdAddress; + uint32_t OpdOffset32 = OpdOffset; + if (OpdOffset == OpdOffset32 && + OpdExtractor->isValidOffsetForAddress(OpdOffset32)) + SymbolAddress = OpdExtractor->getAddress(&OpdOffset32); + } uint64_t SymbolSize; // Getting symbol size is linear for Mach-O files, so assume that symbol // occupies the memory range up to the following symbol. @@ -85,7 +116,7 @@ void ModuleInfo::addSymbol(const SymbolRef &Symbol) { SymbolName = SymbolName.drop_front(); // FIXME: If a function has alias, there are two entries in symbol table // with same address size. Make sure we choose the correct one. - SymbolMapTy &M = SymbolType == SymbolRef::ST_Function ? Functions : Objects; + auto &M = SymbolType == SymbolRef::ST_Function ? Functions : Objects; SymbolDesc SD = { SymbolAddress, SymbolSize }; M.insert(std::make_pair(SD, SymbolName)); } @@ -93,19 +124,20 @@ void ModuleInfo::addSymbol(const SymbolRef &Symbol) { bool ModuleInfo::getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address, std::string &Name, uint64_t &Addr, uint64_t &Size) const { - const SymbolMapTy &M = Type == SymbolRef::ST_Function ? Functions : Objects; - if (M.empty()) + const auto &SymbolMap = Type == SymbolRef::ST_Function ? Functions : Objects; + if (SymbolMap.empty()) return false; SymbolDesc SD = { Address, Address }; - SymbolMapTy::const_iterator it = M.upper_bound(SD); - if (it == M.begin()) + auto SymbolIterator = SymbolMap.upper_bound(SD); + if (SymbolIterator == SymbolMap.begin()) return false; - --it; - if (it->first.Size != 0 && it->first.Addr + it->first.Size <= Address) + --SymbolIterator; + if (SymbolIterator->first.Size != 0 && + SymbolIterator->first.Addr + SymbolIterator->first.Size <= Address) return false; - Name = it->second.str(); - Addr = it->first.Addr; - Size = it->first.Size; + Name = SymbolIterator->second.str(); + Addr = SymbolIterator->first.Addr; + Size = SymbolIterator->first.Size; return true; } @@ -206,14 +238,21 @@ std::string LLVMSymbolizer::symbolizeData(const std::string &ModuleName, void LLVMSymbolizer::flush() { DeleteContainerSeconds(Modules); - BinaryForPath.clear(); + ObjectPairForPathArch.clear(); ObjectFileForArch.clear(); } -static std::string getDarwinDWARFResourceForPath(const std::string &Path) { - StringRef Basename = sys::path::filename(Path); - const std::string &DSymDirectory = Path + ".dSYM"; - SmallString<16> ResourceName = StringRef(DSymDirectory); +// For Path="/path/to/foo" and Basename="foo" assume that debug info is in +// /path/to/foo.dSYM/Contents/Resources/DWARF/foo. +// For Path="/path/to/bar.dSYM" and Basename="foo" assume that debug info is in +// /path/to/bar.dSYM/Contents/Resources/DWARF/foo. +static +std::string getDarwinDWARFResourceForPath( + const std::string &Path, const std::string &Basename) { + SmallString<16> ResourceName = StringRef(Path); + if (sys::path::extension(Path) != ".dSYM") { + ResourceName += ".dSYM"; + } sys::path::append(ResourceName, "Contents", "Resources", "DWARF"); sys::path::append(ResourceName, Basename); return ResourceName.str(); @@ -264,9 +303,8 @@ static bool findDebugBinary(const std::string &OrigPath, return false; } -static bool getGNUDebuglinkContents(const Binary *Bin, std::string &DebugName, +static bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName, uint32_t &CRCHash) { - const ObjectFile *Obj = dyn_cast<ObjectFile>(Bin); if (!Obj) return false; for (const SectionRef &Section : Obj->sections()) { @@ -293,60 +331,96 @@ static bool getGNUDebuglinkContents(const Binary *Bin, std::string &DebugName, return false; } -LLVMSymbolizer::BinaryPair -LLVMSymbolizer::getOrCreateBinary(const std::string &Path) { - BinaryMapTy::iterator I = BinaryForPath.find(Path); - if (I != BinaryForPath.end()) +static +bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj, + const MachOObjectFile *Obj) { + ArrayRef<uint8_t> dbg_uuid = DbgObj->getUuid(); + ArrayRef<uint8_t> bin_uuid = Obj->getUuid(); + if (dbg_uuid.empty() || bin_uuid.empty()) + return false; + return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size()); +} + +ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, + const MachOObjectFile *MachExeObj, const std::string &ArchName) { + // On Darwin we may find DWARF in separate object file in + // resource directory. + std::vector<std::string> DsymPaths; + StringRef Filename = sys::path::filename(ExePath); + DsymPaths.push_back(getDarwinDWARFResourceForPath(ExePath, Filename)); + for (const auto &Path : Opts.DsymHints) { + DsymPaths.push_back(getDarwinDWARFResourceForPath(Path, Filename)); + } + for (const auto &path : DsymPaths) { + ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(path); + std::error_code EC = BinaryOrErr.getError(); + if (EC != errc::no_such_file_or_directory && !error(EC)) { + OwningBinary<Binary> B = std::move(BinaryOrErr.get()); + ObjectFile *DbgObj = + getObjectFileFromBinary(B.getBinary(), ArchName); + const MachOObjectFile *MachDbgObj = + dyn_cast<const MachOObjectFile>(DbgObj); + if (!MachDbgObj) continue; + if (darwinDsymMatchesBinary(MachDbgObj, MachExeObj)) { + addOwningBinary(std::move(B)); + return DbgObj; + } + } + } + return nullptr; +} + +LLVMSymbolizer::ObjectPair +LLVMSymbolizer::getOrCreateObjects(const std::string &Path, + const std::string &ArchName) { + const auto &I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName)); + if (I != ObjectPairForPathArch.end()) return I->second; - Binary *Bin = nullptr; - Binary *DbgBin = nullptr; - ErrorOr<Binary *> BinaryOrErr = createBinary(Path); + ObjectFile *Obj = nullptr; + ObjectFile *DbgObj = nullptr; + ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(Path); if (!error(BinaryOrErr.getError())) { - std::unique_ptr<Binary> ParsedBinary(BinaryOrErr.get()); - // Check if it's a universal binary. - Bin = ParsedBinary.get(); - ParsedBinariesAndObjects.push_back(std::move(ParsedBinary)); - if (Bin->isMachO() || Bin->isMachOUniversalBinary()) { - // On Darwin we may find DWARF in separate object file in - // resource directory. - const std::string &ResourcePath = - getDarwinDWARFResourceForPath(Path); - BinaryOrErr = createBinary(ResourcePath); - std::error_code EC = BinaryOrErr.getError(); - if (EC != errc::no_such_file_or_directory && !error(EC)) { - DbgBin = BinaryOrErr.get(); - ParsedBinariesAndObjects.push_back(std::unique_ptr<Binary>(DbgBin)); - } + OwningBinary<Binary> &B = BinaryOrErr.get(); + Obj = getObjectFileFromBinary(B.getBinary(), ArchName); + if (!Obj) { + ObjectPair Res = std::make_pair(nullptr, nullptr); + ObjectPairForPathArch[std::make_pair(Path, ArchName)] = Res; + return Res; } + addOwningBinary(std::move(B)); + if (auto MachObj = dyn_cast<const MachOObjectFile>(Obj)) + DbgObj = lookUpDsymFile(Path, MachObj, ArchName); // Try to locate the debug binary using .gnu_debuglink section. - if (!DbgBin) { + if (!DbgObj) { std::string DebuglinkName; uint32_t CRCHash; std::string DebugBinaryPath; - if (getGNUDebuglinkContents(Bin, DebuglinkName, CRCHash) && + if (getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash) && findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) { BinaryOrErr = createBinary(DebugBinaryPath); if (!error(BinaryOrErr.getError())) { - DbgBin = BinaryOrErr.get(); - ParsedBinariesAndObjects.push_back(std::unique_ptr<Binary>(DbgBin)); + OwningBinary<Binary> B = std::move(BinaryOrErr.get()); + DbgObj = getObjectFileFromBinary(B.getBinary(), ArchName); + addOwningBinary(std::move(B)); } } } } - if (!DbgBin) - DbgBin = Bin; - BinaryPair Res = std::make_pair(Bin, DbgBin); - BinaryForPath[Path] = Res; + if (!DbgObj) + DbgObj = Obj; + ObjectPair Res = std::make_pair(Obj, DbgObj); + ObjectPairForPathArch[std::make_pair(Path, ArchName)] = Res; return Res; } ObjectFile * -LLVMSymbolizer::getObjectFileFromBinary(Binary *Bin, const std::string &ArchName) { +LLVMSymbolizer::getObjectFileFromBinary(Binary *Bin, + const std::string &ArchName) { if (!Bin) return nullptr; ObjectFile *Res = nullptr; if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(Bin)) { - ObjectFileForArchMapTy::iterator I = ObjectFileForArch.find( + const auto &I = ObjectFileForArch.find( std::make_pair(UB, ArchName)); if (I != ObjectFileForArch.end()) return I->second; @@ -365,7 +439,7 @@ LLVMSymbolizer::getObjectFileFromBinary(Binary *Bin, const std::string &ArchName ModuleInfo * LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { - ModuleMapTy::iterator I = Modules.find(ModuleName); + const auto &I = Modules.find(ModuleName); if (I != Modules.end()) return I->second; std::string BinaryName = ModuleName; @@ -379,18 +453,16 @@ LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { ArchName = ArchStr; } } - BinaryPair Binaries = getOrCreateBinary(BinaryName); - ObjectFile *Obj = getObjectFileFromBinary(Binaries.first, ArchName); - ObjectFile *DbgObj = getObjectFileFromBinary(Binaries.second, ArchName); + ObjectPair Objects = getOrCreateObjects(BinaryName, ArchName); - if (!Obj) { + if (!Objects.first) { // Failed to find valid object file. Modules.insert(make_pair(ModuleName, (ModuleInfo *)nullptr)); return nullptr; } - DIContext *Context = DIContext::getDWARFContext(DbgObj); + DIContext *Context = DIContext::getDWARFContext(*Objects.second); assert(Context); - ModuleInfo *Info = new ModuleInfo(Obj, Context); + ModuleInfo *Info = new ModuleInfo(Objects.first, Context); Modules.insert(make_pair(ModuleName, Info)); return Info; } diff --git a/tools/llvm-symbolizer/LLVMSymbolize.h b/tools/llvm-symbolizer/LLVMSymbolize.h index 45febe0..ff848fc 100644 --- a/tools/llvm-symbolizer/LLVMSymbolize.h +++ b/tools/llvm-symbolizer/LLVMSymbolize.h @@ -10,13 +10,14 @@ // Header for LLVM symbolization library. // //===----------------------------------------------------------------------===// -#ifndef LLVM_SYMBOLIZE_H -#define LLVM_SYMBOLIZE_H +#ifndef LLVM_TOOLS_LLVM_SYMBOLIZER_LLVMSYMBOLIZE_H +#define LLVM_TOOLS_LLVM_SYMBOLIZER_LLVMSYMBOLIZE_H #include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Support/DataExtractor.h" #include "llvm/Support/MemoryBuffer.h" #include <map> #include <memory> @@ -39,13 +40,14 @@ public: bool PrintInlining : 1; bool Demangle : 1; std::string DefaultArch; + std::vector<std::string> DsymHints; Options(bool UseSymbolTable = true, FunctionNameKind PrintFunctions = FunctionNameKind::LinkageName, bool PrintInlining = true, bool Demangle = true, std::string DefaultArch = "") - : UseSymbolTable(UseSymbolTable), PrintFunctions(PrintFunctions), - PrintInlining(PrintInlining), Demangle(Demangle), - DefaultArch(DefaultArch) {} + : UseSymbolTable(UseSymbolTable), + PrintFunctions(PrintFunctions), PrintInlining(PrintInlining), + Demangle(Demangle), DefaultArch(DefaultArch) {} }; LLVMSymbolizer(const Options &Opts = Options()) : Opts(Opts) {} @@ -62,11 +64,15 @@ public: void flush(); static std::string DemangleName(const std::string &Name); private: - typedef std::pair<Binary*, Binary*> BinaryPair; + typedef std::pair<ObjectFile*, ObjectFile*> ObjectPair; ModuleInfo *getOrCreateModuleInfo(const std::string &ModuleName); - /// \brief Returns pair of pointers to binary and debug binary. - BinaryPair getOrCreateBinary(const std::string &Path); + ObjectFile *lookUpDsymFile(const std::string &Path, const MachOObjectFile *ExeObj, + const std::string &ArchName); + + /// \brief Returns pair of pointers to object and debug object. + ObjectPair getOrCreateObjects(const std::string &Path, + const std::string &ArchName); /// \brief Returns a parsed object file for a given architecture in a /// universal binary (or the binary itself if it is an object file). ObjectFile *getObjectFileFromBinary(Binary *Bin, const std::string &ArchName); @@ -75,14 +81,21 @@ private: // Owns all the parsed binaries and object files. SmallVector<std::unique_ptr<Binary>, 4> ParsedBinariesAndObjects; + SmallVector<std::unique_ptr<MemoryBuffer>, 4> MemoryBuffers; + void addOwningBinary(OwningBinary<Binary> OwningBin) { + std::unique_ptr<Binary> Bin; + std::unique_ptr<MemoryBuffer> MemBuf; + std::tie(Bin, MemBuf) = OwningBin.takeBinary(); + ParsedBinariesAndObjects.push_back(std::move(Bin)); + MemoryBuffers.push_back(std::move(MemBuf)); + } + // Owns module info objects. - typedef std::map<std::string, ModuleInfo *> ModuleMapTy; - ModuleMapTy Modules; - typedef std::map<std::string, BinaryPair> BinaryMapTy; - BinaryMapTy BinaryForPath; - typedef std::map<std::pair<MachOUniversalBinary *, std::string>, ObjectFile *> - ObjectFileForArchMapTy; - ObjectFileForArchMapTy ObjectFileForArch; + std::map<std::string, ModuleInfo *> Modules; + std::map<std::pair<MachOUniversalBinary *, std::string>, ObjectFile *> + ObjectFileForArch; + std::map<std::pair<std::string, std::string>, ObjectPair> + ObjectPairForPathArch; Options Opts; static const char kBadString[]; @@ -103,7 +116,11 @@ private: bool getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address, std::string &Name, uint64_t &Addr, uint64_t &Size) const; - void addSymbol(const SymbolRef &Symbol); + // For big-endian PowerPC64 ELF, OpdAddress is the address of the .opd + // (function descriptor) section and OpdExtractor refers to its contents. + void addSymbol(const SymbolRef &Symbol, + DataExtractor *OpdExtractor = nullptr, + uint64_t OpdAddress = 0); ObjectFile *Module; std::unique_ptr<DIContext> DebugInfoContext; @@ -116,12 +133,11 @@ private: return s1.Addr < s2.Addr; } }; - typedef std::map<SymbolDesc, StringRef> SymbolMapTy; - SymbolMapTy Functions; - SymbolMapTy Objects; + std::map<SymbolDesc, StringRef> Functions; + std::map<SymbolDesc, StringRef> Objects; }; } // namespace symbolize } // namespace llvm -#endif // LLVM_SYMBOLIZE_H +#endif diff --git a/tools/llvm-symbolizer/llvm-symbolizer.cpp b/tools/llvm-symbolizer/llvm-symbolizer.cpp index 29db172..d554022 100644 --- a/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" @@ -61,6 +62,11 @@ ClBinaryName("obj", cl::init(""), cl::desc("Path to object file to be symbolized (if not provided, " "object file should be specified for each input line)")); +static cl::list<std::string> +ClDsymHint("dsym-hint", cl::ZeroOrMore, + cl::desc("Path to .dSYM bundles to search for debug info for the " + "object files")); + static bool parseCommand(bool &IsData, std::string &ModuleName, uint64_t &ModuleOffset) { const char *kDataCmd = "DATA "; @@ -119,6 +125,14 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm-symbolizer\n"); LLVMSymbolizer::Options Opts(ClUseSymbolTable, ClPrintFunctions, ClPrintInlining, ClDemangle, ClDefaultArch); + for (const auto &hint : ClDsymHint) { + if (sys::path::extension(hint) == ".dSYM") { + Opts.DsymHints.push_back(hint); + } else { + errs() << "Warning: invalid dSYM hint: \"" << hint << + "\" (must have the '.dSYM' extension).\n"; + } + } LLVMSymbolizer Symbolizer(Opts); bool IsData = false; diff --git a/tools/llvm-vtabledump/CMakeLists.txt b/tools/llvm-vtabledump/CMakeLists.txt new file mode 100644 index 0000000..4fe205b --- /dev/null +++ b/tools/llvm-vtabledump/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Object + Support + ) + +add_llvm_tool(llvm-vtabledump + llvm-vtabledump.cpp + Error.cpp + ) diff --git a/tools/llvm-vtabledump/Error.cpp b/tools/llvm-vtabledump/Error.cpp new file mode 100644 index 0000000..c5de895 --- /dev/null +++ b/tools/llvm-vtabledump/Error.cpp @@ -0,0 +1,43 @@ +//===- Error.cpp - system_error extensions for llvm-vtabledump --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines a new error_category for the llvm-vtabledump tool. +// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +namespace { +class vtabledump_error_category : public std::error_category { +public: + const char *name() const LLVM_NOEXCEPT override { return "llvm.vtabledump"; } + std::string message(int ev) const override { + switch (static_cast<vtabledump_error>(ev)) { + case vtabledump_error::success: + return "Success"; + case vtabledump_error::file_not_found: + return "No such file."; + case vtabledump_error::unrecognized_file_format: + return "Unrecognized file type."; + } + llvm_unreachable( + "An enumerator of vtabledump_error does not have a message defined."); + } +}; +} // namespace + +namespace llvm { +const std::error_category &vtabledump_category() { + static vtabledump_error_category o; + return o; +} +} // namespace llvm diff --git a/tools/llvm-vtabledump/Error.h b/tools/llvm-vtabledump/Error.h new file mode 100644 index 0000000..fd8bb18 --- /dev/null +++ b/tools/llvm-vtabledump/Error.h @@ -0,0 +1,39 @@ +//===- Error.h - system_error extensions for llvm-vtabledump ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This declares a new error_category for the llvm-vtabledump tool. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_VTABLEDUMP_ERROR_H +#define LLVM_TOOLS_LLVM_VTABLEDUMP_ERROR_H + +#include <system_error> + +namespace llvm { +const std::error_category &vtabledump_category(); + +enum class vtabledump_error { + success = 0, + file_not_found, + unrecognized_file_format, +}; + +inline std::error_code make_error_code(vtabledump_error e) { + return std::error_code(static_cast<int>(e), vtabledump_category()); +} + +} // namespace llvm + +namespace std { +template <> +struct is_error_code_enum<llvm::vtabledump_error> : std::true_type {}; +} + +#endif diff --git a/tools/llvm-vtabledump/LLVMBuild.txt b/tools/llvm-vtabledump/LLVMBuild.txt new file mode 100644 index 0000000..6a3cbff --- /dev/null +++ b/tools/llvm-vtabledump/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-vtabledump/LLVMBuild.txt --------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; 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-vtabledump +parent = Tools +required_libraries = all-targets BitReader Object diff --git a/tools/llvm-vtabledump/Makefile b/tools/llvm-vtabledump/Makefile new file mode 100644 index 0000000..596c64c --- /dev/null +++ b/tools/llvm-vtabledump/Makefile @@ -0,0 +1,18 @@ +##===- tools/llvm-vtabledump/Makefile ----------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL := ../.. +TOOLNAME := llvm-vtabledump +LINK_COMPONENTS := bitreader object all-targets + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common + diff --git a/tools/llvm-vtabledump/llvm-vtabledump.cpp b/tools/llvm-vtabledump/llvm-vtabledump.cpp new file mode 100644 index 0000000..a21acae --- /dev/null +++ b/tools/llvm-vtabledump/llvm-vtabledump.cpp @@ -0,0 +1,464 @@ +//===- llvm-vtabledump.cpp - Dump vtables in an Object File -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Dumps VTables resident in object files and archives. Note, it currently only +// supports MS-ABI style object files. +// +//===----------------------------------------------------------------------===// + +#include "llvm-vtabledump.h" +#include "Error.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include <map> +#include <string> +#include <system_error> + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support; + +namespace opts { +cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input object files>"), + cl::ZeroOrMore); +} // namespace opts + +static int ReturnValue = EXIT_SUCCESS; + +namespace llvm { + +bool error(std::error_code EC) { + if (!EC) + return false; + + ReturnValue = EXIT_FAILURE; + outs() << "\nError reading file: " << EC.message() << ".\n"; + outs().flush(); + return true; +} + +} // namespace llvm + +static void reportError(StringRef Input, StringRef Message) { + if (Input == "-") + Input = "<stdin>"; + + errs() << Input << ": " << Message << "\n"; + errs().flush(); + ReturnValue = EXIT_FAILURE; +} + +static void reportError(StringRef Input, std::error_code EC) { + reportError(Input, EC.message()); +} + +static SmallVectorImpl<SectionRef> &getRelocSections(const ObjectFile *Obj, + const SectionRef &Sec) { + static bool MappingDone = false; + static std::map<SectionRef, SmallVector<SectionRef, 1>> SectionRelocMap; + if (!MappingDone) { + for (const SectionRef &Section : Obj->sections()) { + section_iterator Sec2 = Section.getRelocatedSection(); + if (Sec2 != Obj->section_end()) + SectionRelocMap[*Sec2].push_back(Section); + } + MappingDone = true; + } + return SectionRelocMap[Sec]; +} + +static bool collectRelocatedSymbols(const ObjectFile *Obj, + const SectionRef &Sec, uint64_t SecAddress, + uint64_t SymAddress, uint64_t SymSize, + StringRef *I, StringRef *E) { + uint64_t SymOffset = SymAddress - SecAddress; + uint64_t SymEnd = SymOffset + SymSize; + for (const SectionRef &SR : getRelocSections(Obj, Sec)) { + for (const object::RelocationRef &Reloc : SR.relocations()) { + if (I == E) + break; + const object::symbol_iterator RelocSymI = Reloc.getSymbol(); + if (RelocSymI == Obj->symbol_end()) + continue; + StringRef RelocSymName; + if (error(RelocSymI->getName(RelocSymName))) + return true; + uint64_t Offset; + if (error(Reloc.getOffset(Offset))) + return true; + if (Offset >= SymOffset && Offset < SymEnd) { + *I = RelocSymName; + ++I; + } + } + } + return false; +} + +static bool collectRelocationOffsets( + const ObjectFile *Obj, const SectionRef &Sec, uint64_t SecAddress, + uint64_t SymAddress, uint64_t SymSize, StringRef SymName, + std::map<std::pair<StringRef, uint64_t>, StringRef> &Collection) { + uint64_t SymOffset = SymAddress - SecAddress; + uint64_t SymEnd = SymOffset + SymSize; + for (const SectionRef &SR : getRelocSections(Obj, Sec)) { + for (const object::RelocationRef &Reloc : SR.relocations()) { + const object::symbol_iterator RelocSymI = Reloc.getSymbol(); + if (RelocSymI == Obj->symbol_end()) + continue; + StringRef RelocSymName; + if (error(RelocSymI->getName(RelocSymName))) + return true; + uint64_t Offset; + if (error(Reloc.getOffset(Offset))) + return true; + if (Offset >= SymOffset && Offset < SymEnd) + Collection[std::make_pair(SymName, Offset - SymOffset)] = RelocSymName; + } + } + return false; +} + +static void dumpVTables(const ObjectFile *Obj) { + struct CompleteObjectLocator { + StringRef Symbols[2]; + ArrayRef<little32_t> Data; + }; + struct ClassHierarchyDescriptor { + StringRef Symbols[1]; + ArrayRef<little32_t> Data; + }; + struct BaseClassDescriptor { + StringRef Symbols[2]; + ArrayRef<little32_t> Data; + }; + struct TypeDescriptor { + StringRef Symbols[1]; + uint64_t AlwaysZero; + StringRef MangledName; + }; + std::map<std::pair<StringRef, uint64_t>, StringRef> VFTableEntries; + std::map<StringRef, ArrayRef<little32_t>> VBTables; + std::map<StringRef, CompleteObjectLocator> COLs; + std::map<StringRef, ClassHierarchyDescriptor> CHDs; + std::map<std::pair<StringRef, uint64_t>, StringRef> BCAEntries; + std::map<StringRef, BaseClassDescriptor> BCDs; + std::map<StringRef, TypeDescriptor> TDs; + + std::map<std::pair<StringRef, uint64_t>, StringRef> VTableSymEntries; + std::map<std::pair<StringRef, uint64_t>, int64_t> VTableDataEntries; + std::map<std::pair<StringRef, uint64_t>, StringRef> VTTEntries; + std::map<StringRef, StringRef> TINames; + + uint8_t BytesInAddress = Obj->getBytesInAddress(); + + for (const object::SymbolRef &Sym : Obj->symbols()) { + StringRef SymName; + if (error(Sym.getName(SymName))) + return; + object::section_iterator SecI(Obj->section_begin()); + if (error(Sym.getSection(SecI))) + return; + // Skip external symbols. + if (SecI == Obj->section_end()) + continue; + const SectionRef &Sec = *SecI; + // Skip virtual or BSS sections. + if (Sec.isBSS() || Sec.isVirtual()) + continue; + StringRef SecContents; + if (error(Sec.getContents(SecContents))) + return; + uint64_t SymAddress, SymSize; + if (error(Sym.getAddress(SymAddress)) || error(Sym.getSize(SymSize))) + return; + uint64_t SecAddress = Sec.getAddress(); + uint64_t SecSize = Sec.getSize(); + uint64_t SymOffset = SymAddress - SecAddress; + StringRef SymContents = SecContents.substr(SymOffset, SymSize); + + // VFTables in the MS-ABI start with '??_7' and are contained within their + // own COMDAT section. We then determine the contents of the VFTable by + // looking at each relocation in the section. + if (SymName.startswith("??_7")) { + // Each relocation either names a virtual method or a thunk. We note the + // offset into the section and the symbol used for the relocation. + collectRelocationOffsets(Obj, Sec, SecAddress, SecAddress, SecSize, + SymName, VFTableEntries); + } + // VBTables in the MS-ABI start with '??_8' and are filled with 32-bit + // offsets of virtual bases. + else if (SymName.startswith("??_8")) { + ArrayRef<little32_t> VBTableData( + reinterpret_cast<const little32_t *>(SymContents.data()), + SymContents.size() / sizeof(little32_t)); + VBTables[SymName] = VBTableData; + } + // Complete object locators in the MS-ABI start with '??_R4' + else if (SymName.startswith("??_R4")) { + CompleteObjectLocator COL; + COL.Data = ArrayRef<little32_t>( + reinterpret_cast<const little32_t *>(SymContents.data()), 3); + StringRef *I = std::begin(COL.Symbols), *E = std::end(COL.Symbols); + if (collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, + E)) + return; + COLs[SymName] = COL; + } + // Class hierarchy descriptors in the MS-ABI start with '??_R3' + else if (SymName.startswith("??_R3")) { + ClassHierarchyDescriptor CHD; + CHD.Data = ArrayRef<little32_t>( + reinterpret_cast<const little32_t *>(SymContents.data()), 3); + StringRef *I = std::begin(CHD.Symbols), *E = std::end(CHD.Symbols); + if (collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, + E)) + return; + CHDs[SymName] = CHD; + } + // Class hierarchy descriptors in the MS-ABI start with '??_R2' + else if (SymName.startswith("??_R2")) { + // Each relocation names a base class descriptor. We note the offset into + // the section and the symbol used for the relocation. + collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize, + SymName, BCAEntries); + } + // Base class descriptors in the MS-ABI start with '??_R1' + else if (SymName.startswith("??_R1")) { + BaseClassDescriptor BCD; + BCD.Data = ArrayRef<little32_t>( + reinterpret_cast<const little32_t *>(SymContents.data()) + 1, 5); + StringRef *I = std::begin(BCD.Symbols), *E = std::end(BCD.Symbols); + if (collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, + E)) + return; + BCDs[SymName] = BCD; + } + // Type descriptors in the MS-ABI start with '??_R0' + else if (SymName.startswith("??_R0")) { + const char *DataPtr = SymContents.drop_front(BytesInAddress).data(); + TypeDescriptor TD; + if (BytesInAddress == 8) + TD.AlwaysZero = *reinterpret_cast<const little64_t *>(DataPtr); + else + TD.AlwaysZero = *reinterpret_cast<const little32_t *>(DataPtr); + TD.MangledName = SymContents.drop_front(BytesInAddress * 2); + StringRef *I = std::begin(TD.Symbols), *E = std::end(TD.Symbols); + if (collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, + E)) + return; + TDs[SymName] = TD; + } + // Construction vtables in the Itanium ABI start with '_ZTT' or '__ZTT'. + else if (SymName.startswith("_ZTT") || SymName.startswith("__ZTT")) { + collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize, + SymName, VTTEntries); + } + // Typeinfo names in the Itanium ABI start with '_ZTS' or '__ZTS'. + else if (SymName.startswith("_ZTS") || SymName.startswith("__ZTS")) { + TINames[SymName] = SymContents.slice(0, SymContents.find('\0')); + } + // Vtables in the Itanium ABI start with '_ZTV' or '__ZTV'. + else if (SymName.startswith("_ZTV") || SymName.startswith("__ZTV")) { + collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize, + SymName, VTableSymEntries); + for (uint64_t SymOffI = 0; SymOffI < SymSize; SymOffI += BytesInAddress) { + auto Key = std::make_pair(SymName, SymOffI); + if (VTableSymEntries.count(Key)) + continue; + const char *DataPtr = SymContents.substr(SymOffI, BytesInAddress).data(); + int64_t VData; + if (BytesInAddress == 8) + VData = *reinterpret_cast<const little64_t *>(DataPtr); + else + VData = *reinterpret_cast<const little32_t *>(DataPtr); + VTableDataEntries[Key] = VData; + } + } + // Typeinfo structures in the Itanium ABI start with '_ZTI' or '__ZTI'. + else if (SymName.startswith("_ZTI") || SymName.startswith("__ZTI")) { + // FIXME: Do something with these! + } + } + for (const std::pair<std::pair<StringRef, uint64_t>, StringRef> &VFTableEntry : + VFTableEntries) { + StringRef VFTableName = VFTableEntry.first.first; + uint64_t Offset = VFTableEntry.first.second; + StringRef SymName = VFTableEntry.second; + outs() << VFTableName << '[' << Offset << "]: " << SymName << '\n'; + } + for (const std::pair<StringRef, ArrayRef<little32_t>> &VBTable : VBTables) { + StringRef VBTableName = VBTable.first; + uint32_t Idx = 0; + for (little32_t Offset : VBTable.second) { + outs() << VBTableName << '[' << Idx << "]: " << Offset << '\n'; + Idx += sizeof(Offset); + } + } + for (const std::pair<StringRef, CompleteObjectLocator> &COLPair : COLs) { + StringRef COLName = COLPair.first; + const CompleteObjectLocator &COL = COLPair.second; + outs() << COLName << "[IsImageRelative]: " << COL.Data[0] << '\n'; + outs() << COLName << "[OffsetToTop]: " << COL.Data[1] << '\n'; + outs() << COLName << "[VFPtrOffset]: " << COL.Data[2] << '\n'; + outs() << COLName << "[TypeDescriptor]: " << COL.Symbols[0] << '\n'; + outs() << COLName << "[ClassHierarchyDescriptor]: " << COL.Symbols[1] << '\n'; + } + for (const std::pair<StringRef, ClassHierarchyDescriptor> &CHDPair : CHDs) { + StringRef CHDName = CHDPair.first; + const ClassHierarchyDescriptor &CHD = CHDPair.second; + outs() << CHDName << "[AlwaysZero]: " << CHD.Data[0] << '\n'; + outs() << CHDName << "[Flags]: " << CHD.Data[1] << '\n'; + outs() << CHDName << "[NumClasses]: " << CHD.Data[2] << '\n'; + outs() << CHDName << "[BaseClassArray]: " << CHD.Symbols[0] << '\n'; + } + for (const std::pair<std::pair<StringRef, uint64_t>, StringRef> &BCAEntry : + BCAEntries) { + StringRef BCAName = BCAEntry.first.first; + uint64_t Offset = BCAEntry.first.second; + StringRef SymName = BCAEntry.second; + outs() << BCAName << '[' << Offset << "]: " << SymName << '\n'; + } + for (const std::pair<StringRef, BaseClassDescriptor> &BCDPair : BCDs) { + StringRef BCDName = BCDPair.first; + const BaseClassDescriptor &BCD = BCDPair.second; + outs() << BCDName << "[TypeDescriptor]: " << BCD.Symbols[0] << '\n'; + outs() << BCDName << "[NumBases]: " << BCD.Data[0] << '\n'; + outs() << BCDName << "[OffsetInVBase]: " << BCD.Data[1] << '\n'; + outs() << BCDName << "[VBPtrOffset]: " << BCD.Data[2] << '\n'; + outs() << BCDName << "[OffsetInVBTable]: " << BCD.Data[3] << '\n'; + outs() << BCDName << "[Flags]: " << BCD.Data[4] << '\n'; + outs() << BCDName << "[ClassHierarchyDescriptor]: " << BCD.Symbols[1] << '\n'; + } + for (const std::pair<StringRef, TypeDescriptor> &TDPair : TDs) { + StringRef TDName = TDPair.first; + const TypeDescriptor &TD = TDPair.second; + outs() << TDName << "[VFPtr]: " << TD.Symbols[0] << '\n'; + outs() << TDName << "[AlwaysZero]: " << TD.AlwaysZero << '\n'; + outs() << TDName << "[MangledName]: "; + outs().write_escaped(TD.MangledName.rtrim(StringRef("\0", 1)), + /*UseHexEscapes=*/true) + << '\n'; + } + for (const std::pair<std::pair<StringRef, uint64_t>, StringRef> &VTTPair : + VTTEntries) { + StringRef VTTName = VTTPair.first.first; + uint64_t VTTOffset = VTTPair.first.second; + StringRef VTTEntry = VTTPair.second; + outs() << VTTName << '[' << VTTOffset << "]: " << VTTEntry << '\n'; + } + for (const std::pair<StringRef, StringRef> &TIPair : TINames) { + StringRef TIName = TIPair.first; + outs() << TIName << ": " << TIPair.second << '\n'; + } + auto VTableSymI = VTableSymEntries.begin(); + auto VTableSymE = VTableSymEntries.end(); + auto VTableDataI = VTableDataEntries.begin(); + auto VTableDataE = VTableDataEntries.end(); + for (;;) { + bool SymDone = VTableSymI == VTableSymE; + bool DataDone = VTableDataI == VTableDataE; + if (SymDone && DataDone) + break; + if (!SymDone && (DataDone || VTableSymI->first < VTableDataI->first)) { + StringRef VTableName = VTableSymI->first.first; + uint64_t Offset = VTableSymI->first.second; + StringRef VTableEntry = VTableSymI->second; + outs() << VTableName << '[' << Offset << "]: "; + outs() << VTableEntry; + outs() << '\n'; + ++VTableSymI; + continue; + } + if (!DataDone && (SymDone || VTableDataI->first < VTableSymI->first)) { + StringRef VTableName = VTableDataI->first.first; + uint64_t Offset = VTableDataI->first.second; + int64_t VTableEntry = VTableDataI->second; + outs() << VTableName << '[' << Offset << "]: "; + outs() << VTableEntry; + outs() << '\n'; + ++VTableDataI; + continue; + } + } +} + +static void dumpArchive(const Archive *Arc) { + for (const Archive::Child &ArcC : Arc->children()) { + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = ArcC.getAsBinary(); + if (std::error_code EC = ChildOrErr.getError()) { + // Ignore non-object files. + if (EC != object_error::invalid_file_type) + reportError(Arc->getFileName(), EC.message()); + continue; + } + + if (ObjectFile *Obj = dyn_cast<ObjectFile>(&*ChildOrErr.get())) + dumpVTables(Obj); + else + reportError(Arc->getFileName(), + vtabledump_error::unrecognized_file_format); + } +} + +static void dumpInput(StringRef File) { + // If file isn't stdin, check that it exists. + if (File != "-" && !sys::fs::exists(File)) { + reportError(File, vtabledump_error::file_not_found); + return; + } + + // Attempt to open the binary. + ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File); + if (std::error_code EC = BinaryOrErr.getError()) { + reportError(File, EC); + return; + } + Binary &Binary = *BinaryOrErr.get().getBinary(); + + if (Archive *Arc = dyn_cast<Archive>(&Binary)) + dumpArchive(Arc); + else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary)) + dumpVTables(Obj); + else + reportError(File, vtabledump_error::unrecognized_file_format); +} + +int main(int argc, const char *argv[]) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; + + // Initialize targets. + llvm::InitializeAllTargetInfos(); + + // Register the target printer for --version. + cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + + cl::ParseCommandLineOptions(argc, argv, "LLVM VTable Dumper\n"); + + // Default to stdin if no filename is specified. + if (opts::InputFilenames.size() == 0) + opts::InputFilenames.push_back("-"); + + std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), + dumpInput); + + return ReturnValue; +} diff --git a/tools/llvm-vtabledump/llvm-vtabledump.h b/tools/llvm-vtabledump/llvm-vtabledump.h new file mode 100644 index 0000000..62f7557 --- /dev/null +++ b/tools/llvm-vtabledump/llvm-vtabledump.h @@ -0,0 +1,23 @@ +//===-- llvm-vtabledump.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_VTABLEDUMP_LLVM_VTABLEDUMP_H +#define LLVM_TOOLS_LLVM_VTABLEDUMP_LLVM_VTABLEDUMP_H + +#include "llvm/Support/CommandLine.h" +#include <string> + +namespace opts { +extern llvm::cl::list<std::string> InputFilenames; +} // namespace opts + +#define LLVM_VTABLEDUMP_ENUM_ENT(ns, enum) \ + { #enum, ns::enum } + +#endif diff --git a/tools/lto/CMakeLists.txt b/tools/lto/CMakeLists.txt index 71391b7..559b22b 100644 --- a/tools/lto/CMakeLists.txt +++ b/tools/lto/CMakeLists.txt @@ -1,14 +1,11 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} - Core LTO MC MCDisassembler Support ) -add_definitions( -DLLVM_VERSION_INFO=\"${PACKAGE_VERSION}\" ) - set(SOURCES LTODisassembler.cpp lto.cpp diff --git a/tools/lto/Makefile b/tools/lto/Makefile index a4fe9ac..530c05a 100644 --- a/tools/lto/Makefile +++ b/tools/lto/Makefile @@ -17,10 +17,6 @@ EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/lto.exports include $(LEVEL)/Makefile.common -ifdef LLVM_VERSION_INFO -CXX.Flags += -DLLVM_VERSION_INFO='"$(LLVM_VERSION_INFO)"' -endif - ifeq ($(HOST_OS),Darwin) # Special hack to allow libLTO to have an offset version number. ifdef LLVM_LTO_VERSION_OFFSET diff --git a/tools/lto/lto.cpp b/tools/lto/lto.cpp index b401f9a..ef37c90 100644 --- a/tools/lto/lto.cpp +++ b/tools/lto/lto.cpp @@ -32,6 +32,10 @@ static cl::opt<bool> DisableGVNLoadPRE("disable-gvn-loadpre", cl::init(false), cl::desc("Do not run the GVN load PRE pass")); +static cl::opt<bool> +DisableLTOVectorization("disable-lto-vectorization", cl::init(false), + cl::desc("Do not run loop or slp vectorization during LTO")); + // Holds most recent error string. // *** Not thread safe *** static std::string sLastErrorString; @@ -146,6 +150,24 @@ lto_module_t lto_module_create_from_memory_with_path(const void* mem, LTOModule::createFromBuffer(mem, length, Options, sLastErrorString, path)); } +lto_module_t lto_module_create_in_local_context(const void *mem, size_t length, + const char *path) { + lto_initialize(); + llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + return wrap(LTOModule::createInLocalContext(mem, length, Options, + sLastErrorString, path)); +} + +lto_module_t lto_module_create_in_codegen_context(const void *mem, + size_t length, + const char *path, + lto_code_gen_t cg) { + lto_initialize(); + llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + return wrap(LTOModule::createInContext(mem, length, Options, sLastErrorString, + path, &unwrap(cg)->getContext())); +} + void lto_module_dispose(lto_module_t mod) { delete unwrap(mod); } const char* lto_module_get_target_triple(lto_module_t mod) { @@ -205,7 +227,7 @@ lto_code_gen_t lto_codegen_create(void) { void lto_codegen_dispose(lto_code_gen_t cg) { delete unwrap(cg); } bool lto_codegen_add_module(lto_code_gen_t cg, lto_module_t mod) { - return !unwrap(cg)->addModule(unwrap(mod), sLastErrorString); + return !unwrap(cg)->addModule(unwrap(mod)); } bool lto_codegen_set_debug_model(lto_code_gen_t cg, lto_debug_model debug) { @@ -222,10 +244,6 @@ void lto_codegen_set_cpu(lto_code_gen_t cg, const char *cpu) { return unwrap(cg)->setCpu(cpu); } -void lto_codegen_set_attr(lto_code_gen_t cg, const char *attr) { - return unwrap(cg)->setAttr(attr); -} - void lto_codegen_set_assembler_path(lto_code_gen_t cg, const char *path) { // In here only for backwards compatibility. We use MC now. } @@ -256,7 +274,8 @@ const void *lto_codegen_compile(lto_code_gen_t cg, size_t *length) { parsedOptions = true; } return unwrap(cg)->compile(length, DisableOpt, DisableInline, - DisableGVNLoadPRE, sLastErrorString); + DisableGVNLoadPRE, DisableLTOVectorization, + sLastErrorString); } bool lto_codegen_compile_to_file(lto_code_gen_t cg, const char **name) { @@ -265,8 +284,9 @@ bool lto_codegen_compile_to_file(lto_code_gen_t cg, const char **name) { lto_add_attrs(cg); parsedOptions = true; } - return !unwrap(cg)->compile_to_file(name, DisableOpt, DisableInline, - DisableGVNLoadPRE, sLastErrorString); + return !unwrap(cg)->compile_to_file( + name, DisableOpt, DisableInline, DisableGVNLoadPRE, + DisableLTOVectorization, sLastErrorString); } void lto_codegen_debug_options(lto_code_gen_t cg, const char *opt) { diff --git a/tools/macho-dump/macho-dump.cpp b/tools/macho-dump/macho-dump.cpp index 7600979..aac720d 100644 --- a/tools/macho-dump/macho-dump.cpp +++ b/tools/macho-dump/macho-dump.cpp @@ -324,7 +324,7 @@ DumpVersionMin(const MachOObjectFile &Obj, const MachOObjectFile::LoadCommandInfo &LCI) { MachO::version_min_command VMLC = Obj.getVersionMinLoadCommand(LCI); outs() << " ('version, " << VMLC.version << ")\n" - << " ('reserved, " << VMLC.reserved << ")\n"; + << " ('sdk, " << VMLC.sdk << ")\n"; return 0; } @@ -403,12 +403,12 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm Mach-O dumping tool\n"); - ErrorOr<Binary *> BinaryOrErr = createBinary(InputFile); + ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(InputFile); if (std::error_code EC = BinaryOrErr.getError()) return Error("unable to read input: '" + EC.message() + "'"); - std::unique_ptr<Binary> Binary(BinaryOrErr.get()); + Binary &Binary = *BinaryOrErr.get().getBinary(); - const MachOObjectFile *InputObject = dyn_cast<MachOObjectFile>(Binary.get()); + const MachOObjectFile *InputObject = dyn_cast<MachOObjectFile>(&Binary); if (!InputObject) return Error("Not a MachO object"); diff --git a/tools/msbuild/CMakeLists.txt b/tools/msbuild/CMakeLists.txt index b7be71d..4f471e5 100644 --- a/tools/msbuild/CMakeLists.txt +++ b/tools/msbuild/CMakeLists.txt @@ -10,6 +10,8 @@ if (WIN32) set(prop_file_v110_xp "Microsoft.Cpp.${platform}.LLVM-vs2012_xp.props") set(prop_file_v120 "toolset-vs2013.props") set(prop_file_v120_xp "toolset-vs2013_xp.props") + set(prop_file_v140 "toolset-vs2014.props") + set(prop_file_v140_xp "toolset-vs2014_xp.props") if (platform STREQUAL "Win32") set(mflag "m32") @@ -29,6 +31,11 @@ if (WIN32) configure_file(${prop_file_in} ${platform}/${prop_file_v120}) set(VS_VERSION "v120_xp") configure_file(${prop_file_in} ${platform}/${prop_file_v120_xp}) + set(VS_VERSION "v140") + set(MSC_VERSION "1900") + configure_file(${prop_file_in} ${platform}/${prop_file_v140}) + set(VS_VERSION "v140_xp") + configure_file(${prop_file_in} ${platform}/${prop_file_v140_xp}) set(VS_VERSION) set(MSC_VERSION) set(mflag) @@ -38,12 +45,16 @@ if (WIN32) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v110_xp}" DESTINATION tools/msbuild/${platform}) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v120}" DESTINATION tools/msbuild/${platform}) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v120_xp}" DESTINATION tools/msbuild/${platform}) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v140}" DESTINATION tools/msbuild/${platform}) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v140_xp}" DESTINATION tools/msbuild/${platform}) install(FILES "Microsoft.Cpp.Win32.LLVM-vs2010.targets" DESTINATION "tools/msbuild/${platform}" RENAME "Microsoft.Cpp.${platform}.LLVM-vs2010.targets") install(FILES "Microsoft.Cpp.Win32.LLVM-vs2012.targets" DESTINATION "tools/msbuild/${platform}" RENAME "Microsoft.Cpp.${platform}.LLVM-vs2012.targets") install(FILES "Microsoft.Cpp.Win32.LLVM-vs2012_xp.targets" DESTINATION "tools/msbuild/${platform}" RENAME "Microsoft.Cpp.${platform}.LLVM-vs2012_xp.targets") install(FILES "toolset-vs2013.targets" DESTINATION "tools/msbuild/${platform}") install(FILES "toolset-vs2013_xp.targets" DESTINATION "tools/msbuild/${platform}") + install(FILES "toolset-vs2014.targets" DESTINATION "tools/msbuild/${platform}") + install(FILES "toolset-vs2014_xp.targets" DESTINATION "tools/msbuild/${platform}") endforeach() set(LIB_PATH_VERSION) diff --git a/tools/msbuild/install.bat b/tools/msbuild/install.bat index 9880fb2..6e321e3 100644 --- a/tools/msbuild/install.bat +++ b/tools/msbuild/install.bat @@ -6,13 +6,15 @@ set SUCCESS=0 REM Change to the directory of this batch file.
cd /d %~dp0
+REM Loop over the two platforms in awkward batch file fashion.
set PLATFORM=None
-:START
-IF %PLATFORM% == x64 GOTO LOOPEND
+:PLATFORMLOOPHEAD
+IF %PLATFORM% == x64 GOTO PLATFORMLOOPEND
IF %PLATFORM% == Win32 SET PLATFORM=x64
IF %PLATFORM% == None SET PLATFORM=Win32
REM Search for the MSBuild toolsets directory.
+
SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\Platforms\%PLATFORM%\PlatformToolsets"
IF EXIST %D% GOTO FOUND_V100
SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\Platforms\%PLATFORM%\PlatformToolsets"
@@ -30,13 +32,24 @@ IF EXIST %D% GOTO FOUND_V120 SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\V120\Platforms\%PLATFORM%\PlatformToolsets"
IF EXIST %D% GOTO FOUND_V120
-:LOOPEND
+:TRY_V140
+SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\V140\Platforms\%PLATFORM%\PlatformToolsets"
+IF EXIST %D% GOTO FOUND_V140
+SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\V140\Platforms\%PLATFORM%\PlatformToolsets"
+IF EXIST %D% GOTO FOUND_V140
+
+:TRY_V150
+
+GOTO PLATFORMLOOPHEAD
+
+:PLATFORMLOOPEND
IF %SUCCESS% == 1 goto DONE
echo Failed to find MSBuild toolsets directory.
goto FAILED
:FOUND_V100
+REM Routine for installing v100 toolchain.
IF NOT EXIST %D%\LLVM-vs2010 mkdir %D%\LLVM-vs2010
IF NOT %ERRORLEVEL% == 0 GOTO FAILED
copy %PLATFORM%\Microsoft.Cpp.%PLATFORM%.LLVM-vs2010.props %D%\LLVM-vs2010
@@ -47,6 +60,7 @@ set SUCCESS=1 GOTO TRY_V110
:FOUND_V110
+REM Routine for installing v110 toolchain.
IF NOT EXIST %D%\LLVM-vs2012 mkdir %D%\LLVM-vs2012
IF NOT %ERRORLEVEL% == 0 GOTO FAILED
copy %PLATFORM%\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012.props %D%\LLVM-vs2012
@@ -63,6 +77,7 @@ set SUCCESS=1 GOTO TRY_V120
:FOUND_V120
+REM Routine for installing v120 toolchain.
IF NOT EXIST %D%\LLVM-vs2013 mkdir %D%\LLVM-vs2013
IF NOT %ERRORLEVEL% == 0 GOTO FAILED
copy %PLATFORM%\toolset-vs2013.props %D%\LLVM-vs2013\toolset.props
@@ -76,7 +91,24 @@ IF NOT %ERRORLEVEL% == 0 GOTO FAILED copy %PLATFORM%\toolset-vs2013_xp.targets %D%\LLVM-vs2013_xp\toolset.targets
IF NOT %ERRORLEVEL% == 0 GOTO FAILED
set SUCCESS=1
-GOTO START
+GOTO TRY_V140
+
+:FOUND_V140
+REM Routine for installing v140 toolchain.
+IF NOT EXIST %D%\LLVM-vs2014 mkdir %D%\LLVM-vs2014
+IF NOT %ERRORLEVEL% == 0 GOTO FAILED
+copy %PLATFORM%\toolset-vs2014.props %D%\LLVM-vs2014\toolset.props
+IF NOT %ERRORLEVEL% == 0 GOTO FAILED
+copy %PLATFORM%\toolset-vs2014.targets %D%\LLVM-vs2014\toolset.targets
+IF NOT %ERRORLEVEL% == 0 GOTO FAILED
+IF NOT EXIST %D%\LLVM-vs2014_xp mkdir %D%\LLVM-vs2014_xp
+IF NOT %ERRORLEVEL% == 0 GOTO FAILED
+copy %PLATFORM%\toolset-vs2014_xp.props %D%\LLVM-vs2014_xp\toolset.props
+IF NOT %ERRORLEVEL% == 0 GOTO FAILED
+copy %PLATFORM%\toolset-vs2014_xp.targets %D%\LLVM-vs2014_xp\toolset.targets
+IF NOT %ERRORLEVEL% == 0 GOTO FAILED
+set SUCCESS=1
+GOTO TRY_V150
:DONE
diff --git a/tools/msbuild/toolset-vs2014.targets b/tools/msbuild/toolset-vs2014.targets new file mode 100644 index 0000000..05b59a2 --- /dev/null +++ b/tools/msbuild/toolset-vs2014.targets @@ -0,0 +1,3 @@ +<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" />
+</Project>
diff --git a/tools/msbuild/toolset-vs2014_xp.targets b/tools/msbuild/toolset-vs2014_xp.targets new file mode 100644 index 0000000..eec4f18 --- /dev/null +++ b/tools/msbuild/toolset-vs2014_xp.targets @@ -0,0 +1,21 @@ +<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!-- Force TargetFrameworkVersion to v4.0 to support XP-->
+ <PropertyGroup>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <BeforeClCompileTargets>NoSupportCodeAnalysisXP;$(BeforeClCompileTargets)</BeforeClCompileTargets>
+ </PropertyGroup>
+
+ <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" />
+
+ <Target Name="NoSupportCodeAnalysisXP" Condition="'$(ErrorNoSupportCodeAnalysisXP)' != 'false'">
+ <VCMessage Condition="'$(DesignTimeBuild)' != 'true' and '@(ClCompile->AnyHaveMetadataValue('EnablePREfast', 'true'))'=='true'" Code="MSB8026" Type="Error"/>
+ </Target>
+
+ <PropertyGroup>
+ <PrepareForBuildDependsOn>CheckWindowsSDK71A;$(PrepareForBuildDependsOn)</PrepareForBuildDependsOn>
+ </PropertyGroup>
+
+ <Target Name="CheckWindowsSDK71A">
+ <VCMessage Code="MSB8003" Type="Warning" Arguments="WindowsSdkDir_71A" Condition="'$(WindowsSdkDir_71A)'=='' and '$(UseEnv)' != 'true'" />
+ </Target>
+</Project>
diff --git a/tools/msbuild/uninstall.bat b/tools/msbuild/uninstall.bat index b0bc943..c1afae2 100644 --- a/tools/msbuild/uninstall.bat +++ b/tools/msbuild/uninstall.bat @@ -6,8 +6,8 @@ REM CD to the directory of this batch file. cd /d %~dp0
set PLATFORM=None
-:START
-IF %PLATFORM% == x64 GOTO END
+:LOOPHEAD
+IF %PLATFORM% == x64 GOTO LOOPEND
IF %PLATFORM% == Win32 SET PLATFORM=x64
IF %PLATFORM% == None SET PLATFORM=Win32
@@ -51,8 +51,23 @@ IF EXIST %D%\LLVM-vs2013_xp del %D%\LLVM-vs2013_xp\toolset.props IF EXIST %D%\LLVM-vs2013_xp del %D%\LLVM-vs2013_xp\toolset.targets
IF EXIST %D%\LLVM-vs2013_xp rmdir %D%\LLVM-vs2013_xp
+SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\V140\Platforms\%PLATFORM%\PlatformToolsets"
+IF EXIST %D%\LLVM-vs2014 del %D%\LLVM-vs2014\toolset.props
+IF EXIST %D%\LLVM-vs2014 del %D%\LLVM-vs2014\toolset.targets
+IF EXIST %D%\LLVM-vs2014 rmdir %D%\LLVM-vs2014
+IF EXIST %D%\LLVM-vs2014_xp del %D%\LLVM-vs2014_xp\toolset.props
+IF EXIST %D%\LLVM-vs2014_xp del %D%\LLVM-vs2014_xp\toolset.targets
+IF EXIST %D%\LLVM-vs2014_xp rmdir %D%\LLVM-vs2014_xp
+SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\V140\Platforms\%PLATFORM%\PlatformToolsets"
+IF EXIST %D%\LLVM-vs2014 del %D%\LLVM-vs2014\toolset.props
+IF EXIST %D%\LLVM-vs2014 del %D%\LLVM-vs2014\toolset.targets
+IF EXIST %D%\LLVM-vs2014 rmdir %D%\LLVM-vs2014
+IF EXIST %D%\LLVM-vs2014_xp del %D%\LLVM-vs2014_xp\toolset.props
+IF EXIST %D%\LLVM-vs2014_xp del %D%\LLVM-vs2014_xp\toolset.targets
+IF EXIST %D%\LLVM-vs2014_xp rmdir %D%\LLVM-vs2014_xp
-GOTO START
-:END
+GOTO LOOPHEAD
+
+:LOOPEND
echo Done!
diff --git a/tools/obj2yaml/CMakeLists.txt b/tools/obj2yaml/CMakeLists.txt index f167ed5..3cdac5c 100644 --- a/tools/obj2yaml/CMakeLists.txt +++ b/tools/obj2yaml/CMakeLists.txt @@ -3,6 +3,6 @@ set(LLVM_LINK_COMPONENTS Support ) -add_llvm_utility(obj2yaml +add_llvm_tool(obj2yaml obj2yaml.cpp coff2yaml.cpp elf2yaml.cpp Error.cpp ) diff --git a/tools/obj2yaml/Error.cpp b/tools/obj2yaml/Error.cpp index 0074128..abef8af 100644 --- a/tools/obj2yaml/Error.cpp +++ b/tools/obj2yaml/Error.cpp @@ -20,7 +20,9 @@ public: }; } // namespace -const char *_obj2yaml_error_category::name() const { return "obj2yaml"; } +const char *_obj2yaml_error_category::name() const LLVM_NOEXCEPT { + return "obj2yaml"; +} std::string _obj2yaml_error_category::message(int ev) const { switch (static_cast<obj2yaml_error>(ev)) { diff --git a/tools/obj2yaml/Error.h b/tools/obj2yaml/Error.h index 4657f0d..982f59e 100644 --- a/tools/obj2yaml/Error.h +++ b/tools/obj2yaml/Error.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_ERROR_H -#define LLVM_TOOLS_ERROR_H +#ifndef LLVM_TOOLS_OBJ2YAML_ERROR_H +#define LLVM_TOOLS_OBJ2YAML_ERROR_H #include <system_error> diff --git a/tools/obj2yaml/Makefile b/tools/obj2yaml/Makefile index 95f393d..6cbef69 100644 --- a/tools/obj2yaml/Makefile +++ b/tools/obj2yaml/Makefile @@ -14,7 +14,4 @@ LINK_COMPONENTS := object # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS = 1 -# Don't install this utility -NO_INSTALL = 1 - include $(LEVEL)/Makefile.common diff --git a/tools/obj2yaml/coff2yaml.cpp b/tools/obj2yaml/coff2yaml.cpp index fed4533..5baa644 100644 --- a/tools/obj2yaml/coff2yaml.cpp +++ b/tools/obj2yaml/coff2yaml.cpp @@ -20,7 +20,9 @@ namespace { class COFFDumper { const object::COFFObjectFile &Obj; COFFYAML::Object YAMLObj; - void dumpHeader(const object::coff_file_header *Header); + template <typename T> + void dumpOptionalHeader(T OptionalHeader); + void dumpHeader(); void dumpSections(unsigned numSections); void dumpSymbols(unsigned numSymbols); @@ -31,40 +33,90 @@ public: } -static void check(std::error_code ec) { - if (ec) - report_fatal_error(ec.message()); +COFFDumper::COFFDumper(const object::COFFObjectFile &Obj) : Obj(Obj) { + const object::pe32_header *PE32Header = nullptr; + Obj.getPE32Header(PE32Header); + if (PE32Header) { + dumpOptionalHeader(PE32Header); + } else { + const object::pe32plus_header *PE32PlusHeader = nullptr; + Obj.getPE32PlusHeader(PE32PlusHeader); + if (PE32PlusHeader) { + dumpOptionalHeader(PE32PlusHeader); + } + } + dumpHeader(); + dumpSections(Obj.getNumberOfSections()); + dumpSymbols(Obj.getNumberOfSymbols()); } -COFFDumper::COFFDumper(const object::COFFObjectFile &Obj) : Obj(Obj) { - const object::coff_file_header *Header; - check(Obj.getCOFFHeader(Header)); - dumpHeader(Header); - dumpSections(Header->NumberOfSections); - dumpSymbols(Header->NumberOfSymbols); +template <typename T> void COFFDumper::dumpOptionalHeader(T OptionalHeader) { + YAMLObj.OptionalHeader = COFFYAML::PEHeader(); + YAMLObj.OptionalHeader->Header.AddressOfEntryPoint = + OptionalHeader->AddressOfEntryPoint; + YAMLObj.OptionalHeader->Header.AddressOfEntryPoint = + OptionalHeader->AddressOfEntryPoint; + YAMLObj.OptionalHeader->Header.ImageBase = OptionalHeader->ImageBase; + YAMLObj.OptionalHeader->Header.SectionAlignment = + OptionalHeader->SectionAlignment; + YAMLObj.OptionalHeader->Header.FileAlignment = OptionalHeader->FileAlignment; + YAMLObj.OptionalHeader->Header.MajorOperatingSystemVersion = + OptionalHeader->MajorOperatingSystemVersion; + YAMLObj.OptionalHeader->Header.MinorOperatingSystemVersion = + OptionalHeader->MinorOperatingSystemVersion; + YAMLObj.OptionalHeader->Header.MajorImageVersion = + OptionalHeader->MajorImageVersion; + YAMLObj.OptionalHeader->Header.MinorImageVersion = + OptionalHeader->MinorImageVersion; + YAMLObj.OptionalHeader->Header.MajorSubsystemVersion = + OptionalHeader->MajorSubsystemVersion; + YAMLObj.OptionalHeader->Header.MinorSubsystemVersion = + OptionalHeader->MinorSubsystemVersion; + YAMLObj.OptionalHeader->Header.Subsystem = OptionalHeader->Subsystem; + YAMLObj.OptionalHeader->Header.DLLCharacteristics = + OptionalHeader->DLLCharacteristics; + YAMLObj.OptionalHeader->Header.SizeOfStackReserve = + OptionalHeader->SizeOfStackReserve; + YAMLObj.OptionalHeader->Header.SizeOfStackCommit = + OptionalHeader->SizeOfStackCommit; + YAMLObj.OptionalHeader->Header.SizeOfHeapReserve = + OptionalHeader->SizeOfHeapReserve; + YAMLObj.OptionalHeader->Header.SizeOfHeapCommit = + OptionalHeader->SizeOfHeapCommit; + unsigned I = 0; + for (auto &DestDD : YAMLObj.OptionalHeader->DataDirectories) { + const object::data_directory *DD; + if (Obj.getDataDirectory(I++, DD)) + continue; + DestDD = COFF::DataDirectory(); + DestDD->RelativeVirtualAddress = DD->RelativeVirtualAddress; + DestDD->Size = DD->Size; + } } -void COFFDumper::dumpHeader(const object::coff_file_header *Header) { - YAMLObj.Header.Machine = Header->Machine; - YAMLObj.Header.Characteristics = Header->Characteristics; +void COFFDumper::dumpHeader() { + YAMLObj.Header.Machine = Obj.getMachine(); + YAMLObj.Header.Characteristics = Obj.getCharacteristics(); } void COFFDumper::dumpSections(unsigned NumSections) { - std::vector<COFFYAML::Section> &Sections = YAMLObj.Sections; - for (const auto &Section : Obj.sections()) { - const object::coff_section *Sect = Obj.getCOFFSection(Section); - COFFYAML::Section Sec; - Sec.Name = Sect->Name; // FIXME: check the null termination! - uint32_t Characteristics = Sect->Characteristics; - Sec.Header.Characteristics = Characteristics; - Sec.Alignment = 1 << (((Characteristics >> 20) & 0xf) - 1); + std::vector<COFFYAML::Section> &YAMLSections = YAMLObj.Sections; + for (const auto &ObjSection : Obj.sections()) { + const object::coff_section *COFFSection = Obj.getCOFFSection(ObjSection); + COFFYAML::Section NewYAMLSection; + ObjSection.getName(NewYAMLSection.Name); + NewYAMLSection.Header.Characteristics = COFFSection->Characteristics; + NewYAMLSection.Header.VirtualAddress = ObjSection.getAddress(); + NewYAMLSection.Header.VirtualSize = COFFSection->VirtualSize; + NewYAMLSection.Alignment = ObjSection.getAlignment(); ArrayRef<uint8_t> sectionData; - Obj.getSectionContents(Sect, sectionData); - Sec.SectionData = yaml::BinaryRef(sectionData); + if (!ObjSection.isBSS()) + Obj.getSectionContents(COFFSection, sectionData); + NewYAMLSection.SectionData = yaml::BinaryRef(sectionData); std::vector<COFFYAML::Relocation> Relocations; - for (const auto &Reloc : Section.relocations()) { + for (const auto &Reloc : ObjSection.relocations()) { const object::coff_relocation *reloc = Obj.getCOFFRelocation(Reloc); COFFYAML::Relocation Rel; object::symbol_iterator Sym = Reloc.getSymbol(); @@ -73,8 +125,8 @@ void COFFDumper::dumpSections(unsigned NumSections) { Rel.Type = reloc->Type; Relocations.push_back(Rel); } - Sec.Relocations = Relocations; - Sections.push_back(Sec); + NewYAMLSection.Relocations = Relocations; + YAMLSections.push_back(NewYAMLSection); } } @@ -111,13 +163,15 @@ static void dumpWeakExternal(COFFYAML::Symbol *Sym, static void dumpSectionDefinition(COFFYAML::Symbol *Sym, - const object::coff_aux_section_definition *ObjSD) { + const object::coff_aux_section_definition *ObjSD, + bool IsBigObj) { COFF::AuxiliarySectionDefinition YAMLASD; + int32_t AuxNumber = ObjSD->getNumber(IsBigObj); YAMLASD.Length = ObjSD->Length; YAMLASD.NumberOfRelocations = ObjSD->NumberOfRelocations; YAMLASD.NumberOfLinenumbers = ObjSD->NumberOfLinenumbers; YAMLASD.CheckSum = ObjSD->CheckSum; - YAMLASD.Number = ObjSD->Number; + YAMLASD.Number = AuxNumber; YAMLASD.Selection = ObjSD->Selection; Sym->SectionDefinition = YAMLASD; @@ -136,63 +190,64 @@ dumpCLRTokenDefinition(COFFYAML::Symbol *Sym, void COFFDumper::dumpSymbols(unsigned NumSymbols) { std::vector<COFFYAML::Symbol> &Symbols = YAMLObj.Symbols; for (const auto &S : Obj.symbols()) { - const object::coff_symbol *Symbol = Obj.getCOFFSymbol(S); + object::COFFSymbolRef Symbol = Obj.getCOFFSymbol(S); COFFYAML::Symbol Sym; Obj.getSymbolName(Symbol, Sym.Name); - Sym.SimpleType = COFF::SymbolBaseType(Symbol->getBaseType()); - Sym.ComplexType = COFF::SymbolComplexType(Symbol->getComplexType()); - Sym.Header.StorageClass = Symbol->StorageClass; - Sym.Header.Value = Symbol->Value; - Sym.Header.SectionNumber = Symbol->SectionNumber; - Sym.Header.NumberOfAuxSymbols = Symbol->NumberOfAuxSymbols; - - if (Symbol->NumberOfAuxSymbols > 0) { + Sym.SimpleType = COFF::SymbolBaseType(Symbol.getBaseType()); + Sym.ComplexType = COFF::SymbolComplexType(Symbol.getComplexType()); + Sym.Header.StorageClass = Symbol.getStorageClass(); + Sym.Header.Value = Symbol.getValue(); + Sym.Header.SectionNumber = Symbol.getSectionNumber(); + Sym.Header.NumberOfAuxSymbols = Symbol.getNumberOfAuxSymbols(); + + if (Symbol.getNumberOfAuxSymbols() > 0) { ArrayRef<uint8_t> AuxData = Obj.getSymbolAuxData(Symbol); - if (Symbol->isFunctionDefinition()) { + if (Symbol.isFunctionDefinition()) { // This symbol represents a function definition. - assert(Symbol->NumberOfAuxSymbols == 1 && + assert(Symbol.getNumberOfAuxSymbols() == 1 && "Expected a single aux symbol to describe this function!"); const object::coff_aux_function_definition *ObjFD = reinterpret_cast<const object::coff_aux_function_definition *>( AuxData.data()); dumpFunctionDefinition(&Sym, ObjFD); - } else if (Symbol->isFunctionLineInfo()) { + } else if (Symbol.isFunctionLineInfo()) { // This symbol describes function line number information. - assert(Symbol->NumberOfAuxSymbols == 1 && - "Exepected a single aux symbol to describe this section!"); + assert(Symbol.getNumberOfAuxSymbols() == 1 && + "Expected a single aux symbol to describe this function!"); const object::coff_aux_bf_and_ef_symbol *ObjBES = reinterpret_cast<const object::coff_aux_bf_and_ef_symbol *>( AuxData.data()); dumpbfAndEfLineInfo(&Sym, ObjBES); - } else if (Symbol->isWeakExternal()) { + } else if (Symbol.isAnyUndefined()) { // This symbol represents a weak external definition. - assert(Symbol->NumberOfAuxSymbols == 1 && - "Exepected a single aux symbol to describe this section!"); + assert(Symbol.getNumberOfAuxSymbols() == 1 && + "Expected a single aux symbol to describe this weak symbol!"); const object::coff_aux_weak_external *ObjWE = reinterpret_cast<const object::coff_aux_weak_external *>( AuxData.data()); dumpWeakExternal(&Sym, ObjWE); - } else if (Symbol->isFileRecord()) { + } else if (Symbol.isFileRecord()) { // This symbol represents a file record. Sym.File = StringRef(reinterpret_cast<const char *>(AuxData.data()), - Symbol->NumberOfAuxSymbols * COFF::SymbolSize) + Symbol.getNumberOfAuxSymbols() * + Obj.getSymbolTableEntrySize()) .rtrim(StringRef("\0", /*length=*/1)); - } else if (Symbol->isSectionDefinition()) { + } else if (Symbol.isSectionDefinition()) { // This symbol represents a section definition. - assert(Symbol->NumberOfAuxSymbols == 1 && + assert(Symbol.getNumberOfAuxSymbols() == 1 && "Expected a single aux symbol to describe this section!"); const object::coff_aux_section_definition *ObjSD = reinterpret_cast<const object::coff_aux_section_definition *>( AuxData.data()); - dumpSectionDefinition(&Sym, ObjSD); - } else if (Symbol->isCLRToken()) { + dumpSectionDefinition(&Sym, ObjSD, Symbol.isBigObj()); + } else if (Symbol.isCLRToken()) { // This symbol represents a CLR token definition. - assert(Symbol->NumberOfAuxSymbols == 1 && - "Expected a single aux symbol to describe this CLR Token"); + assert(Symbol.getNumberOfAuxSymbols() == 1 && + "Expected a single aux symbol to describe this CLR Token!"); const object::coff_aux_clr_token *ObjCLRToken = reinterpret_cast<const object::coff_aux_clr_token *>( diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp index 8b53ee7..d770ce1 100644 --- a/tools/obj2yaml/elf2yaml.cpp +++ b/tools/obj2yaml/elf2yaml.cpp @@ -133,7 +133,7 @@ std::error_code ELFDumper<ELFT>::dumpSymbol(Elf_Sym_Iter Sym, S.Type = Sym->getType(); S.Value = Sym->st_value; S.Size = Sym->st_size; - S.Visibility = Sym->st_other & 0x3; + S.Other = Sym->st_other; ErrorOr<StringRef> NameOrErr = Obj.getSymbolName(Sym); if (std::error_code EC = NameOrErr.getError()) diff --git a/tools/obj2yaml/obj2yaml.cpp b/tools/obj2yaml/obj2yaml.cpp index 944314a..b64096d 100644 --- a/tools/obj2yaml/obj2yaml.cpp +++ b/tools/obj2yaml/obj2yaml.cpp @@ -32,13 +32,13 @@ static std::error_code dumpInput(StringRef File) { if (File != "-" && !sys::fs::exists(File)) return obj2yaml_error::file_not_found; - ErrorOr<Binary *> BinaryOrErr = createBinary(File); + ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File); if (std::error_code EC = BinaryOrErr.getError()) return EC; - std::unique_ptr<Binary> Binary(BinaryOrErr.get()); + Binary &Binary = *BinaryOrErr.get().getBinary(); // TODO: If this is an archive, then burst it and dump each entry - if (ObjectFile *Obj = dyn_cast<ObjectFile>(Binary.get())) + if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary)) return dumpObject(*Obj); return obj2yaml_error::unrecognized_file_format; diff --git a/tools/obj2yaml/obj2yaml.h b/tools/obj2yaml/obj2yaml.h index 6d81110..643ab7b 100644 --- a/tools/obj2yaml/obj2yaml.h +++ b/tools/obj2yaml/obj2yaml.h @@ -10,8 +10,8 @@ // source file, implement it. //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_OBJ2YAML_H -#define LLVM_TOOLS_OBJ2YAML_H +#ifndef LLVM_TOOLS_OBJ2YAML_OBJ2YAML_H +#define LLVM_TOOLS_OBJ2YAML_OBJ2YAML_H #include "llvm/Object/COFF.h" #include "llvm/Support/raw_ostream.h" diff --git a/tools/opt/Android.mk b/tools/opt/Android.mk index 6f3f48d..fc3c8d0 100644 --- a/tools/opt/Android.mk +++ b/tools/opt/Android.mk @@ -60,6 +60,7 @@ llvm_opt_STATIC_LIBRARIES := \ libLLVMMC \ libLLVMMCParser \ libLLVMObject \ + libLLVMProfileData \ libLLVMCore \ libLLVMAsmParser \ libLLVMOption \ diff --git a/tools/opt/BreakpointPrinter.cpp b/tools/opt/BreakpointPrinter.cpp index 44f4a11..3cbc0ae 100644 --- a/tools/opt/BreakpointPrinter.cpp +++ b/tools/opt/BreakpointPrinter.cpp @@ -62,7 +62,7 @@ struct BreakpointPrinter : public ModulePass { continue; getContextName(SP.getContext().resolve(TypeIdentifierMap), Name); Name = Name + SP.getDisplayName().str(); - if (!Name.empty() && Processed.insert(Name)) { + if (!Name.empty() && Processed.insert(Name).second) { Out << Name << "\n"; } } diff --git a/tools/opt/NewPMDriver.h b/tools/opt/NewPMDriver.h index 3661d3e..f977bac 100644 --- a/tools/opt/NewPMDriver.h +++ b/tools/opt/NewPMDriver.h @@ -18,8 +18,8 @@ /// //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_OPT_NEW_PM_DRIVER_H -#define LLVM_TOOLS_OPT_NEW_PM_DRIVER_H +#ifndef LLVM_TOOLS_OPT_NEWPMDRIVER_H +#define LLVM_TOOLS_OPT_NEWPMDRIVER_H #include "llvm/ADT/StringRef.h" diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 6ba6340..cdd22e4 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -109,14 +109,6 @@ DisableOptimizations("disable-opt", cl::desc("Do not run any optimization passes")); static cl::opt<bool> -DisableInternalize("disable-internalize", - cl::desc("Do not mark all symbols as internal")); - -static cl::opt<bool> -StandardCompileOpts("std-compile-opts", - cl::desc("Include the standard compile time optimizations")); - -static cl::opt<bool> StandardLinkOpts("std-link-opts", cl::desc("Include the standard link time optimizations")); @@ -145,7 +137,7 @@ TargetTriple("mtriple", cl::desc("Override target triple for module")); static cl::opt<bool> UnitAtATime("funit-at-a-time", - cl::desc("Enable IPO. This is same as llvm-gcc's -funit-at-a-time"), + cl::desc("Enable IPO. This corresponds to gcc's -funit-at-a-time"), cl::init(true)); static cl::opt<bool> @@ -198,9 +190,8 @@ static inline void addPass(PassManagerBase &PM, Pass *P) { } } -/// AddOptimizationPasses - This routine adds optimization passes -/// based on selected optimization level, OptLevel. This routine -/// duplicates llvm-gcc behaviour. +/// This routine adds optimization passes based on selected optimization level, +/// OptLevel. /// /// OptLevel - Optimization Level static void AddOptimizationPasses(PassManagerBase &MPM,FunctionPassManager &FPM, @@ -238,41 +229,16 @@ static void AddOptimizationPasses(PassManagerBase &MPM,FunctionPassManager &FPM, Builder.populateModulePassManager(MPM); } -static void AddStandardCompilePasses(PassManagerBase &PM) { - PM.add(createVerifierPass()); // Verify that input is correct - - // If the -strip-debug command line option was specified, do it. - if (StripDebug) - addPass(PM, createStripSymbolsPass(true)); - - // Verify debug info only after it's (possibly) stripped. - PM.add(createDebugInfoVerifierPass()); - - if (DisableOptimizations) return; - - // -std-compile-opts adds the same module passes as -O3. +static void AddStandardLinkPasses(PassManagerBase &PM) { PassManagerBuilder Builder; + Builder.VerifyInput = true; + Builder.StripDebug = StripDebug; + if (DisableOptimizations) + Builder.OptLevel = 0; + if (!DisableInline) Builder.Inliner = createFunctionInliningPass(); - Builder.OptLevel = 3; - Builder.populateModulePassManager(PM); -} - -static void AddStandardLinkPasses(PassManagerBase &PM) { - PM.add(createVerifierPass()); // Verify that input is correct - - // If the -strip-debug command line option was specified, do it. - if (StripDebug) - addPass(PM, createStripSymbolsPass(true)); - - // Verify debug info only after it's (possibly) stripped. - PM.add(createDebugInfoVerifierPass()); - - if (DisableOptimizations) return; - - PassManagerBuilder Builder; - Builder.populateLTOPassManager(PM, /*Internalize=*/ !DisableInternalize, - /*RunInliner=*/ !DisableInline); + Builder.populateLTOPassManager(PM); } //===----------------------------------------------------------------------===// @@ -355,7 +321,8 @@ int main(int argc, char **argv) { // For codegen passes, only passes that do IR to IR transformation are // supported. initializeCodeGenPreparePass(Registry); - initializeAtomicExpandLoadLinkedPass(Registry); + initializeAtomicExpandPass(Registry); + initializeRewriteSymbolsPass(Registry); #ifdef LINK_POLLY_INTO_TOOLS polly::initializePollyPasses(Registry); @@ -372,8 +339,7 @@ int main(int argc, char **argv) { SMDiagnostic Err; // Load the input module... - std::unique_ptr<Module> M; - M.reset(ParseIRFile(InputFilename, Err, Context)); + std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context); if (!M.get()) { Err.print(argv[0], errs()); @@ -395,11 +361,10 @@ int main(int argc, char **argv) { if (OutputFilename.empty()) OutputFilename = "-"; - std::string ErrorInfo; - Out.reset(new tool_output_file(OutputFilename.c_str(), ErrorInfo, - sys::fs::F_None)); - if (!ErrorInfo.empty()) { - errs() << ErrorInfo << '\n'; + std::error_code EC; + Out.reset(new tool_output_file(OutputFilename, EC, sys::fs::F_None)); + if (EC) { + errs() << EC.message() << '\n'; return 1; } } @@ -452,7 +417,7 @@ int main(int argc, char **argv) { } if (DL) - Passes.add(new DataLayoutPass(M.get())); + Passes.add(new DataLayoutPass()); Triple ModuleTriple(M->getTargetTriple()); TargetMachine *Machine = nullptr; @@ -468,7 +433,7 @@ int main(int argc, char **argv) { if (OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz || OptLevelO3) { FPasses.reset(new FunctionPassManager(M.get())); if (DL) - FPasses->add(new DataLayoutPass(M.get())); + FPasses->add(new DataLayoutPass()); if (TM.get()) TM->addAnalysisPasses(*FPasses); @@ -480,11 +445,10 @@ int main(int argc, char **argv) { if (OutputFilename.empty()) OutputFilename = "-"; - std::string ErrorInfo; - Out.reset(new tool_output_file(OutputFilename.c_str(), ErrorInfo, - sys::fs::F_None)); - if (!ErrorInfo.empty()) { - errs() << ErrorInfo << '\n'; + std::error_code EC; + Out.reset(new tool_output_file(OutputFilename, EC, sys::fs::F_None)); + if (EC) { + errs() << EC.message() << '\n'; return 1; } } @@ -492,21 +456,12 @@ int main(int argc, char **argv) { NoOutput = true; } - // If the -strip-debug command line option was specified, add it. If - // -std-compile-opts was also specified, it will handle StripDebug. - if (StripDebug && !StandardCompileOpts) + // If the -strip-debug command line option was specified, add it. + if (StripDebug) addPass(Passes, createStripSymbolsPass(true)); // Create a new optimization pass for each one specified on the command line for (unsigned i = 0; i < PassList.size(); ++i) { - // Check to see if -std-compile-opts was specified before this option. If - // so, handle it. - if (StandardCompileOpts && - StandardCompileOpts.getPosition() < PassList.getPosition(i)) { - AddStandardCompilePasses(Passes); - StandardCompileOpts = false; - } - if (StandardLinkOpts && StandardLinkOpts.getPosition() < PassList.getPosition(i)) { AddStandardLinkPasses(Passes); @@ -579,12 +534,6 @@ int main(int argc, char **argv) { Passes.add(createPrintModulePass(errs())); } - // If -std-compile-opts was specified at the end of the pass list, add them. - if (StandardCompileOpts) { - AddStandardCompilePasses(Passes); - StandardCompileOpts = false; - } - if (StandardLinkOpts) { AddStandardLinkPasses(Passes); StandardLinkOpts = false; diff --git a/tools/verify-uselistorder/CMakeLists.txt b/tools/verify-uselistorder/CMakeLists.txt new file mode 100644 index 0000000..260a95a --- /dev/null +++ b/tools/verify-uselistorder/CMakeLists.txt @@ -0,0 +1,12 @@ +set(LLVM_LINK_COMPONENTS + AsmParser + BitReader + BitWriter + Core + IRReader + Support + ) + +add_llvm_tool(verify-uselistorder + verify-uselistorder.cpp + ) diff --git a/tools/verify-uselistorder/LLVMBuild.txt b/tools/verify-uselistorder/LLVMBuild.txt new file mode 100644 index 0000000..23957c1 --- /dev/null +++ b/tools/verify-uselistorder/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/verify-uselistorder/LLVMBuild.txt ----------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; 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 = verify-uselistorder +parent = Tools +required_libraries = IRReader BitWriter Support diff --git a/tools/verify-uselistorder/Makefile b/tools/verify-uselistorder/Makefile new file mode 100644 index 0000000..90d2aa8 --- /dev/null +++ b/tools/verify-uselistorder/Makefile @@ -0,0 +1,17 @@ +##===- tools/verify-uselistorder/Makefile ------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL := ../.. +TOOLNAME := verify-uselistorder +LINK_COMPONENTS := AsmParser BitReader BitWriter Core IRReader Support + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/verify-uselistorder/verify-uselistorder.cpp b/tools/verify-uselistorder/verify-uselistorder.cpp new file mode 100644 index 0000000..992a5b0 --- /dev/null +++ b/tools/verify-uselistorder/verify-uselistorder.cpp @@ -0,0 +1,562 @@ +//===- verify-uselistorder.cpp - The LLVM Modular Optimizer ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Verify that use-list order can be serialized correctly. After reading the +// provided IR, this tool shuffles the use-lists and then writes and reads to a +// separate Module whose use-list orders are compared to the original. +// +// The shuffles are deterministic, but guarantee that use-lists will change. +// The algorithm per iteration is as follows: +// +// 1. Seed the random number generator. The seed is different for each +// shuffle. Shuffle 0 uses default+0, shuffle 1 uses default+1, and so on. +// +// 2. Visit every Value in a deterministic order. +// +// 3. Assign a random number to each Use in the Value's use-list in order. +// +// 4. If the numbers are already in order, reassign numbers until they aren't. +// +// 5. Sort the use-list using Value::sortUseList(), which is a stable sort. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/UseListOrder.h" +#include "llvm/IR/Verifier.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/SystemUtils.h" +#include <random> +#include <vector> + +using namespace llvm; + +#define DEBUG_TYPE "use-list-order" + +static cl::opt<std::string> InputFilename(cl::Positional, + cl::desc("<input bitcode file>"), + cl::init("-"), + cl::value_desc("filename")); + +static cl::opt<bool> SaveTemps("save-temps", cl::desc("Save temp files"), + cl::init(false)); + +static cl::opt<unsigned> + NumShuffles("num-shuffles", + cl::desc("Number of times to shuffle and verify use-lists"), + cl::init(1)); + +namespace { + +struct TempFile { + std::string Filename; + FileRemover Remover; + bool init(const std::string &Ext); + bool writeBitcode(const Module &M) const; + bool writeAssembly(const Module &M) const; + std::unique_ptr<Module> readBitcode(LLVMContext &Context) const; + std::unique_ptr<Module> readAssembly(LLVMContext &Context) const; +}; + +struct ValueMapping { + DenseMap<const Value *, unsigned> IDs; + std::vector<const Value *> Values; + + /// \brief Construct a value mapping for module. + /// + /// Creates mapping from every value in \c M to an ID. This mapping includes + /// un-referencable values. + /// + /// Every \a Value that gets serialized in some way should be represented + /// here. The order needs to be deterministic, but it's unnecessary to match + /// the value-ids in the bitcode writer. + /// + /// All constants that are referenced by other values are included in the + /// mapping, but others -- which wouldn't be serialized -- are not. + ValueMapping(const Module &M); + + /// \brief Map a value. + /// + /// Maps a value. If it's a constant, maps all of its operands first. + void map(const Value *V); + unsigned lookup(const Value *V) const { return IDs.lookup(V); } +}; + +} // end namespace + +bool TempFile::init(const std::string &Ext) { + SmallVector<char, 64> Vector; + DEBUG(dbgs() << " - create-temp-file\n"); + if (auto EC = sys::fs::createTemporaryFile("use-list-order", Ext, Vector)) { + (void)EC; + DEBUG(dbgs() << "error: " << EC.message() << "\n"); + return true; + } + assert(!Vector.empty()); + + Filename.assign(Vector.data(), Vector.data() + Vector.size()); + Remover.setFile(Filename, !SaveTemps); + DEBUG(dbgs() << " - filename = " << Filename << "\n"); + return false; +} + +bool TempFile::writeBitcode(const Module &M) const { + DEBUG(dbgs() << " - write bitcode\n"); + std::error_code EC; + raw_fd_ostream OS(Filename, EC, sys::fs::F_None); + if (EC) { + DEBUG(dbgs() << "error: " << EC.message() << "\n"); + return true; + } + + WriteBitcodeToFile(&M, OS); + return false; +} + +bool TempFile::writeAssembly(const Module &M) const { + DEBUG(dbgs() << " - write assembly\n"); + std::error_code EC; + raw_fd_ostream OS(Filename, EC, sys::fs::F_Text); + if (EC) { + DEBUG(dbgs() << "error: " << EC.message() << "\n"); + return true; + } + + OS << M; + return false; +} + +std::unique_ptr<Module> TempFile::readBitcode(LLVMContext &Context) const { + DEBUG(dbgs() << " - read bitcode\n"); + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOr = + MemoryBuffer::getFile(Filename); + if (!BufferOr) { + DEBUG(dbgs() << "error: " << BufferOr.getError().message() << "\n"); + return nullptr; + } + + MemoryBuffer *Buffer = BufferOr.get().get(); + ErrorOr<Module *> ModuleOr = + parseBitcodeFile(Buffer->getMemBufferRef(), Context); + if (!ModuleOr) { + DEBUG(dbgs() << "error: " << ModuleOr.getError().message() << "\n"); + return nullptr; + } + return std::unique_ptr<Module>(ModuleOr.get()); +} + +std::unique_ptr<Module> TempFile::readAssembly(LLVMContext &Context) const { + DEBUG(dbgs() << " - read assembly\n"); + SMDiagnostic Err; + std::unique_ptr<Module> M = parseAssemblyFile(Filename, Err, Context); + if (!M.get()) + DEBUG(dbgs() << "error: "; Err.print("verify-use-list-order", dbgs())); + return M; +} + +ValueMapping::ValueMapping(const Module &M) { + // Every value should be mapped, including things like void instructions and + // basic blocks that are kept out of the ValueEnumerator. + // + // The current mapping order makes it easier to debug the tables. It happens + // to be similar to the ID mapping when writing ValueEnumerator, but they + // aren't (and needn't be) in sync. + + // Globals. + for (const GlobalVariable &G : M.globals()) + map(&G); + for (const GlobalAlias &A : M.aliases()) + map(&A); + for (const Function &F : M) + map(&F); + + // Constants used by globals. + for (const GlobalVariable &G : M.globals()) + if (G.hasInitializer()) + map(G.getInitializer()); + for (const GlobalAlias &A : M.aliases()) + map(A.getAliasee()); + for (const Function &F : M) + if (F.hasPrefixData()) + map(F.getPrefixData()); + + // Function bodies. + for (const Function &F : M) { + for (const Argument &A : F.args()) + map(&A); + for (const BasicBlock &BB : F) + map(&BB); + for (const BasicBlock &BB : F) + for (const Instruction &I : BB) + map(&I); + + // Constants used by instructions. + for (const BasicBlock &BB : F) + for (const Instruction &I : BB) + for (const Value *Op : I.operands()) + if ((isa<Constant>(Op) && !isa<GlobalValue>(*Op)) || + isa<InlineAsm>(Op)) + map(Op); + } +} + +void ValueMapping::map(const Value *V) { + if (IDs.lookup(V)) + return; + + if (auto *C = dyn_cast<Constant>(V)) + if (!isa<GlobalValue>(C)) + for (const Value *Op : C->operands()) + map(Op); + + Values.push_back(V); + IDs[V] = Values.size(); +} + +#ifndef NDEBUG +static void dumpMapping(const ValueMapping &VM) { + dbgs() << "value-mapping (size = " << VM.Values.size() << "):\n"; + for (unsigned I = 0, E = VM.Values.size(); I != E; ++I) { + dbgs() << " - id = " << I << ", value = "; + VM.Values[I]->dump(); + } +} + +static void debugValue(const ValueMapping &M, unsigned I, StringRef Desc) { + const Value *V = M.Values[I]; + dbgs() << " - " << Desc << " value = "; + V->dump(); + for (const Use &U : V->uses()) { + dbgs() << " => use: op = " << U.getOperandNo() + << ", user-id = " << M.IDs.lookup(U.getUser()) << ", user = "; + U.getUser()->dump(); + } +} + +static void debugUserMismatch(const ValueMapping &L, const ValueMapping &R, + unsigned I) { + dbgs() << " - fail: user mismatch: ID = " << I << "\n"; + debugValue(L, I, "LHS"); + debugValue(R, I, "RHS"); + + dbgs() << "\nlhs-"; + dumpMapping(L); + dbgs() << "\nrhs-"; + dumpMapping(R); +} + +static void debugSizeMismatch(const ValueMapping &L, const ValueMapping &R) { + dbgs() << " - fail: map size: " << L.Values.size() + << " != " << R.Values.size() << "\n"; + dbgs() << "\nlhs-"; + dumpMapping(L); + dbgs() << "\nrhs-"; + dumpMapping(R); +} +#endif + +static bool matches(const ValueMapping &LM, const ValueMapping &RM) { + DEBUG(dbgs() << "compare value maps\n"); + if (LM.Values.size() != RM.Values.size()) { + DEBUG(debugSizeMismatch(LM, RM)); + return false; + } + + // This mapping doesn't include dangling constant users, since those don't + // get serialized. However, checking if users are constant and calling + // isConstantUsed() on every one is very expensive. Instead, just check if + // the user is mapped. + auto skipUnmappedUsers = + [&](Value::const_use_iterator &U, Value::const_use_iterator E, + const ValueMapping &M) { + while (U != E && !M.lookup(U->getUser())) + ++U; + }; + + // Iterate through all values, and check that both mappings have the same + // users. + for (unsigned I = 0, E = LM.Values.size(); I != E; ++I) { + const Value *L = LM.Values[I]; + const Value *R = RM.Values[I]; + auto LU = L->use_begin(), LE = L->use_end(); + auto RU = R->use_begin(), RE = R->use_end(); + skipUnmappedUsers(LU, LE, LM); + skipUnmappedUsers(RU, RE, RM); + + while (LU != LE) { + if (RU == RE) { + DEBUG(debugUserMismatch(LM, RM, I)); + return false; + } + if (LM.lookup(LU->getUser()) != RM.lookup(RU->getUser())) { + DEBUG(debugUserMismatch(LM, RM, I)); + return false; + } + if (LU->getOperandNo() != RU->getOperandNo()) { + DEBUG(debugUserMismatch(LM, RM, I)); + return false; + } + skipUnmappedUsers(++LU, LE, LM); + skipUnmappedUsers(++RU, RE, RM); + } + if (RU != RE) { + DEBUG(debugUserMismatch(LM, RM, I)); + return false; + } + } + + return true; +} + +static void verifyAfterRoundTrip(const Module &M, + std::unique_ptr<Module> OtherM) { + if (!OtherM) + report_fatal_error("parsing failed"); + if (verifyModule(*OtherM, &errs())) + report_fatal_error("verification failed"); + if (!matches(ValueMapping(M), ValueMapping(*OtherM))) + report_fatal_error("use-list order changed"); +} +static void verifyBitcodeUseListOrder(const Module &M) { + errs() << "*** verify-use-list-order: bitcode ***\n"; + TempFile F; + if (F.init("bc")) + report_fatal_error("failed to initialize bitcode file"); + + if (F.writeBitcode(M)) + report_fatal_error("failed to write bitcode"); + + LLVMContext Context; + verifyAfterRoundTrip(M, F.readBitcode(Context)); +} + +static void verifyAssemblyUseListOrder(const Module &M) { + errs() << "*** verify-use-list-order: assembly ***\n"; + TempFile F; + if (F.init("ll")) + report_fatal_error("failed to initialize assembly file"); + + if (F.writeAssembly(M)) + report_fatal_error("failed to write assembly"); + + LLVMContext Context; + verifyAfterRoundTrip(M, F.readAssembly(Context)); +} + +static void verifyUseListOrder(const Module &M) { + verifyBitcodeUseListOrder(M); + verifyAssemblyUseListOrder(M); +} + +static void shuffleValueUseLists(Value *V, std::minstd_rand0 &Gen, + DenseSet<Value *> &Seen) { + if (!Seen.insert(V).second) + return; + + if (auto *C = dyn_cast<Constant>(V)) + if (!isa<GlobalValue>(C)) + for (Value *Op : C->operands()) + shuffleValueUseLists(Op, Gen, Seen); + + if (V->use_empty() || std::next(V->use_begin()) == V->use_end()) + // Nothing to shuffle for 0 or 1 users. + return; + + // Generate random numbers between 10 and 99, which will line up nicely in + // debug output. We're not worried about collisons here. + DEBUG(dbgs() << "V = "; V->dump()); + std::uniform_int_distribution<short> Dist(10, 99); + SmallDenseMap<const Use *, short, 16> Order; + auto compareUses = + [&Order](const Use &L, const Use &R) { return Order[&L] < Order[&R]; }; + do { + for (const Use &U : V->uses()) { + auto I = Dist(Gen); + Order[&U] = I; + DEBUG(dbgs() << " - order: " << I << ", op = " << U.getOperandNo() + << ", U = "; + U.getUser()->dump()); + } + } while (std::is_sorted(V->use_begin(), V->use_end(), compareUses)); + + DEBUG(dbgs() << " => shuffle\n"); + V->sortUseList(compareUses); + + DEBUG({ + for (const Use &U : V->uses()) { + dbgs() << " - order: " << Order.lookup(&U) + << ", op = " << U.getOperandNo() << ", U = "; + U.getUser()->dump(); + } + }); +} + +static void reverseValueUseLists(Value *V, DenseSet<Value *> &Seen) { + if (!Seen.insert(V).second) + return; + + if (auto *C = dyn_cast<Constant>(V)) + if (!isa<GlobalValue>(C)) + for (Value *Op : C->operands()) + reverseValueUseLists(Op, Seen); + + if (V->use_empty() || std::next(V->use_begin()) == V->use_end()) + // Nothing to shuffle for 0 or 1 users. + return; + + DEBUG({ + dbgs() << "V = "; + V->dump(); + for (const Use &U : V->uses()) { + dbgs() << " - order: op = " << U.getOperandNo() << ", U = "; + U.getUser()->dump(); + } + dbgs() << " => reverse\n"; + }); + + V->reverseUseList(); + + DEBUG({ + for (const Use &U : V->uses()) { + dbgs() << " - order: op = " << U.getOperandNo() << ", U = "; + U.getUser()->dump(); + } + }); +} + +template <class Changer> +static void changeUseLists(Module &M, Changer changeValueUseList) { + // Visit every value that would be serialized to an IR file. + // + // Globals. + for (GlobalVariable &G : M.globals()) + changeValueUseList(&G); + for (GlobalAlias &A : M.aliases()) + changeValueUseList(&A); + for (Function &F : M) + changeValueUseList(&F); + + // Constants used by globals. + for (GlobalVariable &G : M.globals()) + if (G.hasInitializer()) + changeValueUseList(G.getInitializer()); + for (GlobalAlias &A : M.aliases()) + changeValueUseList(A.getAliasee()); + for (Function &F : M) + if (F.hasPrefixData()) + changeValueUseList(F.getPrefixData()); + + // Function bodies. + for (Function &F : M) { + for (Argument &A : F.args()) + changeValueUseList(&A); + for (BasicBlock &BB : F) + changeValueUseList(&BB); + for (BasicBlock &BB : F) + for (Instruction &I : BB) + changeValueUseList(&I); + + // Constants used by instructions. + for (BasicBlock &BB : F) + for (Instruction &I : BB) + for (Value *Op : I.operands()) + if ((isa<Constant>(Op) && !isa<GlobalValue>(*Op)) || + isa<InlineAsm>(Op)) + changeValueUseList(Op); + } + + if (verifyModule(M, &errs())) + report_fatal_error("verification failed"); +} + +static void shuffleUseLists(Module &M, unsigned SeedOffset) { + errs() << "*** shuffle-use-lists ***\n"; + std::minstd_rand0 Gen(std::minstd_rand0::default_seed + SeedOffset); + DenseSet<Value *> Seen; + changeUseLists(M, [&](Value *V) { shuffleValueUseLists(V, Gen, Seen); }); + DEBUG(dbgs() << "\n"); +} + +static void reverseUseLists(Module &M) { + errs() << "*** reverse-use-lists ***\n"; + DenseSet<Value *> Seen; + changeUseLists(M, [&](Value *V) { reverseValueUseLists(V, Seen); }); + DEBUG(dbgs() << "\n"); +} + +int main(int argc, char **argv) { + sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc, argv); + + // Enable debug stream buffering. + EnableDebugBuffering = true; + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + LLVMContext &Context = getGlobalContext(); + + cl::ParseCommandLineOptions(argc, argv, + "llvm tool to verify use-list order\n"); + + SMDiagnostic Err; + + // Load the input module... + std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context); + + if (!M.get()) { + Err.print(argv[0], errs()); + return 1; + } + if (verifyModule(*M, &errs())) + report_fatal_error("verification failed"); + + errs() << "*** verify-use-list-order ***\n"; + // Can't verify if order isn't preserved. + if (!shouldPreserveBitcodeUseListOrder()) { + errs() << "warning: forcing -preserve-bc-use-list-order\n"; + setPreserveBitcodeUseListOrder(true); + } + if (!shouldPreserveAssemblyUseListOrder()) { + errs() << "warning: forcing -preserve-ll-use-list-order\n"; + setPreserveAssemblyUseListOrder(true); + } + + // Verify the use lists now and after reversing them. + verifyUseListOrder(*M); + reverseUseLists(*M); + verifyUseListOrder(*M); + + for (unsigned I = 0, E = NumShuffles; I != E; ++I) { + errs() << "*** shuffle iteration: " << I + 1 << " of " << E << " ***\n"; + + // Shuffle with a different (deterministic) seed each time. + shuffleUseLists(*M, I); + + // Verify again before and after reversing. + verifyUseListOrder(*M); + reverseUseLists(*M); + verifyUseListOrder(*M); + } + + return 0; +} diff --git a/tools/yaml2obj/CMakeLists.txt b/tools/yaml2obj/CMakeLists.txt index 5e63dfb..52e9df4 100644 --- a/tools/yaml2obj/CMakeLists.txt +++ b/tools/yaml2obj/CMakeLists.txt @@ -1,9 +1,10 @@ set(LLVM_LINK_COMPONENTS + MC Object Support ) -add_llvm_utility(yaml2obj +add_llvm_tool(yaml2obj yaml2obj.cpp yaml2coff.cpp yaml2elf.cpp diff --git a/tools/yaml2obj/Makefile b/tools/yaml2obj/Makefile index 8801795..912f0e3 100644 --- a/tools/yaml2obj/Makefile +++ b/tools/yaml2obj/Makefile @@ -14,7 +14,4 @@ LINK_COMPONENTS := object # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS = 1 -# Don't install this utility -NO_INSTALL = 1 - include $(LEVEL)/Makefile.common diff --git a/tools/yaml2obj/yaml2coff.cpp b/tools/yaml2obj/yaml2coff.cpp index c772db9..6983e9d 100644 --- a/tools/yaml2obj/yaml2coff.cpp +++ b/tools/yaml2obj/yaml2coff.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Object/COFFYAML.h" +#include "llvm/Object/COFF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" @@ -30,12 +31,35 @@ using namespace llvm; /// This parses a yaml stream that represents a COFF object file. /// See docs/yaml2obj for the yaml scheema. struct COFFParser { - COFFParser(COFFYAML::Object &Obj) : Obj(Obj) { + COFFParser(COFFYAML::Object &Obj) + : Obj(Obj), SectionTableStart(0), SectionTableSize(0) { // A COFF string table always starts with a 4 byte size field. Offsets into // it include this size, so allocate it now. StringTable.append(4, char(0)); } + bool useBigObj() const { + return static_cast<int32_t>(Obj.Sections.size()) > + COFF::MaxNumberOfSections16; + } + + bool isPE() const { return Obj.OptionalHeader.hasValue(); } + bool is64Bit() const { + return Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_AMD64; + } + + uint32_t getFileAlignment() const { + return Obj.OptionalHeader->Header.FileAlignment; + } + + unsigned getHeaderSize() const { + return useBigObj() ? COFF::Header32Size : COFF::Header16Size; + } + + unsigned getSymbolSize() const { + return useBigObj() ? COFF::Symbol32Size : COFF::Symbol16Size; + } + bool parseSections() { for (std::vector<COFFYAML::Section>::iterator i = Obj.Sections.begin(), e = Obj.Sections.end(); i != e; ++i) { @@ -111,39 +135,61 @@ struct COFFParser { StringMap<unsigned> StringTableMap; std::string StringTable; + uint32_t SectionTableStart; + uint32_t SectionTableSize; }; // Take a CP and assign addresses and sizes to everything. Returns false if the // layout is not valid to do. -static bool layoutCOFF(COFFParser &CP) { - uint32_t SectionTableStart = 0; - uint32_t SectionTableSize = 0; +static bool layoutOptionalHeader(COFFParser &CP) { + if (!CP.isPE()) + return true; + unsigned PEHeaderSize = CP.is64Bit() ? sizeof(object::pe32plus_header) + : sizeof(object::pe32_header); + CP.Obj.Header.SizeOfOptionalHeader = + PEHeaderSize + + sizeof(object::data_directory) * (COFF::NUM_DATA_DIRECTORIES + 1); + return true; +} + +namespace { +enum { DOSStubSize = 128 }; +} +// Take a CP and assign addresses and sizes to everything. Returns false if the +// layout is not valid to do. +static bool layoutCOFF(COFFParser &CP) { // The section table starts immediately after the header, including the // optional header. - SectionTableStart = sizeof(COFF::header) + CP.Obj.Header.SizeOfOptionalHeader; - SectionTableSize = sizeof(COFF::section) * CP.Obj.Sections.size(); + CP.SectionTableStart = + CP.getHeaderSize() + CP.Obj.Header.SizeOfOptionalHeader; + if (CP.isPE()) + CP.SectionTableStart += DOSStubSize + sizeof(COFF::PEMagic); + CP.SectionTableSize = COFF::SectionSize * CP.Obj.Sections.size(); - uint32_t CurrentSectionDataOffset = SectionTableStart + SectionTableSize; + uint32_t CurrentSectionDataOffset = + CP.SectionTableStart + CP.SectionTableSize; // Assign each section data address consecutively. - for (std::vector<COFFYAML::Section>::iterator i = CP.Obj.Sections.begin(), - e = CP.Obj.Sections.end(); - i != e; ++i) { - if (i->SectionData.binary_size() > 0) { - i->Header.SizeOfRawData = i->SectionData.binary_size(); - i->Header.PointerToRawData = CurrentSectionDataOffset; - CurrentSectionDataOffset += i->Header.SizeOfRawData; - if (!i->Relocations.empty()) { - i->Header.PointerToRelocations = CurrentSectionDataOffset; - i->Header.NumberOfRelocations = i->Relocations.size(); - CurrentSectionDataOffset += i->Header.NumberOfRelocations * - COFF::RelocationSize; + for (COFFYAML::Section &S : CP.Obj.Sections) { + if (S.SectionData.binary_size() > 0) { + CurrentSectionDataOffset = RoundUpToAlignment( + CurrentSectionDataOffset, CP.isPE() ? CP.getFileAlignment() : 4); + S.Header.SizeOfRawData = S.SectionData.binary_size(); + if (CP.isPE()) + S.Header.SizeOfRawData = + RoundUpToAlignment(S.Header.SizeOfRawData, CP.getFileAlignment()); + S.Header.PointerToRawData = CurrentSectionDataOffset; + CurrentSectionDataOffset += S.Header.SizeOfRawData; + if (!S.Relocations.empty()) { + S.Header.PointerToRelocations = CurrentSectionDataOffset; + S.Header.NumberOfRelocations = S.Relocations.size(); + CurrentSectionDataOffset += + S.Header.NumberOfRelocations * COFF::RelocationSize; } - // TODO: Handle alignment. } else { - i->Header.SizeOfRawData = 0; - i->Header.PointerToRawData = 0; + S.Header.SizeOfRawData = 0; + S.Header.PointerToRawData = 0; } } @@ -163,7 +209,7 @@ static bool layoutCOFF(COFFParser &CP) { NumberOfAuxSymbols += 1; if (!i->File.empty()) NumberOfAuxSymbols += - (i->File.size() + COFF::SymbolSize - 1) / COFF::SymbolSize; + (i->File.size() + CP.getSymbolSize() - 1) / CP.getSymbolSize(); if (i->SectionDefinition) NumberOfAuxSymbols += 1; if (i->CLRToken) @@ -175,7 +221,10 @@ static bool layoutCOFF(COFFParser &CP) { // Store all the allocated start addresses in the header. CP.Obj.Header.NumberOfSections = CP.Obj.Sections.size(); CP.Obj.Header.NumberOfSymbols = NumberOfSymbols; - CP.Obj.Header.PointerToSymbolTable = SymbolTableStart; + if (NumberOfSymbols > 0 || CP.StringTable.size() > 4) + CP.Obj.Header.PointerToSymbolTable = SymbolTableStart; + else + CP.Obj.Header.PointerToSymbolTable = 0; *reinterpret_cast<support::ulittle32_t *>(&CP.StringTable[0]) = CP.StringTable.size(); @@ -222,15 +271,153 @@ zeros_impl<sizeof(T)> zeros(const T &) { return zeros_impl<sizeof(T)>(); } -bool writeCOFF(COFFParser &CP, raw_ostream &OS) { - OS << binary_le(CP.Obj.Header.Machine) - << binary_le(CP.Obj.Header.NumberOfSections) - << binary_le(CP.Obj.Header.TimeDateStamp) - << binary_le(CP.Obj.Header.PointerToSymbolTable) - << binary_le(CP.Obj.Header.NumberOfSymbols) - << binary_le(CP.Obj.Header.SizeOfOptionalHeader) - << binary_le(CP.Obj.Header.Characteristics); +struct num_zeros_impl { + size_t N; + num_zeros_impl(size_t N) : N(N) {} +}; + +raw_ostream &operator<<(raw_ostream &OS, const num_zeros_impl &NZI) { + for (size_t I = 0; I != NZI.N; ++I) + OS.write(0); + return OS; +} + +num_zeros_impl num_zeros(size_t N) { + num_zeros_impl NZI(N); + return NZI; +} + +template <typename T> +static uint32_t initializeOptionalHeader(COFFParser &CP, uint16_t Magic, T Header) { + memset(Header, 0, sizeof(*Header)); + Header->Magic = Magic; + Header->SectionAlignment = CP.Obj.OptionalHeader->Header.SectionAlignment; + Header->FileAlignment = CP.Obj.OptionalHeader->Header.FileAlignment; + uint32_t SizeOfCode = 0, SizeOfInitializedData = 0, + SizeOfUninitializedData = 0; + uint32_t SizeOfHeaders = RoundUpToAlignment( + CP.SectionTableStart + CP.SectionTableSize, Header->FileAlignment); + uint32_t SizeOfImage = + RoundUpToAlignment(SizeOfHeaders, Header->SectionAlignment); + uint32_t BaseOfData = 0; + for (const COFFYAML::Section &S : CP.Obj.Sections) { + if (S.Header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) + SizeOfCode += S.Header.SizeOfRawData; + if (S.Header.Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) + SizeOfInitializedData += S.Header.SizeOfRawData; + if (S.Header.Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) + SizeOfUninitializedData += S.Header.SizeOfRawData; + if (S.Name.equals(".text")) + Header->BaseOfCode = S.Header.VirtualAddress; // RVA + else if (S.Name.equals(".data")) + BaseOfData = S.Header.VirtualAddress; // RVA + if (S.Header.VirtualAddress) + SizeOfImage += + RoundUpToAlignment(S.Header.VirtualSize, Header->SectionAlignment); + } + Header->SizeOfCode = SizeOfCode; + Header->SizeOfInitializedData = SizeOfInitializedData; + Header->SizeOfUninitializedData = SizeOfUninitializedData; + Header->AddressOfEntryPoint = + CP.Obj.OptionalHeader->Header.AddressOfEntryPoint; // RVA + Header->ImageBase = CP.Obj.OptionalHeader->Header.ImageBase; + Header->MajorOperatingSystemVersion = + CP.Obj.OptionalHeader->Header.MajorOperatingSystemVersion; + Header->MinorOperatingSystemVersion = + CP.Obj.OptionalHeader->Header.MinorOperatingSystemVersion; + Header->MajorImageVersion = + CP.Obj.OptionalHeader->Header.MajorImageVersion; + Header->MinorImageVersion = + CP.Obj.OptionalHeader->Header.MinorImageVersion; + Header->MajorSubsystemVersion = + CP.Obj.OptionalHeader->Header.MajorSubsystemVersion; + Header->MinorSubsystemVersion = + CP.Obj.OptionalHeader->Header.MinorSubsystemVersion; + Header->SizeOfImage = SizeOfImage; + Header->SizeOfHeaders = SizeOfHeaders; + Header->Subsystem = CP.Obj.OptionalHeader->Header.Subsystem; + Header->DLLCharacteristics = CP.Obj.OptionalHeader->Header.DLLCharacteristics; + Header->SizeOfStackReserve = CP.Obj.OptionalHeader->Header.SizeOfStackReserve; + Header->SizeOfStackCommit = CP.Obj.OptionalHeader->Header.SizeOfStackCommit; + Header->SizeOfHeapReserve = CP.Obj.OptionalHeader->Header.SizeOfHeapReserve; + Header->SizeOfHeapCommit = CP.Obj.OptionalHeader->Header.SizeOfHeapCommit; + Header->NumberOfRvaAndSize = COFF::NUM_DATA_DIRECTORIES + 1; + return BaseOfData; +} +static bool writeCOFF(COFFParser &CP, raw_ostream &OS) { + if (CP.isPE()) { + // PE files start with a DOS stub. + object::dos_header DH; + memset(&DH, 0, sizeof(DH)); + + // DOS EXEs start with "MZ" magic. + DH.Magic[0] = 'M'; + DH.Magic[1] = 'Z'; + // Initializing the AddressOfRelocationTable is strictly optional but + // mollifies certain tools which expect it to have a value greater than + // 0x40. + DH.AddressOfRelocationTable = sizeof(DH); + // This is the address of the PE signature. + DH.AddressOfNewExeHeader = DOSStubSize; + + // Write out our DOS stub. + OS.write(reinterpret_cast<char *>(&DH), sizeof(DH)); + // Write padding until we reach the position of where our PE signature + // should live. + OS << num_zeros(DOSStubSize - sizeof(DH)); + // Write out the PE signature. + OS.write(COFF::PEMagic, sizeof(COFF::PEMagic)); + } + if (CP.useBigObj()) { + OS << binary_le(static_cast<uint16_t>(COFF::IMAGE_FILE_MACHINE_UNKNOWN)) + << binary_le(static_cast<uint16_t>(0xffff)) + << binary_le(static_cast<uint16_t>(COFF::BigObjHeader::MinBigObjectVersion)) + << binary_le(CP.Obj.Header.Machine) + << binary_le(CP.Obj.Header.TimeDateStamp); + OS.write(COFF::BigObjMagic, sizeof(COFF::BigObjMagic)); + OS << zeros(uint32_t(0)) + << zeros(uint32_t(0)) + << zeros(uint32_t(0)) + << zeros(uint32_t(0)) + << binary_le(CP.Obj.Header.NumberOfSections) + << binary_le(CP.Obj.Header.PointerToSymbolTable) + << binary_le(CP.Obj.Header.NumberOfSymbols); + } else { + OS << binary_le(CP.Obj.Header.Machine) + << binary_le(static_cast<int16_t>(CP.Obj.Header.NumberOfSections)) + << binary_le(CP.Obj.Header.TimeDateStamp) + << binary_le(CP.Obj.Header.PointerToSymbolTable) + << binary_le(CP.Obj.Header.NumberOfSymbols) + << binary_le(CP.Obj.Header.SizeOfOptionalHeader) + << binary_le(CP.Obj.Header.Characteristics); + } + if (CP.isPE()) { + if (CP.is64Bit()) { + object::pe32plus_header PEH; + initializeOptionalHeader(CP, COFF::PE32Header::PE32_PLUS, &PEH); + OS.write(reinterpret_cast<char *>(&PEH), sizeof(PEH)); + } else { + object::pe32_header PEH; + uint32_t BaseOfData = initializeOptionalHeader(CP, COFF::PE32Header::PE32, &PEH); + PEH.BaseOfData = BaseOfData; + OS.write(reinterpret_cast<char *>(&PEH), sizeof(PEH)); + } + for (const Optional<COFF::DataDirectory> &DD : + CP.Obj.OptionalHeader->DataDirectories) { + if (!DD.hasValue()) { + OS << zeros(uint32_t(0)); + OS << zeros(uint32_t(0)); + } else { + OS << binary_le(DD->RelativeVirtualAddress); + OS << binary_le(DD->Size); + } + } + OS << zeros(uint32_t(0)); + OS << zeros(uint32_t(0)); + } + + assert(OS.tell() == CP.SectionTableStart); // Output section table. for (std::vector<COFFYAML::Section>::iterator i = CP.Obj.Sections.begin(), e = CP.Obj.Sections.end(); @@ -246,6 +433,7 @@ bool writeCOFF(COFFParser &CP, raw_ostream &OS) { << binary_le(i->Header.NumberOfLineNumbers) << binary_le(i->Header.Characteristics); } + assert(OS.tell() == CP.SectionTableStart + CP.SectionTableSize); unsigned CurSymbol = 0; StringMap<unsigned> SymbolTableIndexMap; @@ -257,12 +445,15 @@ bool writeCOFF(COFFParser &CP, raw_ostream &OS) { } // Output section data. - for (std::vector<COFFYAML::Section>::iterator i = CP.Obj.Sections.begin(), - e = CP.Obj.Sections.end(); - i != e; ++i) { - i->SectionData.writeAsBinary(OS); - for (unsigned I2 = 0, E2 = i->Relocations.size(); I2 != E2; ++I2) { - const COFFYAML::Relocation &R = i->Relocations[I2]; + for (const COFFYAML::Section &S : CP.Obj.Sections) { + if (!S.Header.SizeOfRawData) + continue; + assert(S.Header.PointerToRawData >= OS.tell()); + OS << num_zeros(S.Header.PointerToRawData - OS.tell()); + S.SectionData.writeAsBinary(OS); + assert(S.Header.SizeOfRawData >= S.SectionData.binary_size()); + OS << num_zeros(S.Header.SizeOfRawData - S.SectionData.binary_size()); + for (const COFFYAML::Relocation &R : S.Relocations) { uint32_t SymbolTableIndex = SymbolTableIndexMap[R.SymbolName]; OS << binary_le(R.VirtualAddress) << binary_le(SymbolTableIndex) @@ -276,9 +467,12 @@ bool writeCOFF(COFFParser &CP, raw_ostream &OS) { e = CP.Obj.Symbols.end(); i != e; ++i) { OS.write(i->Header.Name, COFF::NameSize); - OS << binary_le(i->Header.Value) - << binary_le(i->Header.SectionNumber) - << binary_le(i->Header.Type) + OS << binary_le(i->Header.Value); + if (CP.useBigObj()) + OS << binary_le(i->Header.SectionNumber); + else + OS << binary_le(static_cast<int16_t>(i->Header.SectionNumber)); + OS << binary_le(i->Header.Type) << binary_le(i->Header.StorageClass) << binary_le(i->Header.NumberOfAuxSymbols); @@ -287,43 +481,50 @@ bool writeCOFF(COFFParser &CP, raw_ostream &OS) { << binary_le(i->FunctionDefinition->TotalSize) << binary_le(i->FunctionDefinition->PointerToLinenumber) << binary_le(i->FunctionDefinition->PointerToNextFunction) - << zeros(i->FunctionDefinition->unused); + << zeros(i->FunctionDefinition->unused) + << num_zeros(CP.getSymbolSize() - COFF::Symbol16Size); if (i->bfAndefSymbol) OS << zeros(i->bfAndefSymbol->unused1) << binary_le(i->bfAndefSymbol->Linenumber) << zeros(i->bfAndefSymbol->unused2) << binary_le(i->bfAndefSymbol->PointerToNextFunction) - << zeros(i->bfAndefSymbol->unused3); + << zeros(i->bfAndefSymbol->unused3) + << num_zeros(CP.getSymbolSize() - COFF::Symbol16Size); if (i->WeakExternal) OS << binary_le(i->WeakExternal->TagIndex) << binary_le(i->WeakExternal->Characteristics) - << zeros(i->WeakExternal->unused); + << zeros(i->WeakExternal->unused) + << num_zeros(CP.getSymbolSize() - COFF::Symbol16Size); if (!i->File.empty()) { + unsigned SymbolSize = CP.getSymbolSize(); uint32_t NumberOfAuxRecords = - (i->File.size() + COFF::SymbolSize - 1) / COFF::SymbolSize; - uint32_t NumberOfAuxBytes = NumberOfAuxRecords * COFF::SymbolSize; + (i->File.size() + SymbolSize - 1) / SymbolSize; + uint32_t NumberOfAuxBytes = NumberOfAuxRecords * SymbolSize; uint32_t NumZeros = NumberOfAuxBytes - i->File.size(); OS.write(i->File.data(), i->File.size()); - for (uint32_t Padding = 0; Padding < NumZeros; ++Padding) - OS.write(0); + OS << num_zeros(NumZeros); } if (i->SectionDefinition) OS << binary_le(i->SectionDefinition->Length) << binary_le(i->SectionDefinition->NumberOfRelocations) << binary_le(i->SectionDefinition->NumberOfLinenumbers) << binary_le(i->SectionDefinition->CheckSum) - << binary_le(i->SectionDefinition->Number) + << binary_le(static_cast<int16_t>(i->SectionDefinition->Number)) << binary_le(i->SectionDefinition->Selection) - << zeros(i->SectionDefinition->unused); + << zeros(i->SectionDefinition->unused) + << binary_le(static_cast<int16_t>(i->SectionDefinition->Number >> 16)) + << num_zeros(CP.getSymbolSize() - COFF::Symbol16Size); if (i->CLRToken) OS << binary_le(i->CLRToken->AuxType) << zeros(i->CLRToken->unused1) << binary_le(i->CLRToken->SymbolTableIndex) - << zeros(i->CLRToken->unused2); + << zeros(i->CLRToken->unused2) + << num_zeros(CP.getSymbolSize() - COFF::Symbol16Size); } // Output string table. - OS.write(&CP.StringTable[0], CP.StringTable.size()); + if (CP.Obj.Header.PointerToSymbolTable) + OS.write(&CP.StringTable[0], CP.StringTable.size()); return true; } @@ -341,6 +542,10 @@ int yaml2coff(yaml::Input &YIn, raw_ostream &Out) { return 1; } + if (!layoutOptionalHeader(CP)) { + errs() << "yaml2obj: Failed to layout optional header for COFF file!\n"; + return 1; + } if (!layoutCOFF(CP)) { errs() << "yaml2obj: Failed to layout COFF file!\n"; return 1; diff --git a/tools/yaml2obj/yaml2elf.cpp b/tools/yaml2obj/yaml2elf.cpp index 6eeecae..44c8c12 100644 --- a/tools/yaml2obj/yaml2elf.cpp +++ b/tools/yaml2obj/yaml2elf.cpp @@ -62,11 +62,7 @@ class NameToIdxMap { public: /// \returns true if name is already present in the map. bool addName(StringRef Name, unsigned i) { - StringMapEntry<int> &Entry = Map.GetOrCreateValue(Name, -1); - if (Entry.getValue() != -1) - return true; - Entry.setValue((int)i); - return false; + return !Map.insert(std::make_pair(Name, (int)i)).second; } /// \returns true if name is not present in the map bool lookup(StringRef Name, unsigned &Idx) const { @@ -190,7 +186,7 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, for (const auto &Sec : Doc.Sections) DotShStrtab.add(Sec->Name); - DotShStrtab.finalize(); + DotShStrtab.finalize(StringTableBuilder::ELF); for (const auto &Sec : Doc.Sections) { zero(SHeader); @@ -261,7 +257,7 @@ void ELFState<ELFT>::initSymtabSectionHeader(Elf_Shdr &SHeader, DotStrtab.add(Sym.Name); for (const auto &Sym : Doc.Symbols.Weak) DotStrtab.add(Sym.Name); - DotStrtab.finalize(); + DotStrtab.finalize(StringTableBuilder::ELF); addSymbols(Doc.Symbols.Local, Syms, ELF::STB_LOCAL); addSymbols(Doc.Symbols.Global, Syms, ELF::STB_GLOBAL); @@ -304,7 +300,7 @@ void ELFState<ELFT>::addSymbols(const std::vector<ELFYAML::Symbol> &Symbols, Symbol.st_shndx = Index; } // else Symbol.st_shndex == SHN_UNDEF (== 0), since it was zero'd earlier. Symbol.st_value = Sym.Value; - Symbol.st_other = Sym.Visibility; + Symbol.st_other = Sym.Other; Symbol.st_size = Sym.Size; Syms.push_back(Symbol); } diff --git a/tools/yaml2obj/yaml2obj.cpp b/tools/yaml2obj/yaml2obj.cpp index 945fad1..375cd89 100644 --- a/tools/yaml2obj/yaml2obj.cpp +++ b/tools/yaml2obj/yaml2obj.cpp @@ -83,11 +83,11 @@ int main(int argc, char **argv) { if (OutputFilename.empty()) OutputFilename = "-"; - std::string ErrorInfo; + std::error_code EC; std::unique_ptr<tool_output_file> Out( - new tool_output_file(OutputFilename.c_str(), ErrorInfo, sys::fs::F_None)); - if (!ErrorInfo.empty()) { - errs() << ErrorInfo << '\n'; + new tool_output_file(OutputFilename, EC, sys::fs::F_None)); + if (EC) { + errs() << EC.message() << '\n'; return 1; } diff --git a/tools/yaml2obj/yaml2obj.h b/tools/yaml2obj/yaml2obj.h index 086f641..7290a9a 100644 --- a/tools/yaml2obj/yaml2obj.h +++ b/tools/yaml2obj/yaml2obj.h @@ -9,8 +9,8 @@ /// \file /// \brief Common declarations for yaml2obj //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_YAML2OBJ_H -#define LLVM_TOOLS_YAML2OBJ_H +#ifndef LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H +#define LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H namespace llvm { class raw_ostream; |